I run a large mixtape download site on WordPress. I'm currently developing a new theme and I'd like to include as much site functionality into the theme's functions.php
.
For serving the downloads, I have several PHP scripts. A download is called by a visitor by clicking on URL (example): http://www.tjoonz.com/house.php?id=1234&file=post-slug
.
The script sanitizes the parameters and serves the MP3
files from a private server directory. My main concern is that these scripts contain the database connection details, which is something I would much rather use $wpdb
for.
Current scripts (for reference)
My apologies if this is a bit excessive, but it may come in handy later. You can probably skip this bit for now if you're just reading my question.
PHP
<?php // Tjoonz.com download script v1.2
// test arguments
if(!isset($_REQUEST['id']) || empty($_REQUEST['id']) || !isset($_REQUEST['file']) || empty($_REQUEST['file']))
{
// invalid argument(s), abort script
header("HTTP/1.0 400 Bad Request");
die();
}
// set variables
$current_time = time();
$user_ip = $_SERVER['REMOTE_ADDR'];
$post_id = $_GET['id'];
$file_requested = $_GET['file'];
$file_name = strip_tags($file_requested);
$file_path = '/home/tjoonz/audio/house/';
$locked = false;
// test if cookie 'downloadsAllowed' is set
// this prevents hotlinking on other domains for users who haven't visited Tjoonz.com yet
if ($_COOKIE["downloadsAllowed"] == "tj00nz")
{
$file_full = $file_path.$file_name;
if (file_exists($file_full)) // test if requested mixtape exists
{
// establish connection to database for Play Counter test
$user="#######";
$password="#######";
$database="#######";
$host="#######";
mysql_connect($host,$user,$password);
mysql_select_db($database) or die(mysql_error());
// set timerange to 2 hours ago (right now minus 7200 seconds)
$timerange = $current_time - 7200;
// get all records from 'playcount_lock' table
$locks = mysql_query("SELECT * FROM playcount_lock WHERE request_time > $timerange") or die(mysql_error());
// test if current IP address has already accessed this mixtape in the last 2 hours
while ($lock = mysql_fetch_object($locks))
{
// set variables with data from record
$lock_ip = $lock->ip_address;
$lock_id = $lock->post_id;
$lock_time = $lock->request_time;
// compare record data with current user data
if($lock_ip == $user_ip && $lock_id == $post_id && ($lock_time + 7200) >= $current_time)
{
// match found, break out of while loop and continue with script
$locked = true;
break;
}
}
if (!$locked) // if playcounter is not locked, update the database
{
// add a lock entry for the next two hours
mysql_query("INSERT INTO `playcount_lock` (`request_time`, `post_id`, `ip_address`) VALUES ('$current_time', '$post_id', '$user_ip')") or die(mysql_error());
// update the play counter for this mixtape
$post_meta = mysql_query("SELECT meta_value FROM `wp_postmeta` WHERE `meta_key` = '_played' AND `post_id` = ".$post_id);
$count = @mysql_result($post_meta, 0);
if($count == FALSE)
{
// if no plays are present, insert the metadata into table
mysql_query("INSERT INTO wp_postmeta (post_id, meta_key, meta_value) VALUES (".$post_id.",'_played',1)");
}
else
{
// otherwise increase the existing number of plays by 1
$newCount = mysql_result($post_meta, 0) + 1;
mysql_query("UPDATE `wp_postmeta` SET meta_value = ".$newCount." WHERE `meta_key` = '_played' AND `post_id` = ".$post_id);
}
}
// we're done with the database, kill the connection
mysql_close();
// finally, send the file
header("X-SENDFILE: ".$file_full);
header("Content-Type: audio/mpeg");
header("Content-Disposition: attachment; filename=".basename($file_full));
header("Content-Length: ".filesize($file_full));
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Transfer-Encoding: binary");
}
else // couldn't find that mixtape
{
header('HTTP/1.0 404 Not Found');
}
}
else // cookie 'downloadsAllowed' not set, redirecting user to mixtape page
{
$file_redirection = substr($file_name,0, -4);
header("Location: http://www.tjoonz.com/house/" . $file_redirection . "/");
}
?>
JS
// when playnow is clicked, add to myPlaylist and immediately start playing the mixtape
$("body").on("click", ".jplayer-playnow", function (e) {
e.preventDefault();
// add to Play Queue and play it immediately
$.jPlayer.timeFormat.showHour = true;
$.jPlayer.timeFormat.sepHour = ":";
$.jPlayer.timeFormat.sepMin = ":";
$.jPlayer.timeFormat.sepSec = "";
myPlaylist.add({
title: $(".single-title-mixtape").text(),
artist: $(".single-title-artist").text(),
genre: $(".jplayer-playnow span").attr("class"),
mp3: $(this).attr("href"), // <--- This is where my problem comes up for my new theme, keep reading
poster: $(".wp-post-image").attr("src")
}, true); // true value here makes the newly added track play immediately
});
Intentions
I'm using the current script twofold: When a user clicks the PLAY
button, jQuery prevents the link from being navigated to, and takes the URL to instruct a music player to get the file instead. When a user clicks the DOWNLOADS
button, the browser just follows through and a direct download is initiated. Both buttons use the same scripts and the same URLs.
In my new script I want to combine house.php
, electro.php
, dubstep.php
, drum-and-bass.php
and techno.php
with a single script that take a genre
parameter. Additionally, depending on how things go from here, I'll add a parameter to specify the action
(download or play).
I intend to completely embed the script inside functions.php
. On a mixtape page, two links would be:
<!--Play-->
<a href="?id=<?php echo $post->ID; ?>&file=<?php tjnz_slug(); ?>&genre=<?php echo $category[0]->category_nicename; ?>&action=play">Play</a>
<!--Download-->
<a href="?id=<?php echo $post->ID; ?>&file=<?php tjnz_slug(); ?>&genre=<?php echo $category[0]->category_nicename; ?>&action=download">Download</a>
The problem
In my old script, I simply caught play links (by class name on the anchor) and prevented the default browser action, then do my things. I was able to do this because I could point to a 'real' file (e.g. house.php
).
If I were to stick the code inside functions.php
, how can I instruct my music player where the audio file is located? The audio file doesn't necessarily have to be an MP3
file, as is evident by my current code: I instruct jPlayer that the audio file is located at house.php for example.
How do I tell jPlayer that the file is now coming from the mysterious functions.php
? Do I link directly to theme dir/functions.php
? Doesn't sound like a good idea, but that's just my gut feeling.
Like I said before, the main reason I want the script to be inside functions.php
is so that I can tap into the power of $wpdb
. If a separate PHP file is still the way to go, I'd still accept that answer (provided you've backed up that theory).
functions.php
deal with exclusively theme related issues. This Q&A may help to clarify things: Where to put my code: plugin or functions.php?functions.php
into a big mess and being able to swap themes and not lose this functionality. You can surely organizefunctions.php
by including external files. Anyway, it's up to you follow one path or another after reading the shortcomings in that Q&A and linked/related ones. plugin-development isn't complex and is well documented here.