Back last December I wrote about a project that I had written called What Should I Play Next? where it would randomly choose something from my record collection to play. This month’s project, part of my 👨💻12 Apps in 12 Months challenge, is the visual equivalent.
The Plex API
When I want to watch a film I am sometimes constrained by the amount of time I have and usually have some feeling of the genre I want so I wanted something that catered for that plus could also exclude things that I had watched before.
While I do subscribe to some streaming services, I wasn’t looking to include them in this project at this stage at least. Instead, I was only interested in media stored on my Plex server and specifically only movies.
It turns out that there is a well documented, but possibly unofficial, API for Plex and so I decided to use that. I say “possibly unofficial” because what you are really doing is making the same calls that Plex itself makes, and these are documented by third parties rather than Plex. This does somewhat leave you at the mercy of Plex not changing any of these commands, but that’s a risk I am willing to take.
SQLite
I wanted to avoid one if the issues with the Now Playing project, namely the time it takes to retrieve information from the remote service, Discogs in that case, Plex in this. There is a further issue here in that my Plex server isn’t on 24/7 so the code needed to be able to deal with that, although I obviously couldn’t watch anything suggested until the server was switched on of course.
This is the first time I have used SQLite in a project where I am creating the structure rather than simply reading the data in a database created by another.
Accessing Plex
In order to access your Plex instance you need to get a Plex api token. Once you have sorted that you can begin to make requests using cURL statements and passing the api token in the header. As an example, the following code snippet accesses Plex and returns all the libraries that it has (music, TV programmes, movies, etc.) and then scans the returned list to find if the Movie library exists.
// get the Plex token
$headers = [
"X-Plex-Token: ".$token,
"Accept: application/json"
];
// get the sections
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint."/library/sections/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode != 200) die("Unable to connect to the Plex server. " . $httpCode . ' ' . $response);
$dets = json_decode($response);
// cycle through the sections to find the movie library
foreach ($dets->MediaContainer->Directory as $section){
if ($section->type == 'movie'){
$sectionKey = $section->key;
}
}
// if we haven't found the movie library then throw an error
if (!isset($sectionKey)){
die("Unable to find the movie library.");
}
Next we cycle through all the items in the Movie library extracting the key data points for each film (crucially, title, length and genre) and store then in a SQLite database (not shown).
// get all the items in the library
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $endpoint."/library/sections/".$sectionKey."/all");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode != 200) die("Unable to get the library items. " . $httpCode . ' ' . $response);
$dets = json_decode($response);
// cycle through all the movies in the Plex library
$movies = $dets->MediaContainer->Metadata;
foreach ($movies as $movie){
if (isset($movie->title)) $movieTitle = $movie->title;
if (isset($movie->year)) $movieYear = $movie->year;
if (isset($movie->audienceRating)) $movieRating = $movie->audienceRating;
if (isset($movie->thumb)) $moviePoster = $movie->thumb;
if (isset($movie->duration)) $movieDuration = $movie->duration;
$movieLastViewedAt = isset($movie->lastViewedAt) ? $movie->lastViewedAt : "0";
// get the genres
if (isset($movie->Genre)){
$genres = [];
foreach ($movie->Genre as $genre){
$genres[] = $genre->tag;
}
}
We now have enough information in the database to query it and find a film that matches our particular parameters. I obviously haven’t shown all the code as there is nothing here regarding reading and writing from the database and the HTML pages – you can find all of this in the repository.
Once it is up-and-running it looks something like the following. You will see that there is a delay before the movie thumbnails are shown which is the latency between the code, which is running in the cloud, and my Plex server on my home network. YMMV as they say.
Possible Enhancements
Could I extend this to cover other streaming services? As far as I am aware the likes of Netflix, Disney+ etc. don’t have official apis that would allow this but I have seen other ways to access so maybe this would be possible. The other thing I would like to do is to open Plex on the selected movie when it is clicked which seems like a fairly easy win.
If you wanted to try and extend the code yourself you can find all of this in the repository.