diff --git a/.gitignore b/.gitignore index c51e209..62caefd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ demo muf +muf.debug.log diff --git a/share/theme/bw.sh b/share/theme/bw.sh new file mode 100644 index 0000000..abc2838 --- /dev/null +++ b/share/theme/bw.sh @@ -0,0 +1,117 @@ +# Simple black-and-white theme + +# Pointers +# ======== +# Sign that indicates the existence of audio files +FORMAT_LOCAL="|>" +# Pointer to the track currently playing (playlist) +FORMAT_CURRENT="->" + +# Input prompts +# ============= +# General search prompt +SEARCH_PROMPT="search> " +# Prompt that takes an artist name as argument +ARTIST_PROMPT="%s > " +# Prompt that takes an artist name and a release name as arguments (in that +# order) +FULL_PROMPT="%s > %s > " + +# Visual representation of current mode +# ===================================== +# Sign to indicate `normal` mode +PROMPT_NORMAL="n " +# Sign to indicate `insert` mode +PROMPT_INSERT="i " + +# Artist view +# =========== +# Artist string for persons +AV_PERSON="<>" +# Artist string for groups +AV_GROUP="<>" +# Artist disambiguation string +AV_DISAMBIGUATION=" (<>)" + +# Release-group view +# ================== +# Default release group string +RGV_RELEASE="<>" +# Release group string if the artist name differs from the current artist +RGV_RELEASE_W_ARTIST="<<title>> by <<artist>>" +# Year of the release group +RGV_YEAR="[<<year>>]" + +# Release-group types +# =================== +# Album +FORMAT_TYPE_ALBUM="[LP]" +# EP +FORMAT_TYPE_EP="[EP]" +# Single +FORMAT_TYPE_SINGLE="[single]" +# Broadcast +FORMAT_TYPE_BROADCAST="[broadcast]" +# Other +FORMAT_TYPE_OTHER="[other]" +# Flag to indicate that the given release group has associated secondary types. +FORMAT_TYPE_HAS_SECONDARY="%s xx" +# Style to represent secondary types (takes one %s placeholder) +FORMAT_TYPE_SECONDARY="(%s)" +# Secondary types +FORMAT_TYPE_SECONDARY_COMPILATION="compilation" +FORMAT_TYPE_SECONDARY_SOUNDTRACK="OST" +FORMAT_TYPE_SECONDARY_SPOKENWORD="spokenword" +FORMAT_TYPE_SECONDARY_INTERVIEW="interview" +FORMAT_TYPE_SECONDARY_AUDIOBOOK="book" +FORMAT_TYPE_SECONDARY_AUDIODRAMA="drama" +FORMAT_TYPE_SECONDARY_LIVE="live" +FORMAT_TYPE_SECONDARY_REMIX="remix" +FORMAT_TYPE_SECONDARY_DJMIX="DJ" +FORMAT_TYPE_SECONDARY_MIXTAPE="mixtape" +FORMAT_TYPE_SECONDARY_DEMO="demo" +FORMAT_TYPE_SECONDARY_FIELDREC="field rec" + +# Artist Preview +# ============== +# Main preview format. Takes two %s placeholder. The first is for the artist +# biography. The second for the life span. +APV_FORMAT="\n\n%s\n\n%s" +# Specification of a date +APV_DATE="%s" +# Specification of a place +APV_PLACE="%s" +# Specification of a date and a place (in that order) +APV_DATEPLACE="%s in %s" +# String to represent when/where a person is born +APV_BORN="Born: %s" +# String to represent when/where a person died +APV_DIED="Died: %s" + +# Release view +# ============ +# Format of a string that represents a release. +RV_FORMAT="<<status>>\t<<tracks>> tracks\t<<media>>\t<<year>>\t<<country>>\t<<label>>" +# Additional string to display the release title and artist name +RV_TITLE_ARTIST="<<artist>> - <<title>>" +# Additional string to display the release title +RV_TITLE="<<title>>" +# Additional string to display the artist +RV_ARTIST="<<artist>>" + +# Release Status +# ============== +FORMAT_STATUS_OFFICIAL="official" +FORMAT_STATUS_PROMO="promo" +FORMAT_STATUS_BOOTLEG="bootleg" +FORMAT_STATUS_PSEUDO="psuedo" +FORMAT_STATUS_WITHDRAWN="withdrawn" +FORMAT_STATUS_EXPUNGED="expunged" +FORMAT_STATUS_CANCELLED="cancelled" + +# Recording view +# ============== +# Format of a track in a release +REC_FORMAT="<<med>>\t<<nr>>\t<<title>>\t<<artist>>\t<<duration>>" +# Format of a track in the playlist +REC_FORMAT_NO_NUMBER="<<title>>\t<<artist>>\t<<duration>>" diff --git a/share/theme/simple.sh b/share/theme/simple.sh new file mode 100644 index 0000000..895350b --- /dev/null +++ b/share/theme/simple.sh @@ -0,0 +1,118 @@ +# Simple theme without emojis + +# Colors (internal only) +ESC=$(printf '\033') +RED="${ESC}[31m" +GREEN="${ESC}[32m" +YELLOW="${ESC}[33m" +BLUE="${ESC}[34m" +PURPLE="${ESC}[35m" +CYAN="${ESC}[36m" +WHITE="${ESC}[37m" +OFF="${ESC}[m" + +# Pointers +# ======== +# Sign that indicates the existence of audio files +FORMAT_LOCAL="${BLUE}|>$OFF" +# Pointer to the track currently playing (playlist) +FORMAT_CURRENT="${WHITE}-->$OFF" + +# Input prompts +# ============= +# General search prompt +SEARCH_PROMPT="search: " +# Prompt that takes an artist name as argument +ARTIST_PROMPT="artist: %s: " +# Prompt that takes an artist name and a release name as arguments (in that +# order) +FULL_PROMPT="artist: %s / %s: " + +# Visual representation of current mode +# ===================================== +# Sign to indicate `normal` mode +PROMPT_NORMAL="n)" +# Sign to indicate `insert` mode +PROMPT_INSERT="i)" + +# Artist view +# =========== +# Artist string for persons +AV_PERSON="(P) ${CYAN}<<name>>$OFF" +# Artist string for groups +AV_GROUP="(G) ${CYAN}<<name>>$OFF" +# Artist disambiguation string +AV_DISAMBIGUATION="// <<disambiguation>>" + +# Release-group view +# ================== +# Default release group string +RGV_RELEASE="${PURPLE}<<title>>$OFF" +# Release group string if the artist name differs from the current artist +RGV_RELEASE_W_ARTIST="${PURPLE}<<title>>$OFF by ${CYAN}<<artist>>$OFF" +# Year of the release group +RGV_YEAR="[<<year>>]" + +# Release-group types +# =================== +# Album +FORMAT_TYPE_ALBUM="${YELLOW}LP$OFF" +# EP +FORMAT_TYPE_EP="${YELLOW}EP$OFF" +# Single +FORMAT_TYPE_SINGLE="${YELLOW}Single$OFF" +# Broadcast +FORMAT_TYPE_BROADCAST="${YELLOW}Broadcast$OFF" +# Other +FORMAT_TYPE_OTHER="" +# Flag to indicate that the given release group has associated secondary types. +FORMAT_TYPE_HAS_SECONDARY="%s ${RED}xx${OFF}" +# Style to represent secondary types (takes one %s placeholder) +FORMAT_TYPE_SECONDARY="${RED}(${OFF}%s${RED})${OFF}" +# Secondary types +FORMAT_TYPE_SECONDARY_COMPILATION="${YELLOW}compilation$OFF" +FORMAT_TYPE_SECONDARY_SOUNDTRACK="${YELLOW}soundtrack$OFF" +FORMAT_TYPE_SECONDARY_SPOKENWORD="${YELLOW}spokenword$OFF" +FORMAT_TYPE_SECONDARY_INTERVIEW="${YELLOW}interview$OFF" +FORMAT_TYPE_SECONDARY_AUDIOBOOK="${YELLOW}audiobook$OFF" +FORMAT_TYPE_SECONDARY_AUDIODRAMA="${YELLOW}audio drama$OFF" +FORMAT_TYPE_SECONDARY_LIVE="${YELLOW}live$OFF" +FORMAT_TYPE_SECONDARY_REMIX="${YELLOW}remix$OFF" +FORMAT_TYPE_SECONDARY_DJMIX="${YELLOW}DJ-mix$OFF" +FORMAT_TYPE_SECONDARY_MIXTAPE="${YELLOW}mixtape$OFF" +FORMAT_TYPE_SECONDARY_DEMO="${YELLOW}demo$OFF" +FORMAT_TYPE_SECONDARY_FIELDREC="${YELLOW}field recording$OFF" + +# Artist Preview +# ============== +# Main preview format. Takes two %s placeholder. The first is for the artist +# biography. The second for the life span. +APV_FORMAT="\n\nBio:\n%s\n\n${WHITE}%s${OFF}" +# Specification of a date +APV_DATE="%s" +# Specification of a place +APV_PLACE="%s" +# Specification of a date and a place (in that order) +APV_DATEPLACE="%s in %s" +# String to represent when/where a person is born +APV_BORN="Born: %s" +# String to represent when/where a person died +APV_DIED="Died: %s" + +# Release view +# ============ +# Format of a string that represents a release. +RV_FORMAT="<<media>> (<<tracks>>, yr: <<year>>)\t<<label>>" +# Additional string to display the release title and artist name +RV_TITLE_ARTIST="${CYAN}<<artist>>$OFF - ${PURPLE}<<title>>$OFF" +# Additional string to display the release title +RV_TITLE="${PURPLE}<<title>>$OFF" +# Additional string to display the artist +RV_ARTIST="${CYAN}<<artist>>$OFF" + +# Recording view +# ============== +# Format of a track in a release +REC_FORMAT="<<med>>\t${RED}<<nr>>$OFF\t${GREEN}<<title>>$OFF\t${CYAN}<<artist>>$OFF\t${WHITE}<<duration>>$OFF" +# Format of a track in the playlist +REC_FORMAT_NO_NUMBER="${GREEN}<<title>>$OFF\t${CYAN}<<artist>>$OFF\t${WHITE}<<duration>>$OFF" diff --git a/src/main.sh b/src/main.sh index 9998eed..56260b9 100755 --- a/src/main.sh +++ b/src/main.sh @@ -128,6 +128,11 @@ EOF exit 0 fi +if [ "${1:-}" = "--refresh-view" ]; then + precompute_view + exit 0 +fi + # Set window title printf '\033]0;%s\007' "$WINDOW_TITLE" @@ -219,7 +224,7 @@ $KEYS_FILTER_LOCAL:transform:$0 --fzf-key {2} {3} {4}" \ --delimiter="\t" \ --prompt="$SEARCH_PROMPT" \ --margin="5%,20%" \ - --bind="$KEYS_FILTER_LOCAL:change-query($FORMAT_LOCAL )" \ + --bind="$KEYS_FILTER_LOCAL:change-query('$QUERY_LOCAL' )" \ --accept-nth="{3}" \ --with-nth="{1}" || true ) @@ -239,18 +244,22 @@ $KEYS_FILTER_LOCAL:transform:$0 --fzf-key {2} {3} {4}" \ --with-nth="{1}" >/dev/null ;; *) # 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_ALL: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 + sel=$( + $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_ALL:transform:$0 --fzf-key {2} {3} {4}" \ + --expect="ctrl-c" \ + --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}" || true + ) + [ "$(echo "$sel" | head -1)" = "ctrl-c" ] && break ;; esac done diff --git a/src/sh/config.sh b/src/sh/config.sh index 44daf93..16ae10c 100644 --- a/src/sh/config.sh +++ b/src/sh/config.sh @@ -1,4 +1,5 @@ -if [ "${CONFIGFILE:-}" ]; then - [ ! -f "$CONFIGFILE" ] && err "Configuration $CONFIGFILE not found." && exit 1 - . "$CONFIGFLIE" -fi +CONFIGFILE_DEFAULT="$HOME/.config/$APP_NAME/config" +CONFIGFILE="${CONFIGFILE:-"$CONFIGFILE_DEFAULT"}" +[ "$CONFIGFILE" != "$CONFIGFILE_DEFAULT" ] && [ ! -f "$CONFIGFILE" ] && err "The configuration file manually specified with the environment variable CONFIGFILE=($CONFIGFILE) does not exist." && exit 1 +# shellcheck source=/dev/null +[ -f "$CONFIGFILE" ] && . "$CONFIGFILE" diff --git a/src/sh/fzf.sh b/src/sh/fzf.sh index 0346d1b..2b142d1 100644 --- a/src/sh/fzf.sh +++ b/src/sh/fzf.sh @@ -59,11 +59,10 @@ fzf_handle_load() { args=$(state_get_args) case "$view" in "$VIEW_ARTIST") - secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" - QUERY="$(printf "!%s " "$secsymb")" + QUERY="!'$QUERY_HAS_SECONDARY' " ;; "$VIEW_RELEASEGROUP") - QUERY="$(printf "%s " "$FORMAT_STATUS_OFFICIAL")" + [ "$QUERY_RV" ] && QUERY="'$QUERY_RV' " || QUERY="" ;; "$VIEW_SEARCH_ARTIST") ENABLE_CHANGE=1 @@ -104,7 +103,7 @@ fzf_info() { echo "Search albums" ;; "$VIEW_SELECT_ARTIST") - foo "Select artist (WE SHOULDNT SEE THIS; THIS IS A BUG!)" + debug "Select artist (WE SHOULDNT SEE THIS; THIS IS A BUG!)" ;; *) if [ "$FZF_KEY" ]; then @@ -247,7 +246,7 @@ fzf_handle_key() { "$VIEW_PLAYLIST") ;; *) state_update_keep_args "$view" "$MODE_NORMAL" - __set_prompt "$view" "$mode" + __set_prompt "$view" "$MODE_NORMAL" ;; esac ;; @@ -258,8 +257,8 @@ fzf_handle_key() { if [ "$view" = "$VIEW_PLAYLIST" ]; then case ",$KEYS_PLAYLIST_RELOAD," in *",$FZF_KEY,"*) - foo "hit playlist reload key" - foo "going to call $(printf "reload:%s" "$0 --fzf-reload")" + debug "hit playlist reload key" + debug "going to call $(printf "reload:%s" "$0 --fzf-reload")" printf "reload:%s" "$0 --fzf-reload" ;; esac @@ -341,7 +340,7 @@ fzf_handle_key() { ACCEPT=1 ;; "$VIEW_PLAYLIST") - foo "NOT IMPLEMENTED" + debug "NOT IMPLEMENTED" ;; esac [ "$VIEW_NEXT_ARGS" ] && VIEW_NEXT="$VIEW_SELECT_ARTIST" @@ -351,7 +350,7 @@ fzf_handle_key() { *",$FZF_KEY,"*) case "$view" in "$VIEW_SEARCH_ARTIST" | "$VIEW_SEARCH_ALBUM" | "$VIEW_PLAYLIST") ;; - *) QUERY="$FORMAT_LOCAL " ;; + *) QUERY="'$QUERY_LOCAL' " ;; esac ;; esac @@ -360,49 +359,10 @@ fzf_handle_key() { # VIEW_LIST_ARTISTS="list-artists" # VIEW_SELECT_ARTIST="select-artist" # VIEW_PLAYLIST="playlist" - case ",$KEYS_FILTER_1," in - *",$FZF_KEY,"*) - case "$view" in - "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") - secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" - QUERY="!$secsymb " - ;; - esac - ;; - esac - case ",$KEYS_FILTER_2," in - *",$FZF_KEY,"*) - case "$view" in - "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") - secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" - typesymb="$(printf "$FORMAT_TYPE_ALBUM" | sed "s/ /\\\ /g")" - QUERY="!$secsymb '$typesymb' " - ;; - esac - ;; - esac - case ",$KEYS_FILTER_3," in - *",$FZF_KEY,"*) - case "$view" in - "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") - secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" - typesymb="$(printf "$FORMAT_TYPE_EP" | sed "s/ /\\\ /g")" - QUERY="!$secsymb '$typesymb' " - ;; - esac - ;; - esac - case ",$KEYS_FILTER_4," in - *",$FZF_KEY,"*) - case "$view" in - "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") - secsymb="$(printf "$FORMAT_TYPE_HAS_SECONDARY" "")" - typesymb="$(printf "$FORMAT_TYPE_SINGLE" | sed "s/ /\\\ /g")" - QUERY="!$secsymb '$typesymb' " - ;; - esac - ;; - esac + case ",$KEYS_FILTER_1," in *",$FZF_KEY,"*) case "$view" in "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") QUERY="!'$QUERY_HAS_SECONDARY' " ;; esac ;; esac + case ",$KEYS_FILTER_2," in *",$FZF_KEY,"*) case "$view" in "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") QUERY="!'$QUERY_HAS_SECONDARY' '$QUERY_ALBUM' " ;; esac ;; esac + case ",$KEYS_FILTER_3," in *",$FZF_KEY,"*) case "$view" in "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") QUERY="!'$QUERY_HAS_SECONDARY' '$QUERY_EP' " ;; esac ;; esac + case ",$KEYS_FILTER_4," in *",$FZF_KEY,"*) case "$view" in "$VIEW_ARTIST" | "$VIEW_LIST_ALBUMS") QUERY="!'$QUERY_HAS_SECONDARY' '$QUERY_SINGLE' " ;; esac ;; esac case ",$KEYS_SWITCH_ARTIST_ALBUM," in *",$FZF_KEY,"*) case "$view" in @@ -440,7 +400,7 @@ fzf_handle_key() { [ "$path" ] || return 0 case "$view" in "$VIEW_ARTIST" | "$VIEW_SEARCH_ARTIST" | "$VIEW_SEARCH_ALBUM" | "$VIEW_LIST_ARTISTS" | "$VIEW_LIST_ALBUMS") - foo "not implemented" + debug "not implemented" ;; "$VIEW_RELEASEGROUP") generate_playlist "$mbid" "$path" | mpv_play_list >/dev/null @@ -461,7 +421,7 @@ fzf_handle_key() { [ "$path" ] || return 0 case "$view" in "$VIEW_ARTIST" | "$VIEW_SEARCH_ARTIST" | "$VIEW_SEARCH_ALBUM" | "$VIEW_LIST_ARTISTS" | "$VIEW_LIST_ALBUMS") - foo "not implemented" + debug "not implemented" ;; "$VIEW_RELEASEGROUP") generate_playlist "$mbid" "$path" | mpv_queue_list >/dev/null @@ -536,7 +496,7 @@ fzf_handle_key() { *",$FZF_KEY,"*) case "$view" in "$VIEW_SELECT_ARTIST" | "$VIEW_PLAYLIST") printf "accept" ;; - *) printf "abort" ;; + *) printf "print(Q)+abort" ;; esac ;; esac @@ -546,7 +506,7 @@ fzf_handle_key() { "$VIEW_PLAYLIST") ;; *) state_update_keep_args "$view" "$MODE_INSERT" - __set_prompt "$view" "$mode" + __set_prompt "$view" "$MODE_INSERT" ;; esac ;; diff --git a/src/sh/helper.sh b/src/sh/helper.sh index 4574730..e1a3954 100644 --- a/src/sh/helper.sh +++ b/src/sh/helper.sh @@ -2,6 +2,7 @@ ERR="\033[38;5;196m" INFO="\033[38;5;75m" DBG=$ERR OFF="\033[m" + err() { echo "${ERR}ERROR:${OFF} ${1:-}" >/dev/stderr } @@ -11,9 +12,5 @@ info() { } debug() { - echo "${DBG}DEBUG${OFF} ${INFO}$(date)${OFF}: $*" >/dev/stderr -} - -foo() { - echo "${DBG}DEBUG${OFF} ${INFO} [$$] $(date)${OFF}: $*" >>/tmp/foo + echo "${DBG}DEBUG${OFF} ${INFO} [$$] $(date)${OFF}: $*" >>"$APP_NAME.debug.log" } diff --git a/src/sh/info.sh b/src/sh/info.sh index 97c1275..f008979 100644 --- a/src/sh/info.sh +++ b/src/sh/info.sh @@ -1,5 +1,5 @@ APP_NAME="muf" APP_VERSION="zero.zero" APP_WEBSITE="https://git.indyfac.ch/amin/muf" -WINDOW_TITLE="๐Ÿ”Ž๐ŸŽถ $APP_NAME | a simple music finder" +WINDOW_TITLE="๐Ÿ”Ž๐ŸŽถ $APP_NAME | a simple music finder and player" export APP_NAME APP_VERSION APP_WEBSITE WINDOW_TITLE diff --git a/src/sh/local.sh b/src/sh/local.sh index c17eece..7ff4ae9 100644 --- a/src/sh/local.sh +++ b/src/sh/local.sh @@ -103,32 +103,8 @@ LOCALDATA_RELEASEGROUPS_VIEW="$LOCALDATADIR/releasegroups_view" LOCALDATA_RELEASES_VIEW="$LOCALDATADIR/releases_view" DECORATION_FILENAME=${DECORATION_FILENAME:-"mbid.json"} -# Load local music -# argument $1: path to decorated music files -load_local() { - [ -d "$LOCALDATADIR" ] || mkdir -p "$LOCALDATADIR" - tmpreleases=$(mktemp) - [ -f "$tmpreleases" ] || exit 1 - info "Locating and parsing decoration files ($DECORATION_FILENAME)" - find "$1" -type f -name "$DECORATION_FILENAME" -print0 | - xargs -0 -P 4 $JQ -r '.releaseid+"\t"+input_filename' | - tee "$LOCALDATA_RELEASES" | - cut -d "$(printf '\t')" -f 1 >"$tmpreleases" - __batch_load_missing "$TYPE_RELEASE" "$tmpreleases" - # Get release groups and album artists - while IFS= read -r rid; do - mb=$(mb_release "$rid") - echo "$mb" | $JQ -r '."release-group".id' >>"$LOCALDATA_RELEASEGROUPS" - echo "$mb" | $JQ -r '."release-group"."artist-credit" | map(.artist.id) | join("\n")' >>"$LOCALDATA_ARTISTS" - done <"$tmpreleases" - tf=$(mktemp) - sort "$LOCALDATA_RELEASEGROUPS" | uniq >"$tf" && mv "$tf" "$LOCALDATA_RELEASEGROUPS" - sort "$LOCALDATA_ARTISTS" | uniq >"$tf" && mv "$tf" "$LOCALDATA_ARTISTS" - # Populate cache with missing data - __batch_load_missing "$TYPE_RELEASEGROUP" "$LOCALDATA_RELEASEGROUPS" - __batch_load_missing "$TYPE_ARTIST" "$LOCALDATA_ARTISTS" - rm -f "$tmpreleases" - # Precompute views +# Precompute views +precompute_view() { info "Precomputing artist view" while IFS= read -r aid; do mb_artist "$aid" | $JQ -r '[ @@ -231,3 +207,33 @@ load_local() { column -t -s "$(printf '\t')" | sed 's| \+\([0-9a-f-]\+\) \+\([0-9a-f-]\+\):\(.*$\)$|\t\1\t\2\t\3|' >"$LOCALDATA_RELEASES_VIEW" } + +# Load local music +# argument $1: path to decorated music files +load_local() { + [ -d "$LOCALDATADIR" ] || mkdir -p "$LOCALDATADIR" + tmpreleases=$(mktemp) + [ -f "$tmpreleases" ] || exit 1 + info "Locating and parsing decoration files ($DECORATION_FILENAME)" + find "$1" -type f -name "$DECORATION_FILENAME" -print0 | + xargs -0 -P 4 $JQ -r '.releaseid+"\t"+input_filename' | + tee "$LOCALDATA_RELEASES" | + cut -d "$(printf '\t')" -f 1 >"$tmpreleases" + __batch_load_missing "$TYPE_RELEASE" "$tmpreleases" + # Get release groups and album artists + while IFS= read -r rid; do + mb=$(mb_release "$rid") + echo "$mb" | $JQ -r '."release-group".id' >>"$LOCALDATA_RELEASEGROUPS" + echo "$mb" | $JQ -r '."release-group"."artist-credit" | map(.artist.id) | join("\n")' >>"$LOCALDATA_ARTISTS" + done <"$tmpreleases" + tf=$(mktemp) + sort "$LOCALDATA_RELEASEGROUPS" | uniq >"$tf" && mv "$tf" "$LOCALDATA_RELEASEGROUPS" + sort "$LOCALDATA_ARTISTS" | uniq >"$tf" && mv "$tf" "$LOCALDATA_ARTISTS" + # Populate cache with missing data + __batch_load_missing "$TYPE_RELEASEGROUP" "$LOCALDATA_RELEASEGROUPS" + __batch_load_missing "$TYPE_ARTIST" "$LOCALDATA_ARTISTS" + rm -f "$tmpreleases" + info "Resetting views" + rm -f "$LOCALDATA_ARTISTS_VIEW" "$LOCALDATA_RELEASEGROUPS_VIEW" "$LOCALDATA_RELEASES_VIEW" + precompute_view +} diff --git a/src/sh/theme.sh b/src/sh/theme.sh index 8a43d9e..4f1b1b5 100644 --- a/src/sh/theme.sh +++ b/src/sh/theme.sh @@ -1,44 +1,81 @@ -# Colors (local) -FAINT="\033[2m" -CARTIST="\033[38;5;209m" -CTITLE="\033[38;5;229m" -CYEAR="\033[38;5;179m" -CDISAMB="$FAINT\033[38;5;172m" -CNOTE="\033[38;5;242m" -CXXX="\033[38;5;109m" -CDESC="\033[38;5;254m" -CLIFE="\033[38;5;251m" -OFF="\033[m" +# Theme file (default theme) +# This file specifies the main theme of the visual representation. All +# specifications provided here may be altered using the configuration file. +# +# This theme requires fonts that support utf-8, colors, and emojis. +# Colors (internal only) +ESC=$(printf '\033') +FAINT="${ESC}[2m" +CARTIST="${ESC}[38;5;209m" +CTITLE="${ESC}[38;5;229m" +CYEAR="${ESC}[38;5;179m" +CDISAMB="$FAINT${ESC}[38;5;172m" +CNOTE="${ESC}[38;5;242m" +CXXX="${ESC}[38;5;109m" +CDESC="${ESC}[38;5;254m" +CLIFE="${ESC}[38;5;251m" +OFF="${ESC}[m" + +# Pointers +# ======== +# Sign that indicates the existence of audio files FORMAT_LOCAL="${FORMAT_LOCAL:-"๐Ÿ”†"}" -export FORMAT_LOCAL +# Pointer to the track currently playing (playlist) FORMAT_CURRENT="${FORMAT_CURRENT:-"๐Ÿ‘‰"}" -# Prompts +# Input prompts +# ============= +# General search prompt SEARCH_PROMPT=${SEARCH_PROMPT:-"๐Ÿ”Ž โŒช"} +# Prompt that takes an artist name as argument ARTIST_PROMPT="${ARTIST_PROMPT:-"๐ŸŽค ${CARTIST}%s$OFF โŒช"}" +# Prompt that takes an artist name and a release name as arguments (in that +# order) FULL_PROMPT="${FULL_PROMPT:-"๐ŸŽค ${CARTIST}%s$OFF ใ€‹${CTITLE}%s$OFF โŒช"}" + +# Visual representation of current mode +# ===================================== +# Sign to indicate `normal` mode PROMPT_NORMAL="${PROMPT_NORMAL:-"[n]"}" +# Sign to indicate `insert` mode PROMPT_INSERT="${PROMPT_INSERT:-"[i]"}" # Artist view +# =========== +# Artist string for persons AV_PERSON="${AV_PERSON:-"๐Ÿง‘โ€๐ŸŽค $CARTIST<<name>>$OFF"}" +# Artist string for groups AV_GROUP="${AV_GROUP:-"๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ $CARTIST<<name>>$OFF"}" +# Artist disambiguation string AV_DISAMBIGUATION="${AV_DISAMBIGUATION:-"$CDISAMB(<<disambiguation>>)$OFF"}" -# Release group view +# Release-group view +# ================== +# Default release group string RGV_RELEASE="${RGV_RELEASE:-"${CTITLE}<<title>>$OFF"}" +# Release group string if the artist name differs from the current artist RGV_RELEASE_W_ARTIST="${RGV_RELEASE_W_ARTIST:-"${CTITLE}<<title>>$OFF โ€” ${CARTIST}<<artist>>$OFF"}" +# Year of the release group RGV_YEAR="${RGV_YEAR:-"${CYEAR}(<<year>>)$OFF"}" -# Types +# Release-group types +# =================== +# Album FORMAT_TYPE_ALBUM="${FORMAT_TYPE_ALBUM:-"LP ๐Ÿ’ฝ"}" +# EP FORMAT_TYPE_EP="${FORMAT_TYPE_EP:-"EP ๐Ÿ“€"}" +# Single FORMAT_TYPE_SINGLE="${FORMAT_TYPE_SINGLE:-"SI ๐ŸŽถ"}" +# Broadcast FORMAT_TYPE_BROADCAST="${FORMAT_TYPE_BROADCAST:-"BR ๐Ÿ“ป"}" +# Other FORMAT_TYPE_OTHER="${FORMAT_TYPE_OTHER:-"OT โ”"}" +# Flag to indicate that the given release group has associated secondary types. FORMAT_TYPE_HAS_SECONDARY="${FORMAT_TYPE_HAS_SECONDARY:-"%sโ˜ผ"}" +# Style to represent secondary types (takes one %s placeholder) FORMAT_TYPE_SECONDARY="${FORMAT_TYPE_SECONDARY:-"${CNOTE}[โ˜ผ: %s]$OFF"}" +# Secondary types FORMAT_TYPE_SECONDARY_COMPILATION="${FORMAT_TYPE_SECONDARY_COMPILATION:-"๐Ÿงฉ compilation"}" FORMAT_TYPE_SECONDARY_SOUNDTRACK="${FORMAT_TYPE_SECONDARY_SOUNDTRACK:-"๐ŸŽฌ soundtrack"}" FORMAT_TYPE_SECONDARY_SPOKENWORD="${FORMAT_TYPE_SECONDARY_SPOKENWORD:-"๐Ÿ“– spokenword"}" @@ -53,14 +90,34 @@ FORMAT_TYPE_SECONDARY_DEMO="${FORMAT_TYPE_SECONDARY_DEMO:-"๐Ÿงช demo"}" FORMAT_TYPE_SECONDARY_FIELDREC="${FORMAT_TYPE_SECONDARY_FIELDREC:-"๐ŸŒฟ field recording"}" # Artist Preview +# ============== +# Main preview format. Takes two %s placeholder. The first is for the artist +# biography. The second for the life span. APV_FORMAT="${APV_FORMAT:-"\n\n${CDESC}%s${OFF}\n\n${CLIFE}%s${OFF}"}" +# Specification of a date APV_DATE="${APV_DATE:-"%s"}" +# Specification of a place APV_PLACE="${APV_PLACE:-"%s"}" +# Specification of a date and a place (in that order) APV_DATEPLACE="${APV_DATEPLACE:-"$APV_DATE, $APV_PLACE"}" +# String to represent when/where a person is born APV_BORN="${APV_BORN:-"๐Ÿผ Born: %s"}" +# String to represent when/where a person died APV_DIED="${APV_DIED:-"๐Ÿ•ฏ๏ธ Died: %s"}" -# Status +# Release view +# ============ +# Format of a string that represents a release. +RV_FORMAT="${RV_FORMAT:-"<<status>>\t${CXXX}<<tracks>> tracks\t<<media>>$OFF\t${CYEAR}<<year>>\t<<country>>$OFF\t${CARTIST}<<label>>$OFF"}" +# Additional string to display the release title and artist name +RV_TITLE_ARTIST="${RV_TITLE_ARTIST:-"${FAINT}as ${CTITLE}<<title>>$OFF by ${FAINT}${CARTIST}<<artist>>$OFF"}" +# Additional string to display the release title +RV_TITLE="${RV_TITLE:-"${FAINT}as ${CTITLE}<<title>>$OFF"}" +# Additional string to display the artist +RV_ARTIST="${RV_ARTIST:-"${FAINT}by ${CARTIST}<<artist>>$OFF"}" + +# Release Status +# ============== FORMAT_STATUS_OFFICIAL="${FORMAT_STATUS_OFFICIAL:-"๐ŸŸข official"}" FORMAT_STATUS_PROMO="${FORMAT_STATUS_PROMO:-"๐Ÿ“ฃ promo"}" FORMAT_STATUS_BOOTLEG="${FORMAT_STATUS_BOOTLEG:-"๐Ÿ’ฃ bootleg"}" @@ -69,12 +126,27 @@ FORMAT_STATUS_WITHDRAWN="${FORMAT_STATUS_WITHDRAWN:-"๐Ÿ”™ withdrawn"}" FORMAT_STATUS_EXPUNGED="${FORMAT_STATUS_EXPUNGED:-"๐Ÿงน expunged"}" FORMAT_STATUS_CANCELLED="${FORMAT_STATUS_CANCELLED:-"โŒ cancelled"}" -# Release view -RV_FORMAT="<<status>>\t${CXXX}<<tracks>> tracks\t<<media>>$OFF\t${CYEAR}<<year>>\t<<country>>$OFF\t${CARTIST}<<label>>$OFF" -RV_TITLE_ARTIST="${FAINT}as ${CTITLE}<<title>>$OFF by ${FAINT}${CARTIST}<<artist>>$OFF" -RV_TITLE="${FAINT}as ${CTITLE}<<title>>$OFF" -RV_ARTIST="${FAINT}by ${CARTIST}<<artist>>$OFF" - # Recording view -REC_FORMAT="${CNOTE}${FAINT}<<med>>\t${CNOTE}<<nr>>$OFF\t${CTITLE}<<title>>\t${CARTIST}<<artist>>\t${CXXX}<<duration>>$OFF" -REC_FORMAT_NO_NUMBER="${CTITLE}<<title>>\t${CARTIST}<<artist>>\t${CXXX}<<duration>>$OFF" +# ============== +# Format of a track in a release +REC_FORMAT="${REC_FORMAT:-"${CNOTE}${FAINT}<<med>>\t${CNOTE}<<nr>>$OFF\t${CTITLE}<<title>>\t${CARTIST}<<artist>>\t${CXXX}<<duration>>$OFF"}" +# Format of a track in the playlist +REC_FORMAT_NO_NUMBER="${REC_FORMAT_NO_NUMBER:-"${CTITLE}<<title>>\t${CARTIST}<<artist>>\t${CXXX}<<duration>>$OFF"}" + +# Derivatives +# =========== +QUERY_LOCAL=$(printf "%s" "$FORMAT_LOCAL" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +QUERY_HAS_SECONDARY=$(printf "$FORMAT_TYPE_HAS_SECONDARY" "" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +QUERY_ALBUM=$(printf "%s" "$FORMAT_TYPE_ALBUM" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +QUERY_EP=$(printf "%s" "$FORMAT_TYPE_EP" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +QUERY_SINGLE=$(printf "%s" "$FORMAT_TYPE_SINGLE" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +QUERY_BROADCAST=$(printf "%s" "$FORMAT_TYPE_BROADCAST" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +QUERY_OTHER=$(printf "%s" "$FORMAT_TYPE_OTHER" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +if printf "$RV_FORMAT" | grep -q "<<status>>"; then + QUERY_RV=$(printf "%s" "$FORMAT_STATUS_OFFICIAL" | sed "s/${ESC}\[[0-9;]*[mK]//g" | sed "s/ /\\\ /g") +else + QUERY_RV="" +fi + +# Necessary exports +export QUERY_LOCAL QUERY_HAS_SECONDARY QUERY_ALBUM QUERY_EP QUERY_SINGLE QUERY_BROADCAST QUERY_OTHER QUERY_RV