add resume playback functionality

This commit is contained in:
Denis Lehmann 2021-06-12 17:09:08 +02:00
parent 2cc32b15b2
commit 3532edc252
3 changed files with 158 additions and 108 deletions

View file

@ -22,7 +22,7 @@
strm daily dweebs strm daily dweebs
#+end_src #+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 (=*=). 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. 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. The =--or= flag can be set to get results which match at least one pattern.
@ -51,14 +51,11 @@
- List remote files by query - List remote files by query
- Play remote files via mpv - Play remote files via mpv
- Query multiple remote directories with one command - 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/. 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 ** Dependencies
The dependencies vary, based on the purpose of a machine: The dependencies vary, based on the purpose of a machine:
@ -98,8 +95,10 @@
-l, --list List files instead of playing -l, --list List files instead of playing
-m, --media-directories MEDIA_DIRECTORIES Use given media directories, config is ignored -m, --media-directories MEDIA_DIRECTORIES Use given media directories, config is ignored
-o, --or Use a logical OR for queries (default: AND) -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) -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 -s, --shuffle Play files in random order
-t, --tidy Don't resume playback and don't synchronize playback positions
EXAMPLES EXAMPLES
strm -l . # List all available files strm -l . # List all available files
@ -115,12 +114,8 @@
- =<= and =>= :: Go backward/forward in the playlist. - =<= and =>= :: Go backward/forward in the playlist.
- =f= :: Toggle fullscreen. - =f= :: Toggle fullscreen.
- =q= :: Stop playing and quit. - =q= :: Stop playing and quit.
- =Q= :: Like =q=, but store the current playback position. 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.
Playing the same file later will resume at the old playback position if possible. Otherwise (or if the =--neat= flag is set) the playback position is not saved on exit and nothing is synchronized.
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=).
*** The =--remote= flag *** The =--remote= flag
@ -155,11 +150,27 @@
# remote-machine/home/bob/music # remote-machine/home/bob/music
# bob@another-machine/media/movies,bob@10.0.0.1/home/bob/series # bob@another-machine/media/movies,bob@10.0.0.1/home/bob/series
media_directories="" media_directories=""
#+end_src
# Directory with which the saved playback positions are synchronized on exit of the following form:
#
# <SSH connection string><absolute_path_to_media_directory>
#
# 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. An example configuration file can be found in the repository.
The default path of the configuration is =$HOME/.config/strm/strm.config=. The default path of the configuration is =$HOME/.config/strm/strm.config=.
You can use the =--config= flag to set another configuration file. 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=.

211
strm
View file

@ -12,14 +12,14 @@ function print_usage {
echo "OPTIONS" echo "OPTIONS"
echo " -h, --help Show this help message" echo " -h, --help Show this help message"
echo " -c, --config CONFIG_FILE Path to config file (default: ~/.config/strm/strm.config)" 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 " -f, --fullscreen Play video files in fullscreen"
echo " -l, --list List files instead of playing" echo " -l, --list List files instead of playing"
echo " -m, --media-directories MEDIA_DIRECTORIES Use given media directories, config is ignored" 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 " -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 " -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 " -s, --shuffle Play files in random order"
echo " -t, --tidy Don't resume playback and don't synchronize playback positions"
echo echo
echo "EXAMPLES" echo "EXAMPLES"
echo " strm -l . # List all available files" echo " strm -l . # List all available files"
@ -29,11 +29,11 @@ function print_usage {
} }
function print_controls { 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 { 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 { function error {
@ -52,76 +52,76 @@ or=false
queries=() queries=()
remote="" remote=""
remote_arguments=() remote_arguments=()
resume_directory="" tmp_playback_directory=""
shuffle=false shuffle=false
# Parse arguments # Parse arguments
while (( "$#" )); do while (( "$#" )); do
case "$1" in case "$1" in
-c|--config) -c|--config)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
config="$2" config="$2"
remote_arguments+=("$1" "$2") remote_arguments+=("$1" "$2")
shift 2 shift 2
else else
error "Argument for '$1' is missing" error "Argument for '$1' is missing"
fi fi
;; ;;
-d|--resume-directory) -d|--playback-directory)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
resume_directory=="$2" tmp_playback_directory="$2"
remote_arguments+=("$1" "$2") remote_arguments+=("$1" "$2")
shift 2 shift 2
else else
error "Argument for '$1' is missing" error "Argument for '$1' is missing"
fi fi
;;
-f|--fullscreen)
fullscreen=true
shift
;; ;;
-f|--fullscreen)
fullscreen=true
shift
;;
-h|--help) -h|--help)
print_usage print_usage
;; ;;
--is-remote-call) --is-remote-call)
is_remote_call=true is_remote_call=true
shift
;;
-l|--list)
list=true
remote_arguments+=("$1")
shift shift
;; ;;
-m|--media-directories) -l|--list)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then list=true
media_directories="$2" remote_arguments+=("$1")
remote_arguments+=("$1" "$2") shift
shift 2 ;;
else -m|--media-directories)
error "Argument for '$1' is missing" if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
fi media_directories="$2"
remote_arguments+=("$1" "$2")
shift 2
else
error "Argument for '$1' is missing"
fi
;; ;;
-n|--neat) -n|--neat)
neat=true neat=true
remote_arguments+=("$1") remote_arguments+=("$1")
shift shift
;; ;;
-o|--or) -o|--or)
or=true or=true
remote_arguments+=("$1") remote_arguments+=("$1")
shift shift
;; ;;
-r|--remote) -r|--remote)
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
remote="$2" remote="$2"
shift 2 shift 2
else else
error "Argument for '$1' is missing" error "Argument for '$1' is missing"
fi fi
;; ;;
-s|--shuffle) -s|--shuffle)
shuffle=true shuffle=true
remote_arguments+=("$1") remote_arguments+=("$1")
shift shift
;; ;;
-*|--*=) -*|--*=)
@ -129,7 +129,7 @@ while (( "$#" )); do
;; ;;
*) *)
queries+=("$1") queries+=("$1")
remote_arguments+=("$1") remote_arguments+=("$1")
shift shift
;; ;;
esac esac
@ -140,33 +140,33 @@ if [ "$remote" != "" ]; then
# Check if dependencies are fulfilled on remote # Check if dependencies are fulfilled on remote
if ! ssh "$remote" "command -v mpv strm tmux &>/dev/null"; then 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 fi
# Check if strm tmux session is already running # Check if strm tmux session is already running
if ssh -o ConnectTimeout=10 "$remote" "tmux has-session -t strm &>/dev/null"; then if ssh -o ConnectTimeout=10 "$remote" "tmux has-session -t strm &>/dev/null"; then
# Attach to tmux session # Attach to tmux session
ssh -o ConnectTimeout=10 -t "$remote" "tmux attach-session -t strm &>/dev/null" ssh -o ConnectTimeout=10 -t "$remote" "tmux attach-session -t strm &>/dev/null"
else else
# Print usage if no queries were given # Print usage if no queries were given
if [ "${#queries[@]}" == 0 ]; then if [ "${#queries[@]}" == 0 ]; then
echo -ne "No strm session running on ${text_bold}$remote${text_reset}, please provide a query\n" echo -ne "No strm session running on ${text_bold}$remote${text_reset}, please provide a query\n"
exit exit
fi fi
# Invert fullscreen argument
if [ "$fullscreen" == false ]; then
remote_arguments+=("-f")
fi
# Notify strm about remote session # Invert fullscreen argument
remote_arguments+=("--is-remote-call") if [ "$fullscreen" == false ]; then
remote_arguments+=("-f")
fi
# Execute strm on remote machine # Notify strm about remote session
ssh -o ConnectTimeout=10 -t "$remote" "DISPLAY=:0 tmux new-session -s strm 'tmux set-option status off; strm ${remote_arguments[@]}' &>/dev/null" 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 fi
exit exit
fi fi
@ -186,17 +186,22 @@ if [ "$media_directories" == "" ]; then
# Read config file # Read config file
if test -f "$config"; then if test -f "$config"; then
source "$config" source "$config"
else else
error "Config file not found ($config)" error "Config file not found ($config)"
fi fi
# Throws error if still no media directory set # Throws error if still no media directory set
if [ "$media_directories" == "" ]; then if [ "$media_directories" == "" ]; then
error "No media directories specified" error "No media directories specified"
fi fi
fi fi
# Override playback_directory if argument set
if [ "$tmp_playback_directory" != "" ]; then
playback_directory="$tmp_playback_directory"
fi
# Read media directories # Read media directories
IFS="," read -a media_directories <<< "$media_directories" IFS="," read -a media_directories <<< "$media_directories"
@ -229,11 +234,11 @@ for media_directory in "${media_directories[@]}"; do
# Check validity of variables # Check validity of variables
if [ "$connection_string" == "" ] || [ "$directory" == "" ]; then if [ "$connection_string" == "" ] || [ "$directory" == "" ]; then
error "Not a valid media directory: $media_directory" error "Not a valid media directory: $media_directory"
fi fi
echo -ne "Fetching results from $text_bold$(basename $directory)$text_reset on $text_bold$connection_string$text_reset\n" 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 # Add leading and trailing slash to directory if missing
[[ "$directory" != /*/ ]] && directory="/$directory/" [[ "$directory" != /*/ ]] && directory="/$directory/"
@ -244,8 +249,8 @@ for media_directory in "${media_directories[@]}"; do
# Build SFTP strings and printable strings # Build SFTP strings and printable strings
for i in "${!tmp_results[@]}"; do for i in "${!tmp_results[@]}"; do
tmp_sftp_results["$i"]="sftp://$connection_string${tmp_results[$i]}" tmp_sftp_results["$i"]="sftp://$connection_string${tmp_results[$i]}"
tmp_print_result="$text_bold$connection_string$text_reset ${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_results["$i"]="${tmp_print_result/$directory/}"
done done
sftp_results=("${sftp_results[@]}" "${tmp_sftp_results[@]}") sftp_results=("${sftp_results[@]}" "${tmp_sftp_results[@]}")
print_results=("${print_results[@]}" "${tmp_print_results[@]}") print_results=("${print_results[@]}" "${tmp_print_results[@]}")
@ -256,10 +261,10 @@ echo
# Exit if no results found # Exit if no results found
if [ "${#sftp_results[@]}" == 0 ]; then if [ "${#sftp_results[@]}" == 0 ]; then
if [ "$is_remote_call" == true ]; then if [ "$is_remote_call" == true ]; then
echo "No files found, press something to quit" echo "No files found, press something to quit"
read -n 1 read -n 1
else else
echo "No files found" echo "No files found"
fi fi
exit exit
fi fi
@ -285,10 +290,11 @@ done
# Play results if --list flag not set # Play results if --list flag not set
if [ "$list" == false ]; then if [ "$list" == false ]; then
# Print controls
if [ "$is_remote_call" == true ]; then if [ "$is_remote_call" == true ]; then
print_remote_controls print_remote_controls
else else
print_controls print_controls
fi fi
# Construct addtitional mpv arguments # Construct addtitional mpv arguments
@ -301,8 +307,39 @@ if [ "$list" == false ]; then
fi fi
if [ "$neat" == true ]; then if [ "$neat" == true ]; then
mpv_arguments+=("--no-resume-playback") 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 fi
# Play all remote files # 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 fi

View file

@ -12,12 +12,14 @@
# bob@another-machine/media/movies,bob@10.0.0.1/home/bob/series # bob@another-machine/media/movies,bob@10.0.0.1/home/bob/series
media_directories="" media_directories=""
# Resume directory in which playback positions are stored on exit. # Directory with which the saved playback positions are synchronized on exit of the following form:
# Can also be located on a remote machine which is accessible via SSH.
# #
# Default: ~/.cache/strm # <SSH connection string><absolute_path_to_media_directory>
#
# If it is not set, the mpv resume playback functionality is not enabled by default.
# #
# Examples: # Examples:
# ~/.strm # localhost/home/bob/.strm
# remote-machine:/path/on/remote # remote-machine/home/bob/strm
resume_directory="" # bob@another-machine/home/bob/strm
playback_directory=""