Skip to content
View in the app

A better way to browse. Learn more.

Unraid

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

doobyns

Members
  • Joined

  • Last visited

  1. Version of this script with latests improvements, still rockin' in 2026 ! (note : I removed transmission section caus' I don't use it, feel free to add it again). #!/bin/bash # This script gets playing sessions from Plex, and tells rsync to copy them from array to cache drive. # It also cleans oldest modified files by rsyncing them back to array (in case of modification). # Hardlink are preserved # # By Reynald mailto:[email protected] # Now works with plex v1.40 or later # WARNING this script must be scheduled carefully one time every 6 minutes max (10 minutes advised), execute it more frequently can damage your SSD # Correction of file permissions by Reynald # By Quinto added analyze of media by plex after syncing to prevent wrong media duration (2hrs bug) # By Quinto Now work with multiple parts movie and with multiple medias movie (eg Same movie in 1080p and 4k) # v.0.6.2 # settings { #Plex # put here the corresponding docker name of your plex App egg binhex-plex, Plex-Media-Server, etc PLEX_DOCKER="Plex-Media-Server" PLEX_TOKEN="xxxxxxxxxxxxxxxxx" PLEX_HOST="192.168.0.240:32400" PLEX_MAX_CACHED_SESSIONS=16 PLEX_CONTAINER_PATH="video_data" #Rsync path & options STORAGE_PATH="/mnt/user0/video_data/" CACHE_PATH="/mnt/plex_smart_cache/video_data/" CACHE_MIN_FREE_SPACE_PCT="94" CACHE_MAX_FREE_SPACE_PCT="60" #Parameters LOG_MAX_SIZE=5000000 NOISY_HOUR_START= NOISY_HOUR_STOP= # if you're not using Youtube-dl agent for plex don't touch this line, it must only be changed when you're using yt-dl custom agent YOUTUBE_DL_AGENT="com.plexapp.agents.youtube-dl" #Options (set to true or false) DRY_RUN=false WRITE_ON_STORAGE=false #number of episodes to cache PLEX_CACHE_NB_EPISODES=6 PLEX_CACHE_SEASON_TILL_END=false VERBOSE=0 #0=Error; 1=Info; 2=More_Info; 3=Debug } ##### No modification below this line ##### CACHE_DISK=$(df -lah "$CACHE_PATH" | awk 'FNR == 2 {print $1}' | tr -d '\n') sys_checks() { # check if plex docker is running plex_running=`docker inspect -f '{{.State.Running}}' $PLEX_DOCKER` if [[ ! $plex_running ]] then echo "Error: Plex docker is not running" rm /var/lock/smart-cache_plex_transmission exit 1 fi # lock if [[ -f /var/lock/smart-cache_plex_transmission ]] then echo "Error: Script already running" rm /var/lock/smart-cache_plex_transmission exit 1 else touch /var/lock/smart-cache_plex_transmission [[ $VERBOSE -ge 2 ]] && echo "Welcome to $0" fi # check that path are mounted if [[ ! -d $STORAGE_PATH ]] || [[ ! -d $CACHE_PATH ]]; then echo "Error: Paths are not accessibles" rm /var/lock/smart-cache_plex_transmission exit 1 fi # cut log LOG_FILE=$(echo $0 | sed 's|\/script|\/log.txt|') echo "log= $LOG_FILE" LOG_SIZE=$(stat -c %s "$LOG_FILE") [[ $VERBOSE -ge 1 ]] && echo "Info: Log size is $LOG_SIZE" if [[ $LOG_SIZE -ge $LOG_MAX_SIZE ]] then [[ $VERBOSE -ge 1 ]] && echo "Info: Emptying log file" echo "" > "$LOG_FILE" fi [[ $VERBOSE -ge 1 ]] && echo "" } ####################### # Transfers functions # ####################### noisy_hours() { # return 0 if time in noisy hour range if [[ $(date '+%-H') -ge $NOISY_HOUR_START ]] && [[ $(date +%-H) -le $NOISY_HOUR_STOP ]] then return 0 else return 1 fi } rsync_transfer() { # get files and path SOURCE_FILE=$1 DEST_FILE=$2 SOURCE_PATH=$3 DEST_PATH=$4 PLEX_ANALYZE_STRING=$5 RS_OPTIONS=$6 [[ $VERBOSE -ge 3 ]] && echo " --- Debug:Rsync_transfer function parameters:" [[ $VERBOSE -ge 3 ]] && echo " ---- Debug: Source file: $SOURCE_FILE" [[ $VERBOSE -ge 3 ]] && echo " ---- Debug: Dest. file: $DEST_FILE" [[ $VERBOSE -ge 3 ]] && echo " ---- Debug: Source path: $SOURCE_PATH" [[ $VERBOSE -ge 3 ]] && echo " ---- Debug: Dest. path: $DEST_PATH" [[ $VERBOSE -ge 3 ]] && echo " ---- Debug: Plex Analyze string: $PLEX_ANALYZE_STRING" [[ $VERBOSE -ge 3 ]] && echo " ---- Debug: Options : $RS_OPTIONS" # check if original file exist if [[ ! -f "${SOURCE_FILE}" ]] && [[ ! -f "${DEST_FILE}" ]] then echo " --- Error: Files:" echo " ${SOURCE_FILE}" echo " ${DEST_FILE}" echo " does not exist" return 1 elif [[ "${DEST_FILE}" = "${DEST_PATH}" ]] || [[ "${SOURCE_FILE}" = "${SOURCE_PATH}" ]] then echo " --- Error: Cannot sync root path!" return 1 elif [[ ! -f "${SOURCE_FILE}" ]] && [[ "${DEST_PATH}" = "${CACHE_PATH}" ]] && [[ -f "${DEST_FILE}" ]] then if noisy_hours then if [[ ! WRITE_ON_STORAGE ]] then [[ $VERBOSE -ge 2 ]] && echo " --- Info: File is on cache only. Inside noisy hours, sending to storage" rsync_transfer "${DEST_FILE}" "${SOURCE_FILE}" "${DEST_PATH}" "${SOURCE_PATH}" "${PLEX_ANALYZE_STRING}" fi else [[ $VERBOSE -ge 2 ]] && echo " --- Warning: File is on cache only. Outside of noisy hours, doing nothing" fi return elif [[ -f "${DEST_FILE}" ]] && [[ "${DEST_PATH}" = "${CACHE_PATH}" ]] then [[ $VERBOSE -ge 1 ]] && echo " --- Info: File already cached" return fi # get dir SOURCE_DIR=$(dirname "${SOURCE_FILE}") DEST_DIR=$(dirname "${DEST_FILE}") if ! $DRY_RUN then # sync file sudo mkdir -p -m u-x,go-rwx,go+u,ugo+X "${DEST_DIR}" [[ $VERBOSE -ge 1 ]] && echo " --- Info: Syncing ${SOURCE_FILE} to ${DEST_FILE}" sudo nice -n 13 ionice -c 3 /usr/bin/rsync --bwlimit=190000 -aHq "${SOURCE_FILE}" "${DEST_FILE}" if [[ ! $? -eq 0 ]] then echo " --- Error: cannot rsync ${SOURCE_FILE}" echo " to ${DEST_FILE}" return 1 else [[ $VERBOSE -ge 1 ]] && echo " --- Info: Syncing succeeded - refreshing plex media" sleep 0.8 curl --limit-rate 400k --silent "${PLEX_ANALYZE_STRING}" fi # remove original file if requested if [[ "${RS_OPTIONS}" = "--remove-source-files" ]] && $RSYNC_RESULT then [[ $VERBOSE -ge 2 ]] && echo " --- Info: Delete ${SOURCE_FILE}" sudo nice -n 13 ionice -c 3 /usr/bin/rm "${SOURCE_FILE}" fi else echo " --- Running in DRY RUN mode : Files aren't really copied, doing nothing" fi } ################ # Plex Caching # ################ plex_cache() { # get Plex sessions STATUS_SESSIONS=$(nice -n 10 curl --limit-rate 400k --silent http://${PLEX_HOST}/status/sessions -H "X-Plex-Token: $PLEX_TOKEN") if [[ -z $STATUS_SESSIONS ]]; then echo "Error: Cannot connect to plex" return 1 fi NB_SESSIONS=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/@size)' -) echo "----------------------------" echo "$NB_SESSIONS active(s) plex session(s):" echo "----------------------------" # for each session if [[ $NB_SESSIONS -gt $PLEX_MAX_CACHED_SESSIONS ]] then NB_SESSIONS=$PLEX_MAX_CACHED_SESSIONS echo "Warning: Caching is limited to $PLEX_MAX_CACHED_SESSIONS plex sessions (user setting)" fi for i in `seq $NB_SESSIONS` do # get title ID=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@ratingKey)' -) TYPE=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@type)' -) MKEY=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@key)' -) # eventually get serie info if [[ $TYPE = "episode" ]] then TYPE="Serie" GRANDPARENTTITLE=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@grandparentTitle)' -) SEASON=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@parentIndex)' -) TITLE=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@title)' -) EPISODE=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@index)' -) PARENT_ID=$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@parentRatingKey)' -) PLEX_FILE_SESS_TVSHOW=$(nice -n 10 curl --limit-rate 400k --silent http://${PLEX_HOST}/library/metadata/$PARENT_ID/children) PARENT_NB_EPISODES=$(echo $PLEX_FILE_SESS_TVSHOW | xmllint --xpath 'string(//MediaContainer/@size)' -) PARENT_START_EPISODE=$(echo $PLEX_FILE_SESS_TVSHOW | xmllint --xpath 'string(//MediaContainer/Video[1]/@index)' -) SECTION_ID=$(curl --limit-rate 400k --silent http://${PLEX_HOST}/library/metadata/$PARENT_ID | xmllint --xpath 'string(//MediaContainer/@librarySectionID)' -) SECTION_AGENT=$(curl --limit-rate 400k --silent http://${PLEX_HOST}/library/sections?X-Plex-Token= | xmllint --xpath 'string(//MediaContainer/Directory[@key='$SECTION_ID']/@agent)' -) # update nb file to cache START_FILE="$EPISODE" if [[ "${SECTION_AGENT}" = "${YOUTUBE_DL_AGENT}" ]] then TYPE="Youtube" NB_FILES=1 else if [[ $PLEX_CACHE_NB_EPISODES -gt 0 ]]; then NB_FILES=$(( ($EPISODE + $PLEX_CACHE_NB_EPISODES) < $PARENT_NB_EPISODES ? ($EPISODE + $PLEX_CACHE_NB_EPISODES) : $PARENT_NB_EPISODES )) else $PLEX_CACHE_NEXT_EPISODE && NB_FILES=$(( $EPISODE + 1)) fi $PLEX_CACHE_SEASON_TILL_END && NB_FILES=$(( $PARENT_NB_EPISODES )) fi TITLE="$TYPE: ${GRANDPARENTTITLE} Season ${SEASON} - Episode ${EPISODE}/${PARENT_NB_EPISODES}: $TITLE" elif [[ $TYPE = "movie" ]] then TYPE="Movie" TITLE="$(echo $STATUS_SESSIONS | xmllint --xpath 'string(//MediaContainer/Video['$i']/@title)' -)" TITLE="$TYPE: $TITLE" PLEX_FILE_SESS_MOVIE=$(nice -n 10 curl --limit-rate 400k --silent http://${PLEX_HOST}/library/metadata/$ID) else TYPE="Audio" TITLE="track caching not implemented" TITLE="$TYPE: $TITLE" START_FILE=0 NB_FILES=0 fi echo " - $i / $NB_SESSIONS: $TITLE" NB_MEDIAS=$(echo $PLEX_FILE_SESS_MOVIE | xmllint --xpath "count(//Media)" -) NB_FILES=$(echo $PLEX_FILE_SESS_MOVIE | xmllint --xpath "count(//Part)" -) [[ $VERBOSE -ge 2 ]] && echo " -- Info: Number of medias :${NB_MEDIAS}" [[ $VERBOSE -ge 2 ]] && echo " -- Info: Number of files :${NB_FILES}" START_FILE=1 for j in `seq $START_FILE $NB_FILES` do # get file path if [[ $TYPE = "Audio" ]] then [[ $VERBOSE -ge 2 ]] && echo " -- Info: Skipping" else if [[ $TYPE != "Movie" ]] then if [[ $TYPE = "Youtube" ]] then # media is a youtube episode # [[ $VERBOSE -ge 2 ]] && echo " -- Info: Skipping" PLEX_FILE=$(echo "${PLEX_FILE_SESS_TVSHOW}" | xmllint --xpath 'string(//MediaContainer/Video['$(($PARENT_START_EPISODE - $EPISODE +1))']/Media/Part/@file)' -) ID=$(echo "${PLEX_FILE_SESS_TVSHOW}" | xmllint --xpath 'string(//MediaContainer/Video['$(($PARENT_START_EPISODE - $EPISODE +1))']/@ratingKey)' -) else # media is tv show PLEX_FILE=$(echo "${PLEX_FILE_SESS_TVSHOW}" | xmllint --xpath 'string(//MediaContainer/Video['$(($j - $PARENT_START_EPISODE + 1))']/Media/Part/@file)' -) ID=$(echo "${PLEX_FILE_SESS_TVSHOW}" | xmllint --xpath 'string(//MediaContainer/Video['$(($j - $PARENT_START_EPISODE + 1))']/@ratingKey)' -) fi m=$PARENT_NB_EPISODES else # media is a movie if [[ $NB_FILES -gt 1 ]] then if [[ $NB_FILES -gt $NB_MEDIAS ]] then # multiple parts movie (part1, part2, etc) PLEX_FILE=$(echo "${PLEX_FILE_SESS_MOVIE}" | xmllint --xpath 'string(//MediaContainer/Video/Media/Part['$j']/@file)' -) else # multiple medias movie (4k, 1080p, 720p, etc) PLEX_FILE=$(echo "${PLEX_FILE_SESS_MOVIE}" | xmllint --xpath 'string(//MediaContainer/Video/Media['$j']/Part/@file)' -) fi else # movie in single part / format PLEX_FILE=$(echo "${PLEX_FILE_SESS_MOVIE}" | xmllint --xpath 'string(//MediaContainer/Video/Media/Part/@file)' -) fi echo "${PLEX_FILE}" m=$NB_FILES fi PLEX_ANALYZE="http://${PLEX_HOST}/library/metadata/$ID/analyze?X-Plex-Token: ${PLEX_TOKEN}" FILE_TO_CACHE="$(sed -e 's|\"\"|\/|g' -e 's/%/\%/g' <<<${PLEX_FILE} | sed 's|\"||g' | sed 's|\/'${PLEX_CONTAINER_PATH}'\/||')" [[ $VERBOSE -ge 2 ]] && echo " -- Info: Streaming File $j/$m: ${FILE_TO_CACHE}" STORAGE_FILE="${STORAGE_PATH}${FILE_TO_CACHE}" CACHE_FILE="${CACHE_PATH}${FILE_TO_CACHE}" # and send to rsync rsync_transfer "${STORAGE_FILE}" "${CACHE_FILE}" "${STORAGE_PATH}" "${CACHE_PATH}" "${PLEX_ANALYZE}" fi sleep .1 done sleep .1 done sleep .1 } #################### # Delete old files # #################### cleanup() { # get free space sa=$(df -lah | grep $CACHE_DISK | awk '{ printf "%d", $5 }') sb=$CACHE_MIN_FREE_SPACE_PCT echo "---------------------" echo "Cache disk usage: ${sa}%" echo "---------------------" if [[ "$sa" -ge "$sb" ]]; then echo "$sa% space used, quota is $sb%, cleaning" [[ $VERBOSE -ge 1 ]] && echo "Info: Scanning files..." # get oldest accessed files find "${CACHE_PATH}" -type f -printf "%C@ %p\n" | sort -n | sed "s|`echo ${CACHE_PATH}`|%|g" | cut -d'%' -f2 | while read FILE_TO_CLEAN do # loop start: get free space again sa=$(df -lah | grep $CACHE_DISK | awk '{ printf "%d", $5 }') sb=$CACHE_MAX_FREE_SPACE_PCT # if free space not enough if [[ "$sa" -ge "$sb" ]]; then [[ $VERBOSE -ge 1 ]] && echo " - Info: $sa% space used, target $sb%, uncaching $FILE_TO_CLEAN" STORAGE_FILE="${STORAGE_PATH}${FILE_TO_CLEAN}" CACHE_FILE="${CACHE_PATH}${FILE_TO_CLEAN}" # sync back cache to storage rsync_transfer "${CACHE_FILE}" "${STORAGE_FILE}" "${CACHE_PATH}" "${STORAGE_PATH}" "--remove-source-files" fi # loop done fi sa=$(df -lah | grep $CACHE_DISK | awk '{ printf "%d", $5 }') sb=$CACHE_MIN_FREE_SPACE_PCT [[ $VERBOSE -ge 1 ]] && echo " - Info: $sa% space used, quota is $sb%, nothing to do" # prune empty directories from source dir [[ $VERBOSE -ge 2 ]] && echo " -- Info: Cleaning empty directories..." find "${CACHE_PATH}" -type d -not -path '*/\.*' -empty -prune -exec rmdir --ignore-fail-on-non-empty {} \; [[ $VERBOSE -ge 1 ]] && echo "" } sys_checks plex_cache cleanup rm /var/lock/smart-cache_plex_transmission exit 0
  2. it's NEXUS DOUBLETWIN | HARD DRIVE VIBRATION ISOLATOR : https://silentpc.com/quiet-pc-accessories/doubletwin
  3. ST14000VN0008-2JG101 is the serial number of the involved hard drive, you can try to reseat the sata cable or to change it...
  4. take a look at this thread : https://forums.unraid.net/topic/26050-idenitifying-which-drive-is-quotata9quot-throwing-errors-into-syslog/
  5. it can be bad sata connexion or bad sata cable, or too much vibration during the parity check which can mess with the sata cable, etc...
  6. Just a little update, the script is broken with the latests Plex versions, i'm working on an update, for jellyfin sorry i don't use that at the moment, so, i can't work on it...
  7. doobyns replied to Frankho's topic in Hardware
    yup these are the correct settings, you will notice a slight impact on ram usage but it's hard to notice only with one transoding for only one user...
  8. weird problem, as a temporary workaround you can downgrade to older version if you want, just put "1.29.1.6316-f4cdfea9c-ls139" in the VERSION variable in your Plex docker settings...
  9. can you try with a video with .SRT subtitles ? I think the problem can be PGS subtitles...
  10. i think this video will help you: Unraid - Enabling Intel Quick Sync for Plex Transcoding (and other containers) : https://www.youtube.com/watch?v=tmsXyFVdRAs
  11. do you use "Intel-GPU-TOP" plugin ?
  12. it must be confusion, latest plex version should be : 1.29.1 at first sight your docker settings seems ok, have you ever used specific script to enable quicksync at the start of the plex docker ?
  13. What's the version number of your plex media server ? Can you show us the complete settings of your Plex-Media-Server docker?
  14. all seems normal now, problem is gone after uninstalling "python3.plg" and "python2.plg" and reinstalling python via NerdTools plugin...
  15. Tu devrais te pencher sur les plugins "gpu statistics" et "Radeon-TOP" je pense que tu trouveras ton bonheur...

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.