#!/bin/bash ######## INDEX ######## # GPT -> getPlexToken # GPS -> getPlexServerToken # GPW -> getPlexWebToken # HELPERS -> keypair, rawurlencode, trimQuotes # RNNG -> running # SHARED -> warn, info, warn ######## CONSTANTS ######## # Current pages we need - Do not change unless Plex.tv changes again URL_LOGIN='https://plex.tv/users/sign_in.json' URL_DOWNLOAD='https://plex.tv/api/downloads/5.json?channel=plexpass' URL_DOWNLOAD_PUBLIC='https://plex.tv/api/downloads/5.json' # Default options for package managers, override if needed REDHAT_INSTALL="dnf -y install" DEBIAN_INSTALL="dpkg -i" DISTRO_INSTALL="" #URL for new version check UPSTREAM_GIT_URL="https://raw.githubusercontent.com/${GIT_OWNER:-mrworf}/plexupdate/${BRANCHNAME:-master}" #Files "owned" by plexupdate, for autoupdate PLEXUPDATE_FILES="plexupdate.sh plexupdate-core extras/installer.sh extras/cronwrapper" ######## FUNCTIONS ######## #### Token Management ##### # GPT getPlexToken() { if [ -n "$TOKEN" ]; then [ "$VERBOSE" = "yes" ] && info "Fetching token from config" elif getPlexServerToken; then [ "$VERBOSE" = "yes" ] && info "Fetching token from Plex server" elif [ -z "$TOKEN" -a -n "$EMAIL" -a -n "$PASS" ]; then warn "Storing your email and password has been deprecated. Please re-run extras/installer.sh or see https://github.com/mrworf/plexupdate#faq" getPlexWebToken # Check if we're connected to a terminal elif [ -z "$TOKEN" -a -t 0 ]; then info "To continue, you will need to provide your Plex account credentials." info "Your email and password will only be used to retrieve a 'token' and will not be saved anywhere." echo while true; do read -e -p "PlexPass Email Address: " -i "$EMAIL" EMAIL if [ -z "${EMAIL}" ] || [[ "$EMAIL" == *"@"* ]] && [[ "$EMAIL" != *"@"*"."* ]]; then info "Please provide a valid email address" else break fi done while true; do read -e -s -p "PlexPass Password: " -i "$PASS" PASS if [ -z "$PASS" ]; then info "Please provide a password" else break fi done getPlexWebToken fi [ -n "$TOKEN" ] # simulate exit status } # GPS getPlexServerToken() { if [ -f /etc/default/plexmediaserver ]; then source /etc/default/plexmediaserver fi # List possible locations to find Plex Server preference file local VALIDPATHS=("${PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR}" "/var/lib/plexmediaserver/Library/Application Support/" "${HOME}/Library/Application Support/") local PREFFILE="/Plex Media Server/Preferences.xml" for I in "${VALIDPATHS[@]}" ; do if [ ! -z "${I}" -a -f "${I}${PREFFILE}" ]; then # When running installer.sh directly from wget, $0 will return bash if [ "$(basename $0)" = "installer.sh" -o "$(basename $0)" = "bash" ]; then TOKEN=$(sudo sed -n 's/.*PlexOnlineToken="\([[:alnum:]]*\).*".*/\1/p' "${I}${PREFFILE}" 2>/dev/null) else TOKEN=$(sed -n 's/.*PlexOnlineToken="\([[:alnum:]]*\).*".*/\1/p' "${I}${PREFFILE}" 2>/dev/null) fi fi done [ -n "$TOKEN" ] # simulate exit status } # GPW getPlexWebToken() { local FILE_POSTDATA=$(mktemp /tmp/plexupdate.postdata.XXXX) local FILE_RAW=$(mktemp /tmp/plexupdate.raw.XXXX) local FILE_FAILCAUSE=$(mktemp /tmp/plexupdate.failcause.XXXX) # Fields we need to submit for login to work # # Field Value # utf8 ✓ # authenticity_token # user[login] $EMAIL # user[password] $PASS # user[remember_me] 0 # commit Sign in # Build post data echo -ne >"${FILE_POSTDATA}" "$(keypair "user[login]" "${EMAIL}" )" echo -ne >>"${FILE_POSTDATA}" "&$(keypair "user[password]" "${PASS}" )" echo -ne >>"${FILE_POSTDATA}" "&$(keypair "user[remember_me]" "0" )" # Authenticate (using Plex Single Sign On) wget --header "X-Plex-Client-Identifier: 4a745ae7-1839-e44e-1e42-aebfa578c865" --header "X-Plex-Product: Plex SSO" "${URL_LOGIN}" --post-file="${FILE_POSTDATA}" -q -S -O "${FILE_FAILCAUSE}" 2>"${FILE_RAW}" # Provide some details to the end user local RESULTCODE=$(head -n1 "${FILE_RAW}" | grep -oe '[1-5][0-9][0-9]') if [ $RESULTCODE -eq 401 ]; then error "Username and/or password incorrect" elif [ $RESULTCODE -ne 201 ]; then error "Failed to log in, debug information:" cat "${FILE_RAW}" >&2 else TOKEN=$(<"${FILE_FAILCAUSE}" grep -ioe '"authToken":"[^"]*' | cut -c 14-) fi # Clean up temp files since they may contain sensitive information rm "${FILE_FAILCAUSE}" "${FILE_POSTDATA}" "${FILE_RAW}" [ -n "$TOKEN" ] # simulate exit status } # HELPERS keypair() { local key="$( rawurlencode "$1" )" local val="$( rawurlencode "$2" )" echo "${key}=${val}" } rawurlencode() { local string="${1}" local strlen=${#string} local encoded="" for (( pos=0 ; pos/dev/null) || return 1 sha1sum <<< "$RESULT" | cut -f1 -d" " } getLocalSHA() { [ -f "$1" ] || return 1 sha1sum "$1" | cut -f1 -d" " } # RNNG running() { # If a server is unclaimed, it probably doesn't have TLS enabled either local DATA # get Plex sessions DATA="$(wget -q -O - http://$1:$2/status/sessions)" local RET=$? # and live TV/DVR sessions DATA+="$(wget -q -O - http://$1:$2/livetv/sessions)" if [ ${RET} -eq 6 ]; then # Server may be claimed, in which case we should use TLS and pass a token getPlexToken DATA="$(wget --no-check-certificate -q -O - https://$1:$2/status/sessions?X-Plex-Token=$TOKEN)" RET=$? DATA+="$(wget --no-check-certificate -q -O - https://$1:$2/livetv/sessions?X-Plex-Token=$TOKEN)" fi if [ ${RET} -eq 0 ]; then # Get a total count of active media (MediaContainer size), then deduct one for every paused stream. # If all streams are paused, we consider the server to not be active. local mediacount="$(awk -F'"' '// {count+=$2}; /]* state="paused"/ {count--}; END {print count}' <<< "${DATA}")" [ "$VERBOSE" = "yes" ] && printf 'Activity check reports a count of %i, based on return data of:\n%s\n\n' "${mediacount}" "${DATA}" >&2 [ $mediacount -gt 0 ] && return 0 fi # if we don't know for sure that Plex is busy, assume that it's not so the update can proceed return 1 } verifyToken() { wget -qO /dev/null "https://plex.tv/api/resources?X-Plex-Token=${TOKEN}" } isNewerVersion() { # Returns true ONLY if 1 > 2. [ -z "$1" ] && return 1 [ -z "$2" ] && return [ "$1" = "$2" ] && return 1 if hash dpkg 2>/dev/null; then dpkg --compare-versions "$1" gt "$2" elif sort -V --version &>/dev/null; then [ "$(printf "$1\n$2" | sort -Vr | head -n1)" = "$1" ] else # sort has had -V since at least 2009, so nobody should ever see this warn "Unable to compare version numbers, assuming '$1' is newer." fi } parseVersion() { if [ "${DISTRO}" = "redhat" ]; then cut -f2- -d- <<< "$1" | cut -f1-4 -d. elif [ "${DISTRO}" = "synology" ]; then cut -f2-3 -d- <<< "$1" else cut -f2 -d_ <<< "$1" fi } getPlexVersion() { if [ "${DISTRO}" = "debian" ]; then dpkg-query --showformat='${Version}' --show plexmediaserver 2>/dev/null elif [ "${DISTRO}" = "synology" ]; then synopkg version "Plex Media Server" 2>/dev/null elif [ "${DISTRO}" = "redhat" ]; then local rpmtemp if rpmtemp=$(rpm -q plexmediaserver); then parseVersion "$rpmtemp" else return 1 fi else error "Unknown OS, exiting." exit 1 fi } verboseOutput() { if [ "$VERBOSE" = "yes" ]; then for i in $@; do info "$i=${!i}" done fi } # Shared functions # SHARED warn() { echo "WARNING: $@" >&1 } info() { echo "$@" >&1 } error() { echo "ERROR: $@" >&2 } # Intentionally leaving this hard to find so that people aren't trying to use it manually. if [ "$(basename "$0")" = "get-plex-token" ]; then [ -f /etc/plexupdate.conf ] && source /etc/plexupdate.conf getPlexToken && info "Token = $TOKEN" fi