Installation and setup of the Plex Controller

My Plex Controller Raspberry Pi, seated with a 2.8" PiTfT screen.

The Plex Controller is a python script that I have only ever run in Linux on a Raspberry Pi, though it may also work in Windows. I use a Raspberry Pi 2 B+ to run mine, in addition to an attached 2.8" display for status information (more on that in the future). These instructions will be specific to the Pi, but if you’re intent on using a different device, it shouldn’t be too difficult.

To start, you’ll need to flash your SD card with Raspbian. If you are planning on using a PiTfT screen as a status display like I have, then I recommend using this version of Raspbian which has the drivers for 2.4", 2.8" and 3.2" screens or this version if you have the 3.5" screen. I will post follow-up instructions on using a screen as a FakeTV status display soon, but consider this the first step in that process.

If in Windows, use Win32DiskImager to flash your memory card. Then, before ejecting the card, open the card location in Windows Explorer. The root directory is the /boot/ directory in Raspbian. Add an empty file named ssh , with no extension to the directory. This will enable SSH access without having to plug in a monitor first. If some of these instructions seem familiar, it’s because they’re the same as when setting up the server on a Pi.

Now you can eject the card from your PC and install it into your Raspberry Pi. Plug in the network and power cables and fire up PuTTy or your preferred SSH client. Once your Pi has started up, point PuTTy at its IP address and connect. When prompted for a username, enter pi and raspberry for the password. Our first order of business will be to change that default password. Once logged in, type sudo raspi-config and select the second option titled ‘Change User Password.’ Set it to something unique that you won’t lose, and hit enter. Once the password is set, you should expand the file system and set the time zone in the ‘Internationalisation Options.’ This is important, because if not set to your local time zone, scheduling will be offset based on your time zone’s position relative to UTC. Also during this time, you can take the opportunity to enter the ‘Advanced Options’ and change the hostname to something unique. I use “controller” for mine. Once this is complete, you can exit raspi-config and reboot.

Install Dependencies

Before installing the script, you’ll want to make sure everything’s up to date and install some dependencies. After the Pi has finished rebooting, reconnect and run sudo apt-get update and sudo apt-get upgrade . Then install python with sudo apt-get install python . Next, install the following python libraries (you may need to run sudo apt-get install python-pip before pip install will work):

The Python Plex API sudo pip install plexapi

Enchant sudo apt-get install enchant and sudo pip install pyenchant

and web.py for Webhooks integration sudo pip install web.py

Important Note: You will need to add the IP or subnet for your Plex controller device to the authorized IP list in your Plex Server Settings. Look for ‘allowed without authorization.’ If it isn’t authorized, you may run into issues updating the Plex controller database.

Install the controller script

Download the most recent version of the system_setup.py file and place it in your /home/pi directory with curl "https://raw.githubusercontent.com/MoeFwacky/Python-Plex-Controller/master/system_setup.py" -o "system_setup.py" , and execute it with python system_setup.py .

The system_setup.py script will create a subdirectory in your home directory, hasystem/ and copy the script files from the repository to your device. The system setup script will prompt you for information in order to get connected with the Plex server and client devices.

Populating the controller database

Once installation is complete, it will prompt you to build and update the database. The Plex controller uses its own database ( myplex.db ) separate from the Plex server database to avoid any accidental corruption. This has the added advantage of the controller not being affected if you watch content from your Plex server independent of this setup. Once the database has been created and scanned all of the TV and movie data, you’re pretty much ready to go. But, if you’re like me and you’ve created a custom library full of commercials, you will want to run python /home/pi/hasystem/upddatedb_pi.py commercials to add those to the database. A note about the database update script, when using the argument all only the movies and TV shows databases will be updated. Commercials and other custom libraries must be updated separately.

At this point, you should be able to play items from your library through commands issued in the SSH window. Try playme “name of media” or playcommerical if you have commercials set up.

Note: If you enter an incorrect value during setup, which prevents the Plex controller from working properly, you can run python system_setup.py reset from your home directory and it will delete the previously stored data and prompt you to re-enter it.

Understanding play modes

Before I get into creating schedules, I’m going to cover the various play modes available in the script. These are the various play modes you can set the script to. When play checking is turned on (more on that later), or the startnextprogram command is used, the script will play an item according to the mode it's set to. You can check the play mode with getplaymode and set it with setplaymode .

normal — Creates a queue and plays items it. It will find more content automatically when the queue is exhausted.

— Creates a queue and plays items it. It will find more content automatically when the queue is exhausted. block.blockname — Plays through the specified block, when it reaches the end of the block, it will drop back into normal mode. Blocks can be customized (such as a series of shows in a specific order) or random (such as a block of random genre-specific movies). I use the randommovieblock feature to follow the end of scheduled content for the day. It plays 3 movies of the specified genre, which is great for random, middle-of-the-night content.

— Plays through the specified block, when it reaches the end of the block, it will drop back into normal mode. Blocks can be customized (such as a series of shows in a specific order) or random (such as a block of random genre-specific movies). I use the feature to follow the end of scheduled content for the day. It plays 3 movies of the specified genre, which is great for random, middle-of-the-night content. marathon.show — Plays the specified show until the mode is changed.

— Plays the specified show until the mode is changed. holiday.usercreatedholiday — Plays items from a user-created holiday list.

— Plays items from a user-created holiday list. commercialmode — Plays random items from the commercials table unless user plays something else or changes play mode. This is the primary mode I use to play in between scheduled content.

— Plays random items from the commercials table unless user plays something else or changes play mode. This is the primary mode I use to play in between scheduled content. custom.customsectionname — plays items from a custom.table user added using upddateddb.py custom.insertablenamehere .

Building a schedule using shell scripts

The syntax to add a scheduled item is addschedule “show or movie” hh:mm day or addschedule “script command” hh:mm day . For day , you have a handful of options. You can choose a day of the week, everyday, weekdays, weekends or today. So, if you wanted the Simpsons to play every weekday at 6:30 PM, you would use the command addschedule “the simpsons” 18:30 weekdays .

Now, as the command only allows for scheduling one item at a time, it can become cumbersome when you’re trying to schedule ten to twenty shows per day, like I do. So, I created a shell script to do the heavy lifting. You’ll need to add a line to the script for each item you’d like to schedule using the template provided. Here is a sample of my script:

Each line schedules a show or command to play on a specific day of the week at a specific time.

As you can see, I have entries at the top scheduled to turn commercial mode on at the beginning of each broadcast day. Once your schedule is built in the schedule.sh file, set its permissions with chmod +777 schedule.sh and execute it with sh schedule.sh . Once the script has finished processing the commands, your scheduled media should start to play at the times you have indicated.

If you looked closely, you would have noticed the above schedule has a gap between 12:30 and 15:00. This is because I have a Sunday afternoon movie that plays starting at 1pm. For date-specific scheduling, I have created a separate script. This script is intended to be run at the beginning of each day. It will check today’s date against the dates in each of the entries, and will add the entries that match today’s date to today’s schedule. Because I’ve allotted a two-hour block for movies, and some movies run for 90 minutes or less, I’ve also added a filler show in those cases to play after the movie ends. You can see a sample of my movies script below.

This script checks today’s date and compares it to the date in each if statement, if they match it runs the command to schedule the movie for today.

As you can see above, you’ll need to use the YYYYMMDD format for your dates in each of the entries. This script will need to be run after midnight each night in order to add that day’s movies to the schedule. More on that in the below section.

Using the play checker and setting up /etc/crontab

Outside of scheduled items, the script has two methods to trigger media to play according to the play mode. There is a play checker script that periodically checks the server to see if anything is currently playing on the client, and if not issues the command to start playback in accordance with the current play mode. The other method relies on webhooks, which are only available to Plex Pass subscribers. I have toyed with the webhooks a bit, and while more quickly responsive, they are triggered when playback stops and had a tendency to conflict with scheduled playback. So, I use the play check script called piplaystate.py . An entry to start this script at boot should be added to crontab when running system_setup.py . Type sudo nano /etc/crontab into the console and ensure the following lines are in your crontab file (you only need the line under #MOVIES if you’re using the movies.sh shell script (make sure the path is correct to the location of your movies.sh file).



* * * * * pi python /home/pi/hasystem/tbn_schedule.py >/dev/null 2>&1 & @reboot pi python /home/pi/hasystem/piplaystate.py > /dev/null 2>&1 &* * * * * pi python /home/pi/hasystem/tbn_schedule.py >/dev/null 2>&1 & #MOVIES

0 1 * * * pi sh /home/pi/hasystem/scripts/movies.sh

In addition to these lines, I have a few others I include in my setup in order to keep the play checker running and turned on during on-air hours, but also not running on the hour and half-hour minutes to avoid conflict with scheduled media. Below is what my crontab file looks like:

The playcheckstatus entries checks if the play check script has crashed and restarts it if it has. There are also a few areas in the script which trigger the play check status to turn off. I haven’t been able to identify why or what causes them yet, so as a stop-gap, I’ve added the playcheckstart lines to turn on play checking every minute. I’ve added the playcheckstop lines around the hour and half hour to prevent the script from checking and executing at the same time as scheduled playback. Then, at the end I have the movies shell script which runs each night at 1 AM and adds anything indicated for that day to the schedule.

Play checking isn’t perfect, and could be improved upon. I’m thinking about finding a way that incorporates both webhooks and play checking for a more responsive system that doesn’t interrupt scheduled playback.