From 13f40e97209dcfe9bfd3a2a0a5ab54939dc534f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=84min=20Baumeler?= Date: Sat, 11 Oct 2025 22:55:32 +0200 Subject: [PATCH] feat: playlist store --- README.md | 15 ++++++- src/awk/recordings.awk | 2 +- src/main.sh | 97 ++++++++++++++++++++++++++++++++++++++---- src/sh/keys.sh | 34 +++++++++++++-- src/sh/playback.sh | 77 +++++++-------------------------- src/sh/playlist.sh | 96 ++++++++++++++++++++++++++++++++++++++++- src/sh/theme.sh | 5 ++- 7 files changed, 248 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index d1778f0..4d6df9d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Control playback from your terminal while browsing the vast world of music relea - Search the MusicBrainz database and discover music artists and releases - Handle metadata without touching your audio files - Fast and hackable application with [vim](https://www.vim.org)-like keybindings +- Save and load playlists 🎞️ Screenshot ------------- @@ -86,6 +87,8 @@ GENERAL OPTIONS: --artist List release groups of given artist --releasegroup List releases in given release group --release Show release given by + --playlists List stored playlists and exit + --load-playlist Load specified playlist MANAGE LOCAL MUSIC: --decorate Decorate directory containing a tagged release @@ -157,10 +160,20 @@ In the insert mode, triggered using `i`, `a`, or `/`, the keys directly modify t Some central keys are `` to start playback of the selected entry, `` to pause playback, and `ctrl-p` to open the currently loaded playlist. +📝 Playlist Store +----------------- +Playlists can be saved, loaded and _shared._ +To save a playlist, simply press `ctrl-s` when viewing your playlist. +To load a playlist, open the playlist store using `ctrl-o` while viewing your playlist. +Alternatively, you may start `fuzic` using the command-line parameters `fuzic --load-playlist `. +Under the hood, a playlist is not more than a sequence of MusicBrainz IDs. +This means that `fuzic` users may share their playlists irrespective of the audio file locations and formats. +Possibly even more importantly, this means that you can preserve your playlist even if you reorganize your local music library: +You can safely share your playlists with your future self. + 🧭 Planned Features ------------------- The following features are planned: -- saving and loading playlists - cover art and artist images - lyrics support - showing current position in track diff --git a/src/awk/recordings.awk b/src/awk/recordings.awk index 70ce29c..215bf94 100644 --- a/src/awk/recordings.awk +++ b/src/awk/recordings.awk @@ -66,7 +66,7 @@ BEGIN { ds = dur % 60 if (ds <= 9) ds = "0"ds - if (dh && dm <=9) + if (dh && dm <= 9) dm = "0"dm dur = dh ? dh":"dm":"ds : dm":"ds } else { diff --git a/src/main.sh b/src/main.sh index 42db494..4b2f068 100755 --- a/src/main.sh +++ b/src/main.sh @@ -36,6 +36,8 @@ VIEW_LIST_ARTISTS="list-artists" VIEW_LIST_ALBUMS="list-albums" VIEW_SELECT_ARTIST="select-artist" VIEW_PLAYLIST="playlist" +VIEW_PLAYLIST_PLAYLISTSTORE="playlist-list" +VIEW_PLAYLIST_STORE="playlist-store" VIEW_QUIT="quit" MODE_NORMAL="hidden" MODE_INSERT="show" @@ -50,21 +52,24 @@ MODE_INSERT="show" # Load configuration . "sh/config.sh" +# Load mpv methods +. "sh/mpv.sh" + # Load query methods . "sh/query.sh" -# Load playback helper -. "sh/playback.sh" +# Load local file handling +. "sh/local.sh" # Load playlist tools . "sh/playlist.sh" +# Load playback helper +. "sh/playback.sh" + # Load MusicBrainz, Discogs, and wiki methods . "sh/api.sh" -# Load mpv methods -. "sh/mpv.sh" - # Load preview methods . "sh/preview.sh" @@ -74,9 +79,6 @@ MODE_INSERT="show" # Load MusicBrainz wrappers . "sh/mb.sh" -# Load local file handling -. "sh/local.sh" - # Load list-generating methods . "sh/lists.sh" @@ -419,6 +421,11 @@ case "${1:-}" in info "Done" exit 0 ;; +"--playlists") + # List available playlists + stored_playlists + exit 0 + ;; "--help") # Print help string cat < List release groups of given artist --releasegroup List releases in given release group --release Show release given by + --playlists List stored playlists and exit + --load-playlist Load specified playlist MANAGE LOCAL MUSIC: --decorate Decorate directory containing a tagged release @@ -485,12 +494,22 @@ case "${1:-}" in MODE="$MODE_NORMAL" MBID="" ;; +"--load-playlist") + # We will load and play later + VIEW="$VIEW_PLAYLIST" + MODE="$MODE_NORMAL" + MBID="" + ;; *) err "Unknown option $1 (see --help)" exit 1 ;; esac +# For history purpose: previous view is always: +LASTVIEW="$VIEW_LIST_ARTISTS" +LASTARG="" + # Start application: # - set title # - check for missing data from MusicBrainz @@ -521,6 +540,9 @@ export LOCKFILE RESULTS PIDFILE # Start mpv mpv_start +# Playback possible now +[ "${1:-}" = "--load-playlist" ] && $0 --playlist "$PLAYLIST_CMD_LOAD" "${2:-}" + # main loop # states are stored in (in)visible labels # @@ -594,6 +616,63 @@ while true; do LASTVIEW="$VIEW_SELECT_ARTIST" LASTARG="$ARGS" ;; + "$VIEW_PLAYLIST_STORE") + VIEW="$VIEW_PLAYLIST" + ARGS="" + MBID="" + tmpf=$(mktemp) + list_playlist | cut -d "$(printf '\t')" -f "3,4" >"$tmpf" + # Make sure we store only nonempty playlists + [ -s "$tmpf" ] || continue + while true; do + infonn "Enter playlist name:" + read -r playlistname + [ "$playlistname" ] || continue + case "$playlistname" in + *[!a-zA-Z0-9._-]*) + info "Please use only alaphnumeric symbols and any of \".-_\" for the playlist name." + ;; + *) + f="$PLAYLIST_DIRECTORY/$playlistname" + if [ -s "$f" ]; then + while true; do + infonn "Playlist with name \"$playlistname\" already exists. Do you want to overwrite it? (yes/no)" + read -r yn + case $yn in + "yes" | "no") break ;; + *) info "Please answer \"yes\" or \"no\"." ;; + esac + done + [ "$yn" = "yes" ] || continue + fi + break + ;; + esac + done + mv "$tmpf" "$f" + ;; + "$VIEW_PLAYLIST_PLAYLISTSTORE") + sel=$( + stored_playlists | $FZF \ + --border=double \ + --border-label="$TITLE_PLYLST_LIST" \ + --margin="2%,10%" \ + --bind="$KEYS_I_NORMAL:" \ + --bind="$KEYS_DOWN:down" \ + --bind="$KEYS_UP:up" \ + --bind="$KEYS_HALFPAGE_DOWN:half-page-down" \ + --bind="$KEYS_HALFPAGE_UP:half-page-up" \ + --bind="$KEYS_OUT,$KEYS_QUIT:accept" \ + --bind="$KEYS_KEYBINDINGS:preview:$0 --show-keybindings $VIEW_PLAYLIST_PLAYLISTSTORE" \ + --bind="$KEYS_SCROLL_PREVIEW_DOWN:preview-down" \ + --bind="$KEYS_SCROLL_PREVIEW_UP:preview-up" \ + --bind="$KEYS_PREVIEW_CLOSE:hide-preview" \ + --bind="$KEYS_PLAYLISTSTORE_SELECT:transform:[ {1} ] && $0 --playlist $PLAYLIST_CMD_LOAD {1} && echo accept" \ + --bind="$KEYS_PLAYLISTSTORE_DELETE:transform:[ {1} ] && rm \"$PLAYLIST_DIRECTORY/{r1}\" && echo \"reload($0 --playlists\)\"" \ + --wrap-sign="" || true + ) + VIEW="$VIEW_PLAYLIST" + ;; "$VIEW_PLAYLIST") sel=$( list_playlist | $FZF \ @@ -637,6 +716,8 @@ while true; do --bind="$KEYS_PLAYLIST_SHUFFLE:execute-silent($0 --playlist $PLAYLIST_CMD_SHUFFLE)+$FZF_RELOAD_PLAYLIST" \ --bind="$KEYS_PLAYLIST_UNSHUFFLE:execute-silent($0 --playlist $PLAYLIST_CMD_UNSHUFFLE)+$FZF_RELOAD_PLAYLIST" \ --bind="$KEYS_PLAYLIST_GOTO_RELEASE:print($VIEW_RELEASE)+accept" \ + --bind="$KEYS_PLAYLIST_STORE:print($VIEW_PLAYLIST_STORE)+print("")+print($LASTVIEW)+print($LASTARG)+accept" \ + --bind="$KEYS_PLAYLIST_OPEN_STORE:print($VIEW_PLAYLIST_PLAYLISTSTORE)+print("")+print($LASTVIEW)+print($LASTARG)+accept" \ --preview-window="hidden" \ --wrap-sign="" \ --delimiter="\t" \ diff --git a/src/sh/keys.sh b/src/sh/keys.sh index d97cced..830012d 100644 --- a/src/sh/keys.sh +++ b/src/sh/keys.sh @@ -87,7 +87,10 @@ # - KEYS_PLAYLIST_GOTO_RELEASE: Jump to release or selected entry # - KEYS_PLAYLIST_STORE: Store current playlist as file # - KEYS_PLAYLIST_LOAD: Load playlist from file -# - KEYS_PLAYLIST_QUIT: Quit playlist view +# +# Playlist store (here, we don't have a `normal` mode): +# - KEYS_PLAYLISTSTORE_SELECT: Use selected playlist +# - KEYS_PLAYLISTSTORE_DELETE: Delete stored playlist if [ ! "${KEYS_LOADED:-}" ]; then # Mode selection: @@ -199,11 +202,17 @@ if [ ! "${KEYS_LOADED:-}" ]; then KEYS_PLAYLIST_UNSHUFFLE="${KEYS_PLAYLIST_UNSHUFFLE:-"S"}" KEYS_PLAYLIST_GOTO_RELEASE="${KEYS_PLAYLIST_GOTO_RELEASE:-"ctrl-g"}" KEYS_PLAYLIST_STORE="${KEYS_PLAYLIST_STORE:-"ctrl-s"}" - KEYS_PLAYLIST_LOAD="${KEYS_PLAYLIST_LOAD:-"ctrl-o"}" + KEYS_PLAYLIST_OPEN_STORE="${KEYS_PLAYLIST_LOAD:-"ctrl-o"}" + KEYS_PLAYLIST_DELETE="${KEYS_PLAYLIST_DELETE:-"del"}" export KEYS_PLAYLIST_RELOAD KEYS_PLAYLIST_REMOVE KEYS_PLAYLIST_UP \ KEYS_PLAYLIST_DOWN KEYS_PLAYLIST_CLEAR KEYS_PLAYLIST_CLEAR_ABOVE \ KEYS_PLAYLIST_CLEAR_BELOW KEYS_PLAYLIST_SHUFFLE KEYS_PLAYLIST_UNSHUFFLE \ - KEYS_PLAYLIST_GOTO_RELEASE KEYS_PLAYLIST_STORE KEYS_PLAYLIST_LOAD + KEYS_PLAYLIST_GOTO_RELEASE KEYS_PLAYLIST_STORE KEYS_PLAYLIST_OPEN_STORE + + # Playlist store (here, we don't have a `normal` mode): + KEYS_PLAYLISTSTORE_SELECT="${KEYS_PLAYLISTSTORE_SELECT:-"enter"}" + KEYS_PLAYLISTSTORE_DELETE="${KEYS_PLAYLISTSTORE_DELETE:-"del"}" + export KEYS_PLAYLISTSTORE_SELECT KEYS_PLAYLISTSTORE_DELETE export KEYS_LOADED=1 fi @@ -297,7 +306,8 @@ print_keybindings() { "$KEYS_PLAYLIST_CLEAR_ABOVE" "Remove all tracks above" \ "$KEYS_PLAYLIST_CLEAR_BELOW" "Remove all tracks below" \ "$KEYS_PLAYLIST_SHUFFLE" "Shuffle" \ - "$KEYS_PLAYLIST_UNSHUFFLE" "Undo shuffle" + "$KEYS_PLAYLIST_UNSHUFFLE" "Undo shuffle" \ + "$KEYS_PLAYLIST_OPEN_STORE" "Manage stored playlists" __keybindinggroup_from_args "Playback" \ "$KEYS_PLAY,$KEYS_N_PLAY" "Play selected item" \ "$KEYS_QUEUE,$KEYS_N_QUEUE" "Queue selected item" \ @@ -313,6 +323,22 @@ print_keybindings() { "$KEYS_N_YANK" "Copy MusicBrainz track ID" \ "$KEYS_YANK_CURRENT" "Copy MusicBrainz release ID" ;; + "$VIEW_PLAYLIST_PLAYLISTSTORE") + __keybindinggroup_from_args "Previews" \ + "$KEYS_SCROLL_PREVIEW_DOWN" "Scroll preview down" \ + "$KEYS_SCROLL_PREVIEW_UP" "Scroll preview up" \ + "$KEYS_KEYBINDINGS" "Show these keybindings" \ + "$KEYS_PREVIEW_CLOSE" "Close preview window" + __keybindinggroup_from_args "Navigation" \ + "$KEYS_DOWN" "Down" \ + "$KEYS_UP" "Up" \ + "$KEYS_HALFPAGE_DOWN" "Down half a page" \ + "$KEYS_HALFPAGE_UP" "Up half a page" \ + "$KEYS_OUT,$KEYS_QUIT" "Leave playlist store" + __keybindinggroup_from_args "Playlist store" \ + "$KEYS_PLAYLISTSTORE_SELECT" "Load playlist" \ + "$KEYS_PLAYLISTSTORE_DELETE" "Delete playlist" + ;; *) __keybindinggroup_from_args "Switch between modes" \ "$KEYS_I_NORMAL" "Swtich to normal mode (insert)" \ diff --git a/src/sh/playback.sh b/src/sh/playback.sh index af397d3..25c8571 100644 --- a/src/sh/playback.sh +++ b/src/sh/playback.sh @@ -43,51 +43,6 @@ __playback_cmd_from_key() { case ",$KEYS_N_SEEK_BACKWARD," in *",$key,"*) echo "$PLAYBACK_CMD_SEEK_BACKWARD" && return ;; esac } -# Generate playlist from MB release ID and path to decoration -# -# @argument $1: MusicBrainz release ID -# @argument $2: Path to decoration file -# @argument $3: MusicBrainz track ID to select (optional) -__generate_playlist() { - printf "#EXTM3U\n" - dir="$(dirname "$2")" - mb_release "$1" | - $JQ \ - --slurpfile decofile "$2" \ - --arg base "$dir" \ - --arg deco "$2" \ - --arg tid "${3:-}" \ - '$decofile[].tracks as $filenames | - . | - .id as $rid | - .media | - length as $l | - .[] | - .position as $pos | - .tracks | - if ($tid == "") then . else map(select(.id == $tid)) end | - map({ - t: [ - $rid, - .id, - $l, - $pos, - .number, - .length, - .title, - (."artist-credit" | map([.name, .joinphrase] | join("")) | join("")), - $deco - ] | join("\t"), - length: (.length // 0 / 1000 | round | tostring), - $pos, - number: .number, - file: $filenames[.id] - }) | - map(if(.number | type == "string" and test("^[0-9]+$")) then .number |= tonumber else . end) | - sort_by([.pos, .number]) | - map("#EXTINF:" + .length + "," + .t + "\n" + $base + "/" + .file)[]' -} - # Main playback method # # @argument $1: view @@ -122,10 +77,10 @@ playback() { rpath="$(echo "$line" | cut -d "$(printf '\t')" -f 5)" [ "$rpath" ] || continue if [ ! "$queue" ]; then - __generate_playlist "$rmbid" "$rpath" | mpv_play_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_play_list >/dev/null queue=1 else - __generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null fi done queue=1 @@ -138,15 +93,15 @@ playback() { rpath="$(echo "$line" | cut -d "$(printf '\t')" -f 5)" [ "$rpath" ] || continue if [ ! "${queue:-}" ]; then - __generate_playlist "$rmbid" "$rpath" | mpv_play_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_play_list >/dev/null queue=1 else - __generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null fi done ;; - "$VIEW_RELEASEGROUP") __generate_playlist "$mbid" "$path" | mpv_play_list >/dev/null ;; - "$VIEW_RELEASE") __generate_playlist "$mbid_current" "$path" "$mbid" | mpv_play_list >/dev/null ;; + "$VIEW_RELEASEGROUP") generate_playlist "$mbid" "$path" | mpv_play_list >/dev/null ;; + "$VIEW_RELEASE") generate_playlist "$mbid_current" "$path" "$mbid" | mpv_play_list >/dev/null ;; "$VIEW_PLAYLIST") mpv_play_index $((FZF_POS - 1)) >/dev/null ;; esac ;; @@ -164,7 +119,7 @@ playback() { rmbid="$(echo "$line" | cut -d "$(printf '\t')" -f 4)" rpath="$(echo "$line" | cut -d "$(printf '\t')" -f 5)" [ "$rpath" ] || continue - __generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null done done ;; @@ -174,12 +129,12 @@ playback() { rmbid="$(echo "$line" | cut -d "$(printf '\t')" -f 4)" rpath="$(echo "$line" | cut -d "$(printf '\t')" -f 5)" [ "$rpath" ] || continue - __generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_queue_list >/dev/null done ;; - "$VIEW_RELEASEGROUP") __generate_playlist "$mbid" "$path" | mpv_queue_list >/dev/null ;; - "$VIEW_RELEASE") __generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_list >/dev/null ;; - "$VIEW_PLAYLIST") __generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_list >/dev/null ;; + "$VIEW_RELEASEGROUP") generate_playlist "$mbid" "$path" | mpv_queue_list >/dev/null ;; + "$VIEW_RELEASE") generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_list >/dev/null ;; + "$VIEW_PLAYLIST") generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_list >/dev/null ;; esac ;; "$PLAYBACK_CMD_QUEUE_NEXT") @@ -196,7 +151,7 @@ playback() { rmbid="$(echo "$line" | cut -d "$(printf '\t')" -f 4)" rpath="$(echo "$line" | cut -d "$(printf '\t')" -f 5)" [ "$rpath" ] || continue - __generate_playlist "$rmbid" "$rpath" | mpv_queue_next_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_queue_next_list >/dev/null done done ;; @@ -206,12 +161,12 @@ playback() { rmbid="$(echo "$line" | cut -d "$(printf '\t')" -f 4)" rpath="$(echo "$line" | cut -d "$(printf '\t')" -f 5)" [ "$rpath" ] || continue - __generate_playlist "$rmbid" "$rpath" | mpv_queue_next_list >/dev/null + generate_playlist "$rmbid" "$rpath" | mpv_queue_next_list >/dev/null done ;; - "$VIEW_RELEASEGROUP") __generate_playlist "$mbid" "$path" | mpv_queue_next_list >/dev/null ;; - "$VIEW_RELEASE") __generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_next_list >/dev/null ;; - "$VIEW_PLAYLIST") __generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_next_list >/dev/null ;; + "$VIEW_RELEASEGROUP") generate_playlist "$mbid" "$path" | mpv_queue_next_list >/dev/null ;; + "$VIEW_RELEASE") generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_next_list >/dev/null ;; + "$VIEW_PLAYLIST") generate_playlist "$mbid_current" "$path" "$mbid" | mpv_queue_next_list >/dev/null ;; esac ;; "$PLAYBACK_CMD_TOGGLE_PLAYBACK") mpv_toggle_pause ;; diff --git a/src/sh/playlist.sh b/src/sh/playlist.sh index 7315f7d..e291914 100644 --- a/src/sh/playlist.sh +++ b/src/sh/playlist.sh @@ -3,6 +3,7 @@ # This files provides an interface to manipulate the playlist. The available # commands are defined in the following variables. if [ ! "${PLAYLIST_LOADED:-}" ]; then + # Playlist commands PLAYLIST_CMD_REMOVE="rm" PLAYLIST_CMD_UP="up" PLAYLIST_CMD_DOWN="down" @@ -11,13 +12,101 @@ if [ ! "${PLAYLIST_LOADED:-}" ]; then PLAYLIST_CMD_CLEAR_BELOW="clear-below" PLAYLIST_CMD_SHUFFLE="shuffle" PLAYLIST_CMD_UNSHUFFLE="unshuffle" + PLAYLIST_CMD_LOAD="load" export PLAYLIST_CMD_REMOVE PLAYLIST_CMD_UP PLAYLIST_CMD_DOWN \ PLAYLIST_CMD_CLEAR PLAYLIST_CMD_CLEAR_ABOVE PLAYLIST_CMD_CLEAR_BELOW \ - PLAYLIST_CMD_SHUFFLE PLAYLIST_CMD_UNSHUFFLE + PLAYLIST_CMD_SHUFFLE PLAYLIST_CMD_UNSHUFFLE PLAYLIST_CMD_LOAD + + # Storage and loading of playlists + PLAYLIST_DIRECTORY="$LOCALDATADIR/playlists" + [ -d "$PLAYLIST_DIRECTORY" ] || mkdir -p "$PLAYLIST_DIRECTORY" + export PLAYLIST_DIRECTORY export PLAYLIST_LOADED=1 fi +# List stored playlists +# +# This prints the names of the stored playlists. +stored_playlists() { + find "$PLAYLIST_DIRECTORY" -mindepth 1 -maxdepth 1 -type f -printf '%f\n' | + sort +} + +# Generate playlist from MB release ID and path to decoration +# +# @argument $1: MusicBrainz release ID +# @argument $2: Path to decoration file +# @argument $3: MusicBrainz track ID to select (optional) +generate_playlist() { + printf "#EXTM3U\n" + dir="$(dirname "$2")" + mb_release "$1" | + $JQ \ + --slurpfile decofile "$2" \ + --arg base "$dir" \ + --arg deco "$2" \ + --arg tid "${3:-}" \ + '$decofile[].tracks as $filenames | + . | + .id as $rid | + .media | + length as $l | + .[] | + .position as $pos | + .tracks | + if ($tid == "") then . else map(select(.id == $tid)) end | + map({ + t: [ + $rid, + .id, + $l, + $pos, + .number, + .length, + .title, + (."artist-credit" | map([.name, .joinphrase] | join("")) | join("")), + $deco + ] | join("\t"), + length: (.length // 0 / 1000 | round | tostring), + $pos, + number: .number, + file: $filenames[.id] + }) | + map(if(.number | type == "string" and test("^[0-9]+$")) then .number |= tonumber else . end) | + sort_by([.pos, .number]) | + map("#EXTINF:" + .length + "," + .t + "\n" + $base + "/" + .file)[]' +} + +# Generate playlist content from stored playlist +# +# @argument $1: Playlist file +generate_playlist_stored() { + f="${1:-}" + [ -s "$f" ] || return + tab="$(printf '\t')" + PLAYLISTSTART="#EXTM3U" + printf "%s\n" "$PLAYLISTSTART" + awk -F '\t' \ + -v rfile="$LOCALDATA_RELEASES" \ + 'BEGIN { + OFS="\t" + while ((getline < rfile) == 1) + release[$1] = $2 + close(rfile) + } + { + if (release[$1]) + print $1, release[$1], $2 + }' <"$f" | + while IFS= read -r line; do + rid=$(echo "$line" | cut -d "$tab" -f 1) + path=$(echo "$line" | cut -d "$tab" -f 2) + tid=$(echo "$line" | cut -d "$tab" -f 3) + generate_playlist "$rid" "$path" "$tid" + done | grep -v "^$PLAYLISTSTART$" +} + # Run playback commands # # @argument $1: playlist command @@ -43,5 +132,10 @@ playlist() { ;; "$PLAYLIST_CMD_SHUFFLE") mpv_playlist_shuffle ;; "$PLAYLIST_CMD_UNSHUFFLE") mpv_playlist_unshuffle ;; + "$PLAYLIST_CMD_LOAD") + f="$PLAYLIST_DIRECTORY/${2:-}" + [ -s "$f" ] || return + generate_playlist_stored "$f" | mpv_play_list >/dev/null + ;; esac } diff --git a/src/sh/theme.sh b/src/sh/theme.sh index 0a889b4..92bf535 100644 --- a/src/sh/theme.sh +++ b/src/sh/theme.sh @@ -192,7 +192,7 @@ if [ ! "${THEME_LOADED:-}" ]; then # <> string Track duration PLYLST_FMT="${PLYLST_FMT:-"<>\t<>\t<<artist>>\t<<duration>>"}" PLYLST_FMT_CNT=$(($(printf "$PLYLST_FMT" | tr -cd '\t' | wc -c) + 1)) - PLYLST_FMT_RIGHTALIGN="${PLYLST_FMT_RIGHTALIGN:-"1,4"}" + PLYLST_FMT_RIGHTALIGN="${PLYLST_FMT_RIGHTALIGN:-"1"}" PLYLST_FMT_PLAYING_YES="${PLYLST_FMT_PLAYING_YES:-"👉"}" PLYLST_FMT_PLAYING_NO="${PLYLST_FMT_PLAYING_NO:-""}" PLYLST_FMT_TITLE="${PLYLST_FMT_TITLE:-"${CTITLE}%s${OFF}"}" @@ -390,7 +390,8 @@ if [ ! "${THEME_LOADED:-}" ]; then # Playlist title # ============== TITLE_PLYLST="${TITLE_PLYLST:-" 🎶 ${CARTIST}Playlist${OFF} "}" - export TITLE_PLYLST + TITLE_PLYLST_LIST="${TITLE_PLYLST_LIST:-" 🎶 ${CARTIST}Stored Playlists${OFF} "}" + export TITLE_PLYLST TITLE_PLYLST_LIST export THEME_LOADED=1 fi