Public Repository

Last pushed: 2 months ago
Short Description
PLEX media server with commercial detection/removal and transcoding
Full Description


This project leverages docker-compose to create a service that provides PLEX media system and a post-processor that will mark and/or remove commercials from DVR OTA recordings, as well as transcode them to high-quality h.264, and reinserting them into the PLEX library.

This project leverages docker and docker-compose to create a service of two containers:

  1. The standard plexinc/pms-docker:plexpass container, modified by injecting a postprocessing script (in src/plexpost)
  2. My plexpost container, which detects new recordings and processes them with:
    • Commercial Detection, marking, and/or removal
    • Transcoding to H.264.

Change Log

The changelog can be found here in the git source repository for this project.


If you are familiar with the docker-compose system (recommended reading), you will see a volume created called postdata. This volume is mapped by both containers (plex and plexpost). When the plexpost container starts, it will copy the postprocessing script to a bin directory inside the postdata volume. Using the defaults, the postdata volume will be mapped/mounted inside both containers at /postdata. Thus, the postprocessing script will end up in /postdata/bin/plexpost.

This mechanism also ensures that the plexpost container can be updated separately from the plex container. For example, to upgrade the plexpost container without affecting the running plex container:

# upgrade steps
$ docker pull mbrown/plexpost:latest
$ docker stop plexpost
$ docker rm plexpost
$ docker-compose up -d

The postprocessing script, run after every recording, will create an entry in the postdata volume. Using defaults this can be seen from either container at the path /postdata/queue. The plexpost container scans the queue folder every QUEUETIMER seconds and launches the actual postprocessing tasks as it finds new jobs.


In addition to the official plex:plexpass docker image, i'm leveraging components from other contributors. Many thanks to these for the fantastic work they have done, without which this project would not have happened.


  1. Create a directory to hold the docker-compose.yml and mapped data (e.g /docker/data/plex)
  2. Copy the docker-compose.yml file below into the directory.
  3. Modify the ADVERTISE_IP to match your host's IP address
  4. Get a Plex Claim Token and set the PLEX_CLAIM variable to this token. This is only needed the first time you run the container; once the data is initialized, it will be ignored. Once you see "Token Successfully Claimed" in the logs you are good to go.
  5. If you are running a firewall, make sure all the ports listed under ports: are opened
  6. Start it up (docker-compose up -d)
  7. Browse to http://your-host-ip:32400

Next Steps

  1. Configure Plex to your liking:
  2. If you were already a PLEX user you can migrate your PLEX configuration. In the example dockerfile, the PLEX Library folder needs to go in the subdirectory ./data below the docker-compose.yml file.
  3. You do need to be sure to set the postprocessing script location in PLEX to: /postdata/bin/plexpost
  4. Refer to the plex container documentation for details on configuring the plex container.
  5. After configuring your DVRs make sure you set the post processor to /postdata/bin/plexpost

docker-compose.yml Configuration

This section describes configuration settings that are specific to my implementation of plex and the plexpost containers. For more information refer to the docker-compose documentation

For more information on the plexpost image environment variables, see the comments in the Dockerfile

  1. Plex container section

    • The port mappings should probably all stay the same.
    • Make sure PLEX_UID and PLEX_GID are set to something useful. Make sure that those UID/GID own or have access to the media directory structures mapped under volumes.
    • The standard plex:plexpass image created its own network, so here I force it to mine, which I called bridge. You can map this however you want. I map the plexpost container to the same network but it really isn't that necessary as the plexpost container doesn't even need the network.
    • Basically the only real change we introduce to the standard plex container configuration is addition of the volume entry:

      - postdata:/postdata

      Which is what makes sure our postdata volume gets mounted into the plex conatiner.

  2. Plexpost container section.
    • If you use a different subdirectory name other than plexpost you will need to update the image name here accordingly. You can also change the container name to match, though that's optional. Hostname is immaterial but should match what you set in the MAIL environment settings.
    • COMSKIP_* variables. These must be set to match what the Plex container uses for its PLEX_* variables.
    • TVDIR and MVDIR These must point to the location in the container where the postprocessor can find your media libraries.
    • QUEUETIMER Set this to how many seconds should elapse between queue scans by the postprocessor.
    • COMCUT if set to 0 (default) the postprocessor will:
      • Convert the recorded ts file to an mkv file with chapter marks around detected commercials.
      • Transcode the result into h.264 and put the result into the plex library with the original name but an extension of .mkv
        If set to 1, the postprocessor will:
      • Convert the original .ts file to .mkv and add chapter marks. The resulting file will be in the plex library with an extension of .mkv-ts so that Plex doesn't see the file. Its there for safekeeping.
      • Cut the commercials out of the file
      • Transcode the result into h.264 and put the result into the plex library with the original name but an extension of .mkv
    • REMOVETS If this is set to 1, it will immediately delete the .mkv-ts file! If set to 0 it leaves it alone
    • TSCLEAN Default is 1. If enabled, once per day the queue manager will scan the media libraries for .mkv-ts files, and if they are older than TSDAYS it will delete them. The point here is to make sure you have the original recording around for a while in case automated commercial removal makes a mess of things.
    • MAIL settings. See the examples in the docker-compose.yml. Fairly self explanatory and very easy to use if you have an outbound relay set up on your network, or an ISP that is permissive to their subscribers without auth. Basically if MAILTO is set it will send an email to alert you if anything goes wrong during post-processing.
    • SLACK_HOOK Default is blank. Set this to a SLACK channel webhook and plexpost will send error notifications there.

Sample docker-compose.yml

version: '2'
    container_name: plex
    image: plexinc/pms-docker:plexpass
    restart: unless-stopped
      - 32400:32400/tcp
      - 3005:3005/tcp
      - 8324:8324/tcp
      - 32469:32469/tcp
      - 32410:32410/udp
      - 32412:32412/udp
      - 32413:32413/udp
      - 32414:32414/udp
      - TZ=America/New_York
      - PLEX_CLAIM=
      - PLEX_UID=113
      - PLEX_GID=123
    hostname: plexserver
      - ./data:/config
      - ./data/transcode-temp:/transcode
      - ./data/tv:/data/tv
      - ./data/movies:/data/movies
      - ./data/home-video:/data/home-video
      - ./data/music:/data/music
      - ./data/pictures:/data/pictures
      - postdata:/postdata
    tty: true
    stdin_open: true
    network_mode: "bridge"
    container_name: plexpost
    image: mbrown/plexpost:latest
    hostname: plexpost
    restart: unless-stopped
      - TZ=America/New_York
      - COMSKIP_USER=plex
      - COMSKIP_GROUP=plex
      - COMSKIP_UID=113
      - COMSKIP_GID=123
      - TVDIR=/media/tv
      - MVDIR=/media/movies
      - QUEUETIMER=10
      - COMCUT=1
      - REMOVETS=0
      - ./data/tv:/media/tv
      - ./data/movies:/media/movies
      - postdata:/postdata
    tty: true
    stdin_open: true
    network_mode: "bridge"

Docker Pull Command