From 3532edc252943bf13b4618e10b6db42cfb13bd54 Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Sat, 12 Jun 2021 17:09:08 +0200 Subject: [PATCH] add resume playback functionality --- README.org | 41 ++++++---- strm | 211 ++++++++++++++++++++++++++++++---------------------- strm.config | 14 ++-- 3 files changed, 158 insertions(+), 108 deletions(-) diff --git a/README.org b/README.org index 6e9ab16..be3f748 100644 --- a/README.org +++ b/README.org @@ -22,7 +22,7 @@ strm daily dweebs #+end_src - Every argument which is not assigned to a flag is interpreted as part of the query. + Every argument which is not assigned to an flag is interpreted as part of the query. The query arguments are interpreted as [[https://en.wikipedia.org/wiki/Glob_(programming)][glob patterns]], additionally surrounded by wildcards (=*=). If every pattern matches any filepath in the configured media directories, the matched filepaths are interpreted as result. The =--or= flag can be set to get results which match at least one pattern. @@ -51,14 +51,11 @@ - List remote files by query - Play remote files via mpv - Query multiple remote directories with one command - - Play remote files on other machines (need to have *strm* installed) - + - Play remote files on other machines + - Synchronize playback positions with remote machines + You can access your local machine also via SSH, so every occurrence of /remote/ in the list can be replaced with /local/. - mpv has a nice feature which lets you resume playback. - This is handy if you want to e.g. continue watching you favourite series at a later point in time. - Check the *usage* below to see how this works. - ** Dependencies The dependencies vary, based on the purpose of a machine: @@ -98,8 +95,10 @@ -l, --list List files instead of playing -m, --media-directories MEDIA_DIRECTORIES Use given media directories, config is ignored -o, --or Use a logical OR for queries (default: AND) + -p, --playback-directory DIRECTORY Playback files directory -r, --remote SSH_CONNECTION_STRING Execute strm with other given arguments on remote machine (-f is set by default) -s, --shuffle Play files in random order + -t, --tidy Don't resume playback and don't synchronize playback positions EXAMPLES strm -l . # List all available files @@ -115,12 +114,8 @@ - =<= and =>= :: Go backward/forward in the playlist. - =f= :: Toggle fullscreen. - =q= :: Stop playing and quit. - - =Q= :: Like =q=, but store the current playback position. - Playing the same file later will resume at the old playback position if possible. - This works for single and mutliple files. - To listen e.g. to an audiobook in multiple stages you can always use the same command (=strm audiobook=). - Just make sure you always quit with =Q=. - To clear all stored positions remove the directory (=~/.config/mpv/watch_later=). + If the =playback_directory= is configured or the =--playback-directory= argument ist set, the playback position of the current file is saved on exit and synchronized with the given directory. + Otherwise (or if the =--neat= flag is set) the playback position is not saved on exit and nothing is synchronized. *** The =--remote= flag @@ -155,11 +150,27 @@ # remote-machine/home/bob/music # bob@another-machine/media/movies,bob@10.0.0.1/home/bob/series media_directories="" - #+end_src + # Directory with which the saved playback positions are synchronized on exit of the following form: + # + # + # + # If it is not set, the mpv resume playback functionality is not enabled by default. + # + # Examples: + # localhost/home/bob/.strm + # remote-machine/home/bob/strm + # bob@another-machine/home/bob/strm + playback_directory="" + #+end_src + An example configuration file can be found in the repository. The default path of the configuration is =$HOME/.config/strm/strm.config=. You can use the =--config= flag to set another configuration file. - The flag =--media-directories= is used in favour of a configuration file and can be used with the same syntax as described above. + The flags =--media-directories= and =--playback-directory= are used in favour of configuration parameters and can be used with the same syntax as described above. + Be aware that if the =--media-directories= flag is given, no configuraion file is loaded (even if explicitly set with =--config=) and thus the configured =playback_directory= is not used. + In this case use additionally the =--playback-directory= flag with the same value from your config file. + + The local directory to which and from which the playback positions are synchronized is =$HOME/.cache/strm=. diff --git a/strm b/strm index e758a1c..8a02730 100755 --- a/strm +++ b/strm @@ -12,14 +12,14 @@ function print_usage { echo "OPTIONS" echo " -h, --help Show this help message" echo " -c, --config CONFIG_FILE Path to config file (default: ~/.config/strm/strm.config)" - echo " -d, --resume-directory DIRECTORY Resume files directory (default: ~/.cache/strm)" echo " -f, --fullscreen Play video files in fullscreen" echo " -l, --list List files instead of playing" echo " -m, --media-directories MEDIA_DIRECTORIES Use given media directories, config is ignored" - echo " -n, --neat Don't resume playback" echo " -o, --or Use a logical OR for queries (default: AND)" + echo " -p, --playback-directory DIRECTORY Playback files directory" echo " -r, --remote SSH_CONNECTION_STRING Execute strm with other given arguments on remote machine (-f is set by default)" echo " -s, --shuffle Play files in random order" + echo " -t, --tidy Don't resume playback and don't synchronize playback positions" echo echo "EXAMPLES" echo " strm -l . # List all available files" @@ -29,11 +29,11 @@ function print_usage { } function print_controls { - echo -ne "\n[${text_bold}p${text_reset}] Play/Pause, [${text_bold}<${text_reset}/${text_bold}>${text_reset}] Previous/Next, [${text_bold}q${text_reset}] Quit, [${text_bold}Q${text_reset}] Save position and quit\n" + echo -ne "\n[${text_bold}p${text_reset}] Play/Pause, [${text_bold}<${text_reset}/${text_bold}>${text_reset}] Previous/Next, [${text_bold}q${text_reset}] Quit\n" } function print_remote_controls { - echo -ne "\n[${text_bold}p${text_reset}] Play/Pause, [${text_bold}<${text_reset}/${text_bold}>${text_reset}] Previous/Next, [${text_bold}q${text_reset}] Quit, [${text_bold}Q${text_reset}] Save position and quit, [${text_bold}C-b d${text_reset}] Detach from session\n" + echo -ne "\n[${text_bold}p${text_reset}] Play/Pause, [${text_bold}<${text_reset}/${text_bold}>${text_reset}] Previous/Next, [${text_bold}q${text_reset}] Quit, [${text_bold}C-b d${text_reset}] Detach from session\n" } function error { @@ -52,76 +52,76 @@ or=false queries=() remote="" remote_arguments=() -resume_directory="" +tmp_playback_directory="" shuffle=false # Parse arguments while (( "$#" )); do case "$1" in -c|--config) - if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then - config="$2" - remote_arguments+=("$1" "$2") - shift 2 - else - error "Argument for '$1' is missing" - fi + if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then + config="$2" + remote_arguments+=("$1" "$2") + shift 2 + else + error "Argument for '$1' is missing" + fi ;; - -d|--resume-directory) - if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then - resume_directory=="$2" - remote_arguments+=("$1" "$2") - shift 2 - else - error "Argument for '$1' is missing" - fi + -d|--playback-directory) + if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then + tmp_playback_directory="$2" + remote_arguments+=("$1" "$2") + shift 2 + else + error "Argument for '$1' is missing" + fi + ;; + -f|--fullscreen) + fullscreen=true + shift ;; - -f|--fullscreen) - fullscreen=true - shift - ;; -h|--help) print_usage ;; - --is-remote-call) - is_remote_call=true - shift - ;; - -l|--list) - list=true - remote_arguments+=("$1") + --is-remote-call) + is_remote_call=true shift ;; - -m|--media-directories) - if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then - media_directories="$2" - remote_arguments+=("$1" "$2") - shift 2 - else - error "Argument for '$1' is missing" - fi + -l|--list) + list=true + remote_arguments+=("$1") + shift + ;; + -m|--media-directories) + if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then + media_directories="$2" + remote_arguments+=("$1" "$2") + shift 2 + else + error "Argument for '$1' is missing" + fi ;; -n|--neat) neat=true - remote_arguments+=("$1") - shift - ;; - -o|--or) - or=true - remote_arguments+=("$1") - shift - ;; - -r|--remote) - if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then - remote="$2" - shift 2 - else - error "Argument for '$1' is missing" - fi + remote_arguments+=("$1") + shift + ;; + -o|--or) + or=true + remote_arguments+=("$1") + shift + ;; + -r|--remote) + if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then + remote="$2" + shift 2 + else + error "Argument for '$1' is missing" + fi ;; -s|--shuffle) shuffle=true - remote_arguments+=("$1") + remote_arguments+=("$1") shift ;; -*|--*=) @@ -129,7 +129,7 @@ while (( "$#" )); do ;; *) queries+=("$1") - remote_arguments+=("$1") + remote_arguments+=("$1") shift ;; esac @@ -140,33 +140,33 @@ if [ "$remote" != "" ]; then # Check if dependencies are fulfilled on remote if ! ssh "$remote" "command -v mpv strm tmux &>/dev/null"; then - error "make sure ${text_bold}$remote${text_reset} is accessible and mpv, strm and tmux are installed" + error "make sure ${text_bold}$remote${text_reset} is accessible and mpv, strm and tmux are installed" fi # Check if strm tmux session is already running if ssh -o ConnectTimeout=10 "$remote" "tmux has-session -t strm &>/dev/null"; then - # Attach to tmux session - ssh -o ConnectTimeout=10 -t "$remote" "tmux attach-session -t strm &>/dev/null" + # Attach to tmux session + ssh -o ConnectTimeout=10 -t "$remote" "tmux attach-session -t strm &>/dev/null" else - # Print usage if no queries were given - if [ "${#queries[@]}" == 0 ]; then - echo -ne "No strm session running on ${text_bold}$remote${text_reset}, please provide a query\n" - exit - fi - - # Invert fullscreen argument - if [ "$fullscreen" == false ]; then - remote_arguments+=("-f") - fi + # Print usage if no queries were given + if [ "${#queries[@]}" == 0 ]; then + echo -ne "No strm session running on ${text_bold}$remote${text_reset}, please provide a query\n" + exit + fi - # Notify strm about remote session - remote_arguments+=("--is-remote-call") + # Invert fullscreen argument + if [ "$fullscreen" == false ]; then + remote_arguments+=("-f") + fi - # Execute strm on remote machine - ssh -o ConnectTimeout=10 -t "$remote" "DISPLAY=:0 tmux new-session -s strm 'tmux set-option status off; strm ${remote_arguments[@]}' &>/dev/null" + # Notify strm about remote session + remote_arguments+=("--is-remote-call") + + # Execute strm on remote machine + ssh -o ConnectTimeout=10 -t "$remote" "DISPLAY=:0 tmux new-session -s strm 'tmux set-option status off; strm ${remote_arguments[@]}' &>/dev/null" fi exit fi @@ -186,17 +186,22 @@ if [ "$media_directories" == "" ]; then # Read config file if test -f "$config"; then - source "$config" + source "$config" else - error "Config file not found ($config)" + error "Config file not found ($config)" fi # Throws error if still no media directory set if [ "$media_directories" == "" ]; then - error "No media directories specified" + error "No media directories specified" fi fi +# Override playback_directory if argument set +if [ "$tmp_playback_directory" != "" ]; then + playback_directory="$tmp_playback_directory" +fi + # Read media directories IFS="," read -a media_directories <<< "$media_directories" @@ -229,11 +234,11 @@ for media_directory in "${media_directories[@]}"; do # Check validity of variables if [ "$connection_string" == "" ] || [ "$directory" == "" ]; then - error "Not a valid media directory: $media_directory" + error "Not a valid media directory: $media_directory" fi - + echo -ne "Fetching results from $text_bold$(basename $directory)$text_reset on $text_bold$connection_string$text_reset\n" - + # Add leading and trailing slash to directory if missing [[ "$directory" != /*/ ]] && directory="/$directory/" @@ -244,8 +249,8 @@ for media_directory in "${media_directories[@]}"; do # Build SFTP strings and printable strings for i in "${!tmp_results[@]}"; do tmp_sftp_results["$i"]="sftp://$connection_string${tmp_results[$i]}" - tmp_print_result="$text_bold$connection_string$text_reset ${tmp_results[$i]}" - tmp_print_results["$i"]="${tmp_print_result/$directory/}" + tmp_print_result="$text_bold$connection_string$text_reset ${tmp_results[$i]}" + tmp_print_results["$i"]="${tmp_print_result/$directory/}" done sftp_results=("${sftp_results[@]}" "${tmp_sftp_results[@]}") print_results=("${print_results[@]}" "${tmp_print_results[@]}") @@ -256,10 +261,10 @@ echo # Exit if no results found if [ "${#sftp_results[@]}" == 0 ]; then if [ "$is_remote_call" == true ]; then - echo "No files found, press something to quit" - read -n 1 + echo "No files found, press something to quit" + read -n 1 else - echo "No files found" + echo "No files found" fi exit fi @@ -285,10 +290,11 @@ done # Play results if --list flag not set if [ "$list" == false ]; then + # Print controls if [ "$is_remote_call" == true ]; then - print_remote_controls + print_remote_controls else - print_controls + print_controls fi # Construct addtitional mpv arguments @@ -301,8 +307,39 @@ if [ "$list" == false ]; then fi if [ "$neat" == true ]; then mpv_arguments+=("--no-resume-playback") + elif [ "$playback_directory" != "" ]; then + + # Make local playback directory if not existent + mkdir -p "$HOME/.cache/strm" + + # Get connection string and remote directory + IFS="/" read -r connection_string directory <<< "$playback_directory" + + # Add leading and trailing slash to directory if missing + [[ "$directory" != /*/ ]] && directory="/$directory/" + + # Correct empty connection string + if [ "$connection_string" == "" ]; then + connection_string="localhost" + fi + + # Make remote directory if not existent + ssh -o ConnectTimeout=10 "$connection_string" "mkdir -p $directory" + + # Synchronize remote to local + rsync -az --delete "$connection_string:$directory" "$HOME/.cache/strm/" + + # Add mpv argument + mpv_arguments+=("--save-position-on-quit") + fi # Play all remote files - mpv --msg-level=all=error,statusline=status --term-status-msg='${playlist-pos-1}/${playlist-count} - ${time-pos}/${duration} - \e[1m${metadata/artist:}${?metadata/artist: - }${metadata/album:}${?metadata/album: - }${metadata/title:}${!metadata/title:${filename/no-ext}}\e[0m' "${mpv_arguments[@]}" "${sftp_results[@]}" + mpv --msg-level=all=error,statusline=status --watch-later-directory="$HOME/.cache/strm" --term-status-msg='${playlist-pos-1}/${playlist-count} - ${time-pos}/${duration} - \e[1m${metadata/artist:}${?metadata/artist: - }${metadata/album:}${?metadata/album: - }${metadata/title:}${!metadata/title:${filename/no-ext}}\e[0m' "${mpv_arguments[@]}" "${sftp_results[@]}" + + # Synchronize playback directory back if not neat and directory is set + if [ "$neat" == false ] && [ "$playback_directory" != "" ]; then + rsync -az --delete "$HOME/.cache/strm/" "$connection_string:$directory" + fi + fi diff --git a/strm.config b/strm.config index f4ee761..c3a9e20 100644 --- a/strm.config +++ b/strm.config @@ -12,12 +12,14 @@ # bob@another-machine/media/movies,bob@10.0.0.1/home/bob/series media_directories="" -# Resume directory in which playback positions are stored on exit. -# Can also be located on a remote machine which is accessible via SSH. +# Directory with which the saved playback positions are synchronized on exit of the following form: # -# Default: ~/.cache/strm +# +# +# If it is not set, the mpv resume playback functionality is not enabled by default. # # Examples: -# ~/.strm -# remote-machine:/path/on/remote -resume_directory="" \ No newline at end of file +# localhost/home/bob/.strm +# remote-machine/home/bob/strm +# bob@another-machine/home/bob/strm +playback_directory="" \ No newline at end of file