#!/bin/sh set -eu # Load helper methods . "sh/info.sh" # Load helper methods . "sh/helper.sh" # Load configuration . "sh/config.sh" # Load theme . "sh/theme.sh" # Load keys . "sh/keys.sh" # Load AWK scripts . "sh/awk.sh" # Load tools . "sh/tools.sh" # Load MusicBrainz and Discogs methods . "sh/api.sh" # Load mpv methods . "sh/mpv.sh" # Load preview methods . "sh/preview.sh" # Load cache functionality . "sh/cache.sh" # Load MusicBrainz wrappers . "sh/mb.sh" # Load local file handling . "sh/local.sh" # Load list-generating methods . "sh/lists.sh" if [ "${1:-}" = "--fzf-reload" ]; then mode=$(cut -d "#" -f 1 "$MODEFILE") args=$(cut -d "#" -f 2- "$MODEFILE") case "$mode" in "$MODE_ARTIST") list_releasegroups "$args" ;; "$MODE_RELEASEGROUP") list_releases "$args" ;; "$MODE_RELEASE") list_recordings "$args" ;; "$MODE_LIST_ARTISTS") list_local_artists ;; "$MODE_LIST_ALBUMS") list_local_releasegroups ;; esac exit 0 fi if [ "${1:-}" = "--fzf-load" ]; then mode=$(cut -d "#" -f 1 "$MODEFILE") args=$(cut -d "#" -f 2- "$MODEFILE") case "$mode" in "$MODE_ARTIST") secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" QUERY="$(printf "!%s " "$secsymb")" name="$(mb_artist "$args" | $JQ -r '.name')" PROMPT=$(printf "$ARTIST_PROMPT" "$name") ;; "$MODE_RELEASEGROUP") title="$(mb_releasegroup "$args" | $JQ -r '.title')" artist="$(mb_releasegroup "$args" | $JQ -r '."artist-credit" | map(([.name, .joinphrase]|join(""))) | join("")')" QUERY="$(printf "%s " "$FORMAT_STATUS_OFFICIAL")" PROMPT=$(printf "$FULL_PROMPT" "$artist" "$title") ;; "$MODE_RELEASE") title="$(mb_release "$args" | $JQ -r '.title')" artist="$(mb_release "$args" | $JQ -r '."artist-credit" | map(([.name, .joinphrase]|join(""))) | join("")')" PROMPT=$(printf "$FULL_PROMPT" "$artist" "$title") ;; "$MODE_SEARCH_ARTIST") ENABLE_CHANGE=1 DISABLE_SEARCH=1 SHOW_PREVIEW=1 ;; "$MODE_SEARCH_ALBUM") ENABLE_CHANGE=1 DISABLE_SEARCH=1 ;; "$MODE_LIST_ARTISTS") SHOW_PREVIEW=1 ;; "$MODE_LIST_ALBUMS") ;; esac printf "+change-query(%s)" "${QUERY:-}" printf "+change-prompt(%s)" "${PROMPT:-"$SEARCH_PROMPT"}" [ "${DISABLE_SEARCH:-}" ] && printf "+disable-search" || printf "+enable-search" [ "${SHOW_PREVIEW:-}" ] && printf "+show-preview" || printf "+hide-preview" [ "${ENABLE_CHANGE:-}" ] && printf "+rebind(change)" || printf "+unbind(change)" exit 0 fi if [ "${1:-}" = "--fzf-info" ]; then mode=$(cut -d "#" -f 1 "$MODEFILE") args=$(cut -d "#" -f 2- "$MODEFILE") case "$mode" in "$MODE_SEARCH_ARTIST") echo "Search music artist on MusicBrainz" ;; "$MODE_SEARCH_ALBUM") echo "Search album on MusicBrainz" ;; "$MODE_LIST_ARTISTS") echo "Search artists" ;; "$MODE_LIST_ALBUMS") echo "Search albums" ;; "$MODE_SELECT_ARTIST") echo "Select artist (WE SHOULDNT SEE THIS; THIS IS A BUG!)" ;; *) if [ "$FZF_KEY" ]; then printf "[last key: %s] %s/%s" "$FZF_KEY" "$FZF_MATCH_COUNT" "$FZF_TOTAL_COUNT" else printf "%s/%s" "$FZF_MATCH_COUNT" "$FZF_TOTAL_COUNT" fi ;; esac exit 0 fi if [ "${1:-}" = "--fzf-change-reload" ]; then # Wait for async. process to terminate sleep 1 while [ -f "$LOCKFILE" ]; do sleep 1 done # Show results column -t -s "$(printf '\t')" "$RESULTS" | sed 's| \+\([0-9a-f-]\+\) \+\([0-9a-f-]\+\)$|\t\1\t\2|' exit 0 fi if [ "${1:-}" = "--fzf-change" ]; then # Kill any running search if [ -f "$PIDFILE" ]; then pid=$(cat "$PIDFILE") rm -f "$PIDFILE" kill -9 "$pid" >/dev/null 2>&1 || true fi # Stop, if no search string is given [ "$FZF_QUERY" ] || exit 0 # Store PID of current process echo "$$" >"$PIDFILE" touch "$LOCKFILE" sleep 1 mode=$(cut -d "#" -f 1 "$MODEFILE") if [ "$mode" = "$MODE_SEARCH_ARTIST" ]; then api_mb_search_artist "$FZF_QUERY" | $JQ -r '.artists[] | [ .id, .type, .name, .disambiguation, .["life-span"].begin, .["life-span"].end ] | join("\t")' | awk \ -F "\t" \ -v file_local_artists="${LOCALDATA_ARTISTS:-}" \ -v format_person="$AV_PERSON" \ -v format_group="$AV_GROUP" \ -v format_disambiguation="$AV_DISAMBIGUATION" \ -v format_local="$FORMAT_LOCAL" \ "$AWK_ARTISTS" >"$RESULTS" || true else api_mb_search_releasegroup "$FZF_QUERY" | $JQ -r '."release-groups"[] | [ .id, ."primary-type", (."secondary-types" // []|join(";")), ."first-release-date", .title, (."artist-credit" | map(([.name, .joinphrase]|join(""))) | join("")) ] | join("\t")' | awk \ -F "\t" \ -v file_local_releasegroups="${LOCALDATA_RELEASEGROUPS:-}" \ -v format_release="$RGV_RELEASE" \ -v format_release_w_artist="$RGV_RELEASE_W_ARTIST" \ -v format_year="$RGV_YEAR" \ -v format_album="$FORMAT_TYPE_ALBUM" \ -v format_single="$FORMAT_TYPE_SINGLE" \ -v format_ep="$FORMAT_TYPE_EP" \ -v format_broadcast="$FORMAT_TYPE_BROADCAST" \ -v format_other="$FORMAT_TYPE_OTHER" \ -v format_has_secondary="$FORMAT_TYPE_HAS_SECONDARY" \ -v format_secondary="$FORMAT_TYPE_SECONDARY" \ -v format_compilation="$FORMAT_TYPE_SECONDARY_COMPILATION" \ -v format_soundtrack="$FORMAT_TYPE_SECONDARY_SOUNDTRACK" \ -v format_spokenword="$FORMAT_TYPE_SECONDARY_SPOKENWORD" \ -v format_interview="$FORMAT_TYPE_SECONDARY_INTERVIEW" \ -v format_audiobook="$FORMAT_TYPE_SECONDARY_AUDIOBOOK" \ -v format_audiodrama="$FORMAT_TYPE_SECONDARY_AUDIODRAMA" \ -v format_live="$FORMAT_TYPE_SECONDARY_LIVE" \ -v format_remix="$FORMAT_TYPE_SECONDARY_REMIX" \ -v format_djmix="$FORMAT_TYPE_SECONDARY_DJMIX" \ -v format_mixtape="$FORMAT_TYPE_SECONDARY_MIXTAPE" \ -v format_demo="$FORMAT_TYPE_SECONDARY_DEMO" \ -v format_fieldrec="$FORMAT_TYPE_SECONDARY_FIELDREC" \ -v format_local="$FORMAT_LOCAL" \ "$AWK_RELEASEGROUPS" | cut -d "$(printf '\t')" -f 2- >"$RESULTS" || true fi # Process ends now: Display and quit rm -f "$LOCKFILE" "$PIDFILE" exit 0 fi if [ "${1:-}" = "--fzf-key" ]; then parentmbid="${2:-}" mbid="${3:-}" path="${4:-}" mode=$(cut -d "#" -f 1 "$MODEFILE") args=$(cut -d "#" -f 2- "$MODEFILE") case ",$KEYS_PLAY," in *",$FZF_KEY,"*) [ "$path" ] || exit 0 case "$mode" in "$MODE_RELEASEGROUP") generate_playlist "$mbid" "$path" | mpv_play_list >/dev/null ;; "$MODE_RELEASE") generate_playlist "$parentmbid" "$path" "$mbid" | mpv_play_list >/dev/null ;; esac ;; esac case ",$KEYS_QUEUE," in *",$FZF_KEY,"*) [ "$path" ] || exit 0 case "$mode" in "$MODE_RELEASEGROUP") generate_playlist "$mbid" "$path" | mpv_queue_list >/dev/null ;; "$MODE_RELEASE") generate_playlist "$parentmbid" "$path" "$mbid" | mpv_queue_list >/dev/null ;; esac ;; esac case ",$KEYS_TOGGLE_PLAY_PAUSE," in *",$FZF_KEY,"*) mpv_toggle_pause >/dev/null ;; esac case ",$KEYS_PLAY_NEXT," in *",$FZF_KEY,"*) mpv_next >/dev/null ;; esac case ",$KEYS_PLAY_PREV," in *",$FZF_KEY,"*) mpv_prev >/dev/null ;; esac case ",$KEYS_SEEK_FORWARD," in *",$FZF_KEY,"*) mpv_seek_forward >/dev/null ;; esac case ",$KEYS_SEEK_BACKWARD," in *",$FZF_KEY,"*) mpv_seek_backward >/dev/null ;; esac case ",$KEYS_HALFPAGE_DOWN," in *",$FZF_KEY,"*) printf "half-page-down" ;; esac case ",$KEYS_HALFPAGE_UP," in *",$FZF_KEY,"*) printf "half-page-up" ;; esac case ",$KEYS_FILTER_LOCAL," in *",$FZF_KEY,"*) case "$mode" in "$MODE_SEARCH_ARTIST" | "$MODE_SEARCH_ALBUM") ;; *) QUERY="$FORMAT_LOCAL " ;; esac ;; esac case ",$KEYS_SWITCH_ARTIST_ALBUM," in *",$FZF_KEY,"*) case "$mode" in "$MODE_SEARCH_ARTIST") MODE_NEXT="$MODE_SEARCH_ALBUM" ;; "$MODE_SEARCH_ALBUM") MODE_NEXT="$MODE_SEARCH_ARTIST" ;; "$MODE_LIST_ARTISTS") MODE_NEXT="$MODE_LIST_ALBUMS" ;; "$MODE_LIST_ALBUMS") MODE_NEXT="$MODE_LIST_ARTISTS" ;; esac ;; esac case ",$KEYS_SWITCH_LOCAL_REMOTE," in *",$FZF_KEY,"*) case "$mode" in "$MODE_SEARCH_ARTIST") MODE_NEXT="$MODE_LIST_ARTISTS" ;; "$MODE_SEARCH_ALBUM") MODE_NEXT="$MODE_LIST_ALBUMS" ;; "$MODE_LIST_ARTISTS") MODE_NEXT="$MODE_SEARCH_ARTIST" ;; "$MODE_LIST_ALBUMS") MODE_NEXT="$MODE_SEARCH_ALBUM" ;; esac ;; esac case ",$KEYS_BROWSE," in *",$FZF_KEY,"*) [ "$mbid" ] || exit 0 case "$mode" in "$MODE_SEARCH_ARTIST" | "$MODE_LIST_ARTISTS") urltype="artist" ;; "$MODE_ARTIST") urltype="release-group" ;; "$MODE_RELEASEGROUP") urltype="release" ;; "$MODE_RELEASE") urltype="track" ;; esac printf "execute-silent(xdg-open \"https://musicbrainz.org/%s/%s\" &)" "$urltype" "$mbid" ;; esac case ",$KEYS_OUT," in *",$FZF_KEY,"*) case "$mode" in "$MODE_ARTIST") MODE_NEXT="$MODE_LIST_ARTISTS" ;; "$MODE_RELEASEGROUP") MODE_NEXT="$MODE_SELECT_ARTIST" MODE_NEXT_ARGS="$(mb_releasegroup "$args" | $JQ -r --compact-output '."artist-credit"')" ACCEPT=1 ;; "$MODE_RELEASE") MODE_NEXT="$MODE_RELEASEGROUP" MODE_NEXT_ARGS="$(mb_release "$args" | $JQ -r --compact-output '."release-group".id')" ;; esac ;; esac case ",$KEYS_IN," in *",$FZF_KEY,"*) [ "$mbid" ] || exit 0 MODE_NEXT_ARGS="$mbid" case "$mode" in "$MODE_LIST_ARTISTS" | "$MODE_SEARCH_ARTIST") MODE_NEXT="$MODE_ARTIST" ;; "$MODE_ARTIST") MODE_NEXT="$MODE_RELEASEGROUP" ;; "$MODE_RELEASEGROUP" | "$MODE_LIST_ALBUMS" | "$MODE_SEARCH_ALBUM") MODE_NEXT="$MODE_RELEASE" ;; esac ;; esac case ",$KEYS_SELECT_ARTIST," in *",$FZF_KEY,"*) [ "$mbid" ] || exit 0 case "$mode" in "$MODE_ARTIST" | "$MODE_SEARCH_ARTIST" | "$MODE_LIST_ARTISTS") MODE_NEXT_ARGS="$(mb_releasegroup "$mbid" | $JQ -r --compact-output '."artist-credit"')" ACCEPT=1 ;; "$MODE_RELEASEGROUP" | "$MODE_SEARCH_ALBUM" | "$MODE_LIST_ALBUMS") MODE_NEXT_ARGS="$(mb_release "$mbid" | $JQ -r --compact-output '."artist-credit"')" ACCEPT=1 ;; "$MODE_RELEASE") MODE_NEXT_ARGS="$(mb_release "$args" | $JQ -r --compact-output '."artist-credit"')" ACCEPT=1 ;; esac [ "$MODE_NEXT_ARGS" ] && MODE_NEXT="$MODE_SELECT_ARTIST" ;; esac case ",$KEYS_FILTER_PRIMARY," in *",$FZF_KEY,"*) [ "$mode" = "$MODE_ARTIST" ] || exit 0 secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" QUERY="!$secsymb " ;; esac case ",$KEYS_FILTER_SECONDARY," in *",$FZF_KEY,"*) [ "$mode" = "$MODE_ARTIST" ] || exit 0 secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" QUERY="$secsymb " ;; esac case ",$KEYS_SHOW_PLAYLIST," in *",$FZF_KEY,"*) MODE_NEXT="$MODE_PLAYLIST" ACCEPT=1 ;; esac [ "${QUERY:-}" ] && printf "+change-query(%s)" "$QUERY" if [ "$MODE_NEXT" ]; then printf "%s#%s" "$MODE_NEXT" "${MODE_NEXT_ARGS:-}" >"$MODEFILE" [ "${ACCEPT:-}" ] && printf "+accept" || printf "+reload:%s" "$0 --fzf-reload" fi exit 0 fi # Support of local music files if [ "${1:-}" = "--decorate" ]; then [ ! "${2:-}" ] && err "You did not specify a directory." && exit 1 [ ! -d "$2" ] && err "Path $2 does not point to a directory." && exit 1 if ! decorate "$2"; then err "Something went wrong. Are you're files tagged correctly?" exit 1 fi exit 0 fi if [ "${1:-}" = "--reload" ]; then [ ! "${2:-}" ] && err "Path to decorated music is missing." && exit 1 [ ! -d "$2" ] && err "Path does not point to a directory." && exit 1 info "Reloading information of local music directory $2" load_local "$2" || err "Failed to load local data" info "Done" exit 0 fi if [ "${1:-}" = "--internal-preview-artist" ]; then __preview_artist "$2" exit 0 fi if [ "${1:-}" = "--help" ]; then cat <] Search artist on MusicBrainz --search-album [] Search album on MusicBrainz --artist List release groups of given artist --releasegroup List releases in given release group --release Show release given by --ni-search-artist [] Non-interactive search on MusicBrainz --ni-search-album [] Non-interactive search on MusicBrainz MANAGE LOCAL MUSIC: --decorate Decorate directory containing a tagged release --reload Populate da$KEYS_SWITCH_ARTIST_ALBUMase with decorated local music from EOF exit 0 fi # Set window title printf '\033]0;%s\007' "$WINDOW_TITLE" # Generate filenames for temporary files tmpdir=$(mktemp -d) LOCKFILE="$tmpdir/lock" RESULTS="$tmpdir/results" PIDFILE="$tmpdir/pid" MODEFILE="$tmpdir/mode" trap 'rm -rf "$tmpdir"' EXIT INT export LOCKFILE RESULTS PIDFILE MODEFILE if [ "${1:-}" = "--ni-search-artist" ]; then $0 --internal-search "artist" "$2" exit 0 fi if [ "${1:-}" = "--ni-search-album" ]; then $0 --internal-search "releasegroup" "$2" exit 0 fi # Modes MODE_ARTIST="artist" MODE_RELEASEGROUP="rg" MODE_RELEASE="release" MODE_SEARCH_ARTIST="search-artist" MODE_SEARCH_ALBUM="search-album" MODE_LIST_ARTISTS="list-artists" MODE_LIST_ALBUMS="list-albums" MODE_SELECT_ARTIST="select-artist" MODE_PLAYLIST="playlist" export MODE_ARTIST MODE_RELEASEGROUP MODE_RELEASE MODE_SEARCH_ARTIST \ MODE_SEARCH_ALBUM MODE_LIST_ARTISTS MODE_LIST_ALBUMS MODE_SELECT_ARTIST \ MODE_PLAYLIST case "${1:-}" in "--artist") [ ! "${2:-}" ] && err "MusicBrainz Artist ID not specified (see --help)" && exit 1 printf "%s#%s" "$MODE_ARTIST" "$2" >"$MODEFILE" ;; "--releasegroup") [ ! "${2:-}" ] && err "MusicBrainz Release-Group ID not specified (see --help)" && exit 1 printf "%s#%s" "$MODE_RELEASEGROUP" "$2" >"$MODEFILE" ;; "--release") [ ! "${2:-}" ] && err "MusicBrainz Release ID not specified (see --help)" && exit 1 printf "%s#%s" "$MODE_RELEASE" "$2" >"$MODEFILE" ;; "--search-artist") printf "%s#%s" "$MODE_SEARCH_ARTIST" "${2:-}" >"$MODEFILE" ;; "--search-album") printf "%s#%s" "$MODE_SEARCH_ALBUM" "${2:-}" >"$MODEFILE" ;; "--artists" | "") printf "%s#" "$MODE_LIST_ARTISTS" >"$MODEFILE" ;; "--albums") printf "%s#" "$MODE_LIST_ALBUMS" >"$MODEFILE" ;; *) err "Unknown option $1 (see --help)" exit 1 ;; esac # Start mpv mpv_start # $KEYS_PLAY main loop while true; do mode=$(cut -d "#" -f 1 "$MODEFILE") args=$(cut -d "#" -f 2- "$MODEFILE") case "$mode" in "$MODE_SELECT_ARTIST") sel=$( echo "$args" | list_artists_from_json | $FZF \ --bind="$KEYS_HALFPAGE_DOWN,$KEYS_HALFPAGE_UP,\ $KEYS_BROWSE,\ $KEYS_FILTER_LOCAL:transform:$0 --fzf-key {2} {3} {4}" \ -1 \ --border="bold" \ --border-label="Select artist" \ --delimiter="\t" \ --prompt="$SEARCH_PROMPT" \ --margin="5%,20%" \ --bind="$KEYS_FILTER_LOCAL:change-query($FORMAT_LOCAL )" \ --accept-nth="{3}" \ --with-nth="{1}" ) printf "%s#%s" "$MODE_ARTIST" "$sel" >"$MODEFILE" ;; "$MODE_PLAYLIST") count=$(mpv_playlist_count) foo "Playlist: count=$count" list_playlist | $FZF \ --reverse \ --no-sort \ --border=double \ --border-label=" Playlist " \ --no-input \ --margin="2%,10%" \ --bind="$KEYS_HALFPAGE_DOWN,$KEYS_HALFPAGE_UP,\ $KEYS_BROWSE,\ $KEYS_OUT,\ $KEYS_SELECT_ARTIST,\ $KEYS_PLAY,$KEYS_QUEUE,\ $KEYS_PLAY,$KEYS_QUEUE,$KEYS_TOGGLE_PLAY_PAUSE,\ $KEYS_PLAY_NEXT,$KEYS_PLAY_PREV,\ $KEYS_SEEK_FORWARD,$KEYS_SEEK_BACKWARD:transform:$0 --fzf-key {2} {3} {4}" \ --delimiter="\t" \ --with-nth="{1}" >/dev/null # --bind="$KEYS_HALFPAGE_DOWN,$KEYS_HALFPAGE_UP,\ #$KEYS_BROWSE,\ #$KEYS_IN,$KEYS_OUT,\ #$KEYS_SELECT_ARTIST,\ #$KEYS_FILTER_LOCAL,$KEYS_FILTER_PRIMARY,$KEYS_FILTER_SECONDARY,\ #$KEYS_SWITCH_ARTIST_ALBUM,$KEYS_SWITCH_LOCAL_REMOTE,\ #$KEYS_PLAY,$KEYS_QUEUE,$KEYS_TOGGLE_PLAY_PAUSE,\ #$KEYS_PLAY_NEXT,$KEYS_PLAY_PREV,\ #$KEYS_SEEK_FORWARD,$KEYS_SEEK_BACKWARD:transform:$0 --fzf-key {2} {3}" \ printf "%s#%s" "$MODE_LIST_ARTISTS" "" >"$MODEFILE" ;; *) # Main instance $FZF \ --reverse \ --bind="start:reload:$0 --fzf-reload" \ --bind="load:transform:$0 --fzf-load" \ --bind="change:execute-silent($0 --fzf-change &)+reload:$0 --fzf-change-reload" \ --bind="$KEYS_HALFPAGE_DOWN,$KEYS_HALFPAGE_UP,\ $KEYS_BROWSE,\ $KEYS_IN,$KEYS_OUT,\ $KEYS_SELECT_ARTIST,\ $KEYS_FILTER_LOCAL,$KEYS_FILTER_PRIMARY,$KEYS_FILTER_SECONDARY,\ $KEYS_SWITCH_ARTIST_ALBUM,$KEYS_SWITCH_LOCAL_REMOTE,\ $KEYS_PLAY,$KEYS_QUEUE,\ $KEYS_SHOW_PLAYLIST:transform:$0 --fzf-key {2} {3} {4}" \ --info="inline-right" \ --info-command="$0 --fzf-info" \ --preview-window="right,25%,border-left,wrap,<30(hidden)" \ --preview="$0 --internal-preview-artist {3}" \ --delimiter="\t" \ --with-nth="{1}" >/dev/null ;; esac done