It all started in my question in Toster. And now, for half a year I have been using the Plex media server. For those who have not heard of it, I will explain: it is software that analyzes and structures your media library, and provides access to it through the web and not only, a kind of personal Netflix without registration and SMS. I use Plex to watch movies and TV shows via a browser on a laptop or Chromebook .
I used to have to configure NFS or Samba share, conjure with automount (8) , put up with the roll-off share after suspend-resume, or just copy files over sftp / scp, but now I use Tide Plex. Unfortunately, not everything is simple with him either.
Cubietruck performs the role of my home server with an ARM Cortex-A7 1GHz processor and an Armbian distribution (Vanilla kernel for Docker and namespaces (7) support). It is quite enough for everyday needs (backup storage, VPN server), but it is obviously not intended for more resource-intensive things.
Plex is a freeware software. It is free, but not free, which imposes certain restrictions. For example, there are no source codes and official deb packages for any processor architecture. Well, by itself, many paranoid moviegoers will not want to install a cat in a bag on their system.
There is a opensource project Emby Media Server , an analogue of Plex, written in Mono. Unfortunately, he has problems with playing files in browsers. Many of Emby video formats are fully transcoded, even if the h264 codec is originally used.
Today Plex allows you to play more video formats without full conversion than Emby Media Server. Perhaps some of you will help fix this situation. In the meantime, you can conjure over the file browserdeviceprofile.js , which is responsible for browser profiles.
We will solve the first problem with the help of officially distributed packages for NAS devices, and the second partially with the help of Docker.
Let's take as a basis the package for NAS QNAP with the ARMv7-X31 + architecture (this build supports the Neon extension, which is supported by Cubietruck, you can check it with the command cat /proc/cpuinfo | grep neon
):
$ curl -s https://plex.tv/api/downloads/1.json | python -mjson.tool | grep x31plus "url": "https://downloads.plex.tv/plex-media-server/1.0.3.2461-35f0caa/PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg
The qpkg file is a symbiosis of a shell script and several archives. We can plex_media_server
it into the plex_media_server
directory using the command:
$ mkdir plex_media_server $ wget https://downloads.plex.tv/plex-media-server/1.0.3.2461-35f0caa/PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg $ dd if=PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg bs=22954 skip=1 status=none | tar -xzf - -C plex_media_server
The resulting files can be placed in the Docker container, but more on that later. Suppose we launched Plex and are going to watch a movie in the browser that is already encoded in h264 with audio AC3 5.1. What will Plex do? It will begin to transcode AC3 5.1 track into AAC 5.1. And to watch the video on a laptop, we don’t need to listen to the video with six channels, and the slow processor makes itself felt with periodic pauses when watching.
Fortunately, the Plex has configuration profiles that can be edited. For example, profile for browsers Resources/Profiles/Web.xml
.
<?xml version="1.0" encoding="utf-8"?> <Client name="Web"> <!-- Author: Plex Inc. --> <TranscodeTargets> <VideoProfile protocol="hls" container="mpegts" codec="h264" audioCodec="aac,mp3" context="streaming" /> <VideoProfile protocol="dash" container="mp4" codec="h264" audioCodec="aac" context="streaming" /> <VideoProfile protocol="http" container="mkv" codec="h264" audioCodec="aac,mp3" context="streaming" /> <MusicProfile container="mp3" codec="mp3" /> <PhotoProfile container="jpeg" /> <SubtitleProfile container="ass" codec="ass" context="all" /> </TranscodeTargets> <CodecProfiles> <VideoCodec name="*"> <Limitations> <UpperBound name="video.bitDepth" value="8" /> </Limitations> </VideoCodec> <VideoAudioCodec name="*"> <Limitations> <UpperBound name="audio.channels" value="6" /> </Limitations> </VideoAudioCodec> </CodecProfiles> </Client>
In it, we see the parameter <UpperBound name="audio.channels" value="6" />
, which means that the maximum number of channels for audio should not exceed six. And when transcoding an audio track, this means that if we convert AC3 6 channels to AAC, then the resulting AAC will also have 6 channels, i.e. we decode 6 AC3 channels and encode them into 6 AAC channels, once again using CPU resources. When watching a video, this causes periodic suspensions.
To enable the so-called downmix , you need to replace parameter 6 by 2 and get <UpperBound name="audio.channels" value="2" />
. Then files with a six-channel audio track will be converted to stereo.
For most users, this option will be acceptable. But not for those who have files with the six-channel AAC track. In this case, the six-channel AAC will be converted to stereo AAC. And this is again a waste of processor resources and periodic freezes when watching a video. I thought that conjuring with profiles might solve the problem, but unfortunately, in the current version of Plex such exceptions are not possible. On the Plex forum for two weeks already there is a request for adding a similar option.
The only option to solve this problem I saw in replacing the Plex Transcoder
binary with a script that will generate the necessary parameters if there is an AAC track in the video file.
#!/bin/bash # This script disables transcode for videos which already have aac audio magic=0 i=0 input=false for arg in "$@"; do ((i++)) next=$((i+1)) if [[ "$arg" == "-i" ]]; then input=true fi if [[ "$arg" =~ -codec:[0-9] && "${@:$next:1}" == "aac" && $magic == 0 && $input == false ]]; then ((magic++)) continue fi if [[ "$arg" == "aac" && $magic == 1 ]]; then ((magic++)) continue fi if [[ "$arg" == "-codec:1" && $magic == 2 ]]; then ((magic++)) fi if [[ "$arg" == "aac" && $magic == 3 ]]; then args[$i]="copy" ((magic++)) continue fi if [[ "$arg" == "-ar:1" && $magic == 4 ]]; then args[$i]="-copypriorss:1" ((magic++)) continue fi if [[ "$arg" == "48000" && $magic == 5 ]]; then args[$i]="0" ((magic++)) continue fi if [[ "$arg" == "-channel_layout:1" && $magic == 6 ]]; then ((magic++)) continue fi if [[ "$arg" == "stereo" && $magic == 7 ]]; then ((magic++)) continue fi if [[ "$arg" == "-b:1" && $magic == 8 ]]; then ((magic++)) continue fi if [[ "$arg" == "256k" && $magic == 9 ]]; then ((magic++)) continue fi args[$i]=$(printf "%q" "$arg") done set -- "${args[@]}" eval "/opt/plex/Application/Resources/Plex\ Transcoder_ $@"
github link: https://github.com/kayrus/plex/blob/master/magic.sh
When testing, it turned out that this hack works well with video files from my library.
Now let's see how to wrap all this into a Docker image. At a minimum, the following conditions must be met:
The following is an excerpt from the Dockefile
that I use.
Copy and unpack the downloaded archive (can be obtained directly via wget, but this is forbidden in the configuration I use). I use COPY
instead of ADD
to avoid automatic unpacking of the archive, in this case there is no need for this. Expression || true
|| true
allows you to ignore the gzip message about garbage after the end of the archive.
COPY PlexMediaServer_1.0.3.2461-35f0caa_arm-x31plus.qpkg /tmp/plex_media_server.tar RUN { dd if=/tmp/plex_media_server.tar bs=22954 skip=1 status=none | tar -xzf - -C /opt/plex/Application || true; } && rm -f /tmp/plex_media_server.tar
Add to the container unprivileged system user plex
.
RUN useradd -r -d /var/lib/plex -s /sbin/nologin plex
Activate downmix:
RUN sed -i 's/name="audio.channels" value="6"/name="audio.channels" value="2"/' /opt/plex/Application/Resources/Profiles/Web.xml
All further actions in the container will be performed by the plex
user.
USER plex
Mark the paths /var/lib/plex
(to save the state of the media file database) and /media
(the path for media files) as external volumes:
VOLUME ["/var/lib/plex","/media"]
In the command below, we broadcast the standard port 32400 to the 80th http port, mount the path /home/plex
to /var/lib/plex
inside the container and /home/user/media
to /media
.
$ docker run --name plex --hostname plex --rm -p 80:32400 -v /home/plex:/var/lib/plex -v /home/user/media:/media pleximage
Unit file that I use to run the plex container.
[Unit] Description=Plex Media Server After=docker.service Requires=docker.service [Service] Environment=MEDIA_LIB=/home/user/media Environment=CONFIG_DIR=/var/lib/plex Environment=DOCKER_IMAGE=kayrus/plex Environment=PLEX_INT_PORT=32400 Environment=PLEX_EXT_PORT=32400 # Remove old Plex container ExecStarPre=-/usr/bin/docker rm plex ExecStart=/usr/bin/docker run --name plex --hostname plex --rm -p ${PLEX_EXT_PORT}:${PLEX_INT_PORT} -v ${CONFIG_DIR}:/var/lib/plex -v ${MEDIA_LIB}:/media ${DOCKER_IMAGE} # Fix foreign network which requires Plex login/signup ExecStartPost=/sbin/iptables -t nat -I POSTROUTING -o docker0 -p tcp -m tcp --dport ${PLEX_INT_PORT} -j MASQUERADE ExecStopPost=-/sbin/iptables -t nat -D POSTROUTING -o docker0 -p tcp -m tcp --dport ${PLEX_INT_PORT} -j MASQUERADE ExecStop=/usr/bin/docker stop plex # Remove pidfile after stop which prevents Plex server start ExecStopPost=/bin/rm -f ${CONFIG_DIR}/Library/Application\x20Support/Plex\x20Media\x20Server/plexmediaserver.pid [Install] WantedBy=multi-user.target
To access the Plex from the Internet, it is recommended to use an HTTPS connection. If you do not want to register and pay for additional Plex features, you can configure the certificate yourself. You can use a self-signed certificate, you can use a certificate from Let's Encrypt . But ultimately the nginx configuration file will look something like this:
# Plex dashboard, . map $request_method$request_uri$http_referer $do_redirect { "GET/" 1; default 0; } server { # Listen only HTTPS socket listen [::]:443; # Enter your domain here server_name plex.example.com; # Configure your SSL certificates here ssl on; include ssl.conf; ssl_trusted_certificate ssl/ca-certs.pem; ssl_certificate ssl/plex.example.com.pem; ssl_certificate_key ssl/plex.example.com-key.pem; # Protect Plex by basic auth auth_basic "denied"; auth_basic_user_file .htpasswd; # Redirect to the Plex dashboard if ($do_redirect = 1) { return 302 https://$host/web; } # Default location location / { # Plex proxy_set_header Authorization ""; proxy_buffering off; proxy_pass http://localhost:32400; } # Websockets location location /:/websockets/ { # Plex proxy_set_header Authorization ""; proxy_buffering off; proxy_pass http://localhost:32400; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }
References:
Source: https://habr.com/ru/post/306886/
All Articles