Code

Something containing programming code I've written

DRM-Free iPlayer download link with iplayer-dl

I'm a big fan of BBC iPlayer. However, I'm not such a big fan of DRM-encumbered downloads, and the flash player doesn't work on my netbook. For this reason, I use iplayer-dl, a clever little ruby app that pretends it's an iPhone, and lets you download DRM-free versions on the videos.
The downside of this iplayer-dl is that it requires you to manually copy and paste the URL or ID of the video into the command line. I thought it would be handy if there was a link on the iPlayer page to download an episode through iplayer-dl.

After about an hour of messing, I was successful. The steps I followed are below. Please note that this is a VERY hacky solution, and probably isn't the best way to do it, nor do I recommend anyone else does it this way. One stage of it potentially leaves your system vulnerable to attack.

  1. Install iplayer-dl
    This bit's pretty straightforward, the instructions are on the iplayer-dl site linked above.
  2. PHP Script
    I run a local web server on my netbook for web development purposes. I created a script called iplayer.php in my public_html folder with code similar to the following:
    <?php
    $proxy = "";
    $dest = "/home/mark/downloads/iplayer";
    
    $cmd = "gnome-terminal -x /usr/bin/iplayer-dl -d $dest -p $proxy {$_GET['id']}";
    
    header("Content-Type: text/something");
    echo("#!/bin/bash\n");
    echo($cmd);
    
    ?>
    

    This code essentially generates a shell script which will open a gnome-terminal window (it could equally use Konsole, xterm etc), and runs the iplayer-dl with the specified parameters. It gets the show's ID or URL from the query string.
    Note the Content-Type header. I haven't actually included the Content-Type I used here, since I've told Firefox to open it in Bash automatically (remember the vulnerability I mentioned?). Suffice to say, I chose something fairly innocuous that I'm unlikely to ever come across on the web, so I won't get weird things suddenly trying to execute in bash.

  3. Displaying a link to the PHP Script
    This piece of magic was done with the Greasemonkey Firefox extension, which is well worth a play with, especially if you're good with Javascript. Long story short, it lets you define scripts to run automatically after certain pages have loaded, allowing to you edit how they look.
    I wrote a script to run on BBC iPlayer's episode pages (the pages where the actually video player is displayed). It looks like this:
    var dlFlash = document.getElementById('download-air');
    dlFlash.innerHTML = '<a href="http://localhost/~mark/iplayer.php?id='+window.location+'" target="_blank">Download with iplayer-dl</a>';
    

    Obviously this exact script only works for me. If you did it yourself, you'd have to change the href to the location of your iplayer.php. Essentially all this script does it replace the normal download link on the page (Which tries download the video in BBC's Adobe AIR-based client) with a link to my PHP script (effectively, to the shell script generated by the PHP). By a happy accident, it even keeps the icon and background of the download button, so it's nice and aesthetically pleasing.

  4. Click the Link
    Clicking the link pops up the usual "What shall I do with this file?" dialogue. Now, I want as little faff as possible, so I set it to run with bash, and to do it every time without asking. This associates the MIME type I chose earlier with Bash, so in future all scripts produced from these links will open the download instantly. For this reason I didn't use application/x-sh as the MIME type, as I didn't want to accidentally click a link to a real shell script on another page and have it execute automatically! If you were happy to select bash and click "OK" each time you wanted to download from iPlayer, you could use this MIME type.

And that's it! I've now got a handy DRM-free one-click download link on every iPlayer page.

Displaying MySQL enum values in PHP

I've always been in the habit of using MySQL's enum data types, allowing you to define a set of values allowed in a particular field. This saves having to create an extra table which only has 2 fields while still having a strict set of values for a field. The problem with this over the other option is that you can't just do a SELECT query to get all the available options for the field, for example to display in an HTML <select> box.

After some googling I turned up a HowTo by Bill Heaton with a reasonable solution. However, there's 2 niggles I had with his solution: it uses a load of string processing functions when it could use a single Regular Expression, and it's a function, not a class.

The solution I've got in practice uses a "query" class I wrote, but for the example I'll use plain old mysql_query():

class enum_values {
	
	public $values;
	
	public function __construct($table, $column){
		
		$sql = "SHOW COLUMNS FROM $table LIKE '$column'";
		if ($result = mysql_query($sql)) { // If the query's successful
			
			$enum = mysql_fetch_object($result);
			preg_match_all("/'([\w ]*)'/", $enum->Type, $values);
			$this->values = $values[1];
			
		} else {
			
			die("Unable to fetch enum values: ".mysql_error());
			
		}
		
	}
}

Let's have a look at that code. If you're not familiar with OOP in PHP 5, we start off by creating a class called "enum_values" with 1 property ($values) and a constructor that accepts 2 arguments, the table and column we're getting the values from. This constructor is called when we create an instance of the class (an object), e.g.

$example = new enum_values('table_name', 'column_name');

Right, so within the constructor with have a "SHOW COLUMNS" query. This will show us the structure of a specified column in a specified table. The fields returned include "Name" (containing the column's name) and "Type", which contains the column's data type, and in the case of enum, it's possible values. This is the field we're interested in.

To get at the values field we use mysql_fetch_object to create an object containing the fields and their values. We could equally use mysql_fetch_array here, but I like objects.
The next line is where the magic happens. The string we've got to work with in the Type field looks like:

enum('value1','value2','value3')

Bill's solution suggests using substr to cut the string down to the list of values within the brackets (after finding the position with strpos), using explode to split the resulting string into an array by using the commas as delimeters, then looping through the array and removing the quotes around each value with str_replace. But wouldn't it be a lot nicer if we could just extract the values without the quotes in the first place?

The way I solved this was with preg_match_all, which has the wonderful ability to do all this in a single line. Firstly I needed a regular expression that matched characters in single quotes. The regex '([\w ]*)' will match a single quote, followed by any number of alphanumeric characters and spaces, followed by another single quote. Note that I haven't used a . to allow any character inside the quotes, since this would allow single quotes, meaning the entire string would be matched. The parentheses are used to "group" the characters inside the quotes, meaning we can refer back to them.
The regex is then delimited with slashes (to show PHP it's a regex), then double quotes (since we need to pass it as a string). The resulting argument passed to the function is "/'([\w ]*)'/".

The second argument is simply the string we're operating on, $enum->Type. The third argument is a variable that's going to store every match found in an array. But it's even better than that. What we actually get is a multidimensional array, with [0] containing an array of the entire matched string, and [1] containing the contents of the first "group" within that match. If there was a second group, it would be in [2], a third in [3], etc. So while $values[0][0] would contain 'value1' with the single quotes, $values[1][0] would simply contain value1, as the characters inside the quotes were grouped.

Now we've got our array of values we store it in the object's $values property (note that it's referred to as $this->values, it's not the same as the $values array I was just referring to). Now we can access an array of the values like this:

// Db connection stuff goes here
$example = new enum_values('table_name', 'column_name');
print_r($example->values);

Lovely!

Syndicate content