Compare commits
7 Commits
347014a8ce
...
main
Author | SHA1 | Date | |
---|---|---|---|
1373fd7bb8 | |||
c85f3df62e | |||
c3d5d324ab | |||
665a2c936c | |||
2a42a31cf5 | |||
c638d50698 | |||
46dacef79d |
364
fuzique
Executable file
364
fuzique
Executable file
@@ -0,0 +1,364 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
APP_NAME="fuzique"
|
||||||
|
|
||||||
|
if [ "${1:-}" = "--mpv-play" ]; then
|
||||||
|
if [ "$3" = "$MODE_PLAYLIST" ]; then
|
||||||
|
echo "{ \"command\": [\"set\", \"playlist-pos-1\", \"$FZF_POS\"] }" | socat - "$MPV_SOCKET"
|
||||||
|
else
|
||||||
|
echo "{ \"command\": [\"loadfile\", \"$2\"] }" | socat - "$MPV_SOCKET"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${1:-}" = "--mpv-queue" ]; then
|
||||||
|
echo "{ \"command\": [\"loadfile\", \"$2\", \"append-play\"] }" | socat - "$MPV_SOCKET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${1:-}" = "--mpv-next" ]; then
|
||||||
|
echo '{ "command": ["playlist-next"] }' | socat - "$MPV_SOCKET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${1:-}" = "--mpv-prev" ]; then
|
||||||
|
echo '{ "command": ["playlist-prev"] }' | socat - "$MPV_SOCKET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${1:-}" = "--mpv-seekf" ]; then
|
||||||
|
echo '{ "command": ["seek", "10"] }' | socat - "$MPV_SOCKET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${1:-}" = "--mpv-seekb" ]; then
|
||||||
|
echo '{ "command": ["seek", "-10"] }' | socat - "$MPV_SOCKET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${1:-}" = "--mpv-toggle-pause" ]; then
|
||||||
|
echo '{ "command": ["cycle", "pause"] }' | socat - "$MPV_SOCKET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${1:-}" = "--browse" ]; then
|
||||||
|
rel=${3#"$ROOT/"}
|
||||||
|
d=$(echo "$rel" | awk -F'/' '{ print NF }')
|
||||||
|
if [ "$2" = "l" ]; then
|
||||||
|
if [ "$d" -eq 1 ]; then
|
||||||
|
grep -F "$3/" "$release_file_browse"
|
||||||
|
elif [ "$d" -eq 2 ]; then
|
||||||
|
grep -F "$3/" "$tracks_file_browse"
|
||||||
|
else
|
||||||
|
p=$(echo "$rel" | cut -d '/' -f 1,2)
|
||||||
|
grep -F "$ROOT/$p/" "$tracks_file_browse"
|
||||||
|
fi
|
||||||
|
elif [ "$2" = "h" ]; then
|
||||||
|
if [ "$d" -eq 3 ]; then
|
||||||
|
p=$(echo "$rel" | cut -d '/' -f 1)
|
||||||
|
grep -F "$ROOT/$p/" "$release_file_browse"
|
||||||
|
elif [ "$d" -eq 2 ]; then
|
||||||
|
cat "$artists_file_browse"
|
||||||
|
else
|
||||||
|
cat "$artists_file_browse"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${1:-}" = "--prompt" ]; then
|
||||||
|
shift
|
||||||
|
file="$1"
|
||||||
|
shift
|
||||||
|
mode="$1"
|
||||||
|
case "$mode" in
|
||||||
|
"$MODE_SEARCH")
|
||||||
|
printf "$SEARCH_PROMPT"
|
||||||
|
;;
|
||||||
|
"$MODE_BROWSE")
|
||||||
|
rel=${file#"$ROOT/"}
|
||||||
|
artist=$(echo "$rel" | cut -d "/" -f 1)
|
||||||
|
release=$(echo "$rel" | cut -d "/" -f 2)
|
||||||
|
d=$(echo "$rel" | awk -F'/' '{ print NF }')
|
||||||
|
[ "$d" -eq 1 ] && printf "$BROWSE_PROMPT_ROOT"
|
||||||
|
[ "$d" -eq 2 ] && printf "$BROWSE_PROMPT_ARTIST" "$artist"
|
||||||
|
[ "$d" -eq 3 ] && printf "$BROWSE_PROMPT_RELEASE" "$artist" "$release"
|
||||||
|
;;
|
||||||
|
"$MODE_PLAYLIST")
|
||||||
|
printf ""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${1:-}" = "--preview" ]; then
|
||||||
|
shift
|
||||||
|
file="$1"
|
||||||
|
rel=${file#"$ROOT/"}
|
||||||
|
artist=$(echo "$rel" | cut -d "/" -f 1)
|
||||||
|
release=$(echo "$rel" | cut -d "/" -f 2)
|
||||||
|
track=$(echo "$rel" | cut -d "/" -f 3)
|
||||||
|
if [ "$track" ]; then
|
||||||
|
echo "(not implemented: track info for $track)"
|
||||||
|
elif [ "$release" ]; then
|
||||||
|
echo "(not implemented: release info for $release)"
|
||||||
|
elif [ "$artist" ]; then
|
||||||
|
echo "(not implemented: artist info for $artist)"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mpv_text() {
|
||||||
|
echo "{ \"command\": [\"expand-text\",\"$1\"] }" | socat - "$MPV_SOCKET" | jq -r '.data'
|
||||||
|
}
|
||||||
|
|
||||||
|
tags_from_file() {
|
||||||
|
ffprobe \
|
||||||
|
-v quiet \
|
||||||
|
-show_entries format \
|
||||||
|
-of json \
|
||||||
|
"$1" |
|
||||||
|
jq -r '.format.tags | [ [.artist, .ARTIST], [.album, .ALBUM], [.title, .TITLE] ] | map(map(select(. != null))) | map(.[0]) | join("|")'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${1:-}" = "--show-playlist" ]; then
|
||||||
|
count=$(mpv_text '${playlist/count}')
|
||||||
|
if [ "$count" -eq 0 ]; then
|
||||||
|
printf "$EMPTY_PLAYLIST_STRING\t\t%s\n" "$MODE_PLAYLIST"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
pos=$(mpv_text '${playlist-pos}')
|
||||||
|
cmd=""
|
||||||
|
for i in $(seq 0 $((count - 1))); do
|
||||||
|
cmd="$cmd\${playlist/$i/filename}|"
|
||||||
|
done
|
||||||
|
fn=$(mpv_text "$cmd")
|
||||||
|
for i in $(seq 0 $((count - 1))); do
|
||||||
|
f=$(echo "$fn" | cut -d "|" -f "$((i + 1))")
|
||||||
|
[ ! "$f" ] && continue
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
tags=$(tags_from_file "$f")
|
||||||
|
artist=$(echo "$tags" | cut -d "|" -f 1)
|
||||||
|
release=$(echo "$tags" | cut -d "|" -f 2)
|
||||||
|
track=$(echo "$tags" | cut -d "|" -f 3)
|
||||||
|
fi
|
||||||
|
rel=${f#"$ROOT/"}
|
||||||
|
[ "${artist:-}" ] || artist=$(echo "$rel" | cut -d "/" -f 1)
|
||||||
|
[ "${release:-}" ] || release=$(echo "$rel" | cut -d "/" -f 2)
|
||||||
|
[ "${track:-}" ] || track=$(echo "$rel" | cut -d "/" -f 3 | sed 's/\..*$//')
|
||||||
|
artist=$(printf "$AFMT" "$artist")
|
||||||
|
release=$(printf "$RFMT" "$release")
|
||||||
|
track=$(printf "$TFMT" "$track")
|
||||||
|
[ "$i" -eq "$pos" ] && pnt="$PLAYLIST_POINTER" || pnt=" "
|
||||||
|
printf "%s|%s|%s|%s\t%s\t%s\n" "$pnt" "$track" "$release" "$artist" "$f" "$MODE_PLAYLIST"
|
||||||
|
done |
|
||||||
|
grep '.' |
|
||||||
|
column -t -s '|'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
command -v "fdfind" >/dev/null && FD="fdfind" || (err "Did not find \`fdfind\`." && exit 1)
|
||||||
|
command -v "fzf" >/dev/null && FZF="fzf" || (err "Did not find \`fzf\`." && exit 1)
|
||||||
|
command -v "mpv" >/dev/null && MPV="mpv" || (err "Did not find \`mpv\`." && exit 1)
|
||||||
|
if command -v "bat" >/dev/null; then
|
||||||
|
CAT="bat"
|
||||||
|
elif command -v "batcat" >/dev/null; then
|
||||||
|
CAT="batcat"
|
||||||
|
fi
|
||||||
|
CAT=${CAT:+$CAT --color=always --style=plain --language=md}
|
||||||
|
CAT=${CAT:-cat}
|
||||||
|
export CAT
|
||||||
|
|
||||||
|
if [ ! "${1:-}" ] || [ "${1:-}" = "--help" ]; then
|
||||||
|
$CAT <<EOF
|
||||||
|
Usage: \`$0 [ --help | [ MUSIC_DIRECTORY [ --reset-cache ] ] ]\`
|
||||||
|
|
||||||
|
The \`MUSIC_DIRECTORY\` is the path to a folder with the following tree
|
||||||
|
structure: \`./<artist>/<album>/<track>\`. For instance:
|
||||||
|
\`\`\`sh
|
||||||
|
$ tree /media/Musik
|
||||||
|
/media/Musik/Mos Def
|
||||||
|
└── Black on Both Sides
|
||||||
|
├── 01 Fear Not of Man.m4a
|
||||||
|
├── 02 Hip Hop.m4a
|
||||||
|
├── 03 Love.m4a
|
||||||
|
├── 04 Ms. Fat Booty.m4a
|
||||||
|
├── 05 Speed Law.m4a
|
||||||
|
├── 06 Do It Now.m4a
|
||||||
|
├── 07 Got.m4a
|
||||||
|
├── 08 Umi Says.m4a
|
||||||
|
├── 09 New World Water.m4a
|
||||||
|
├── 10 Rock n Roll.m4a
|
||||||
|
├── 11 Know That.m4a
|
||||||
|
├── 12 Climb.m4a
|
||||||
|
├── 13 Brooklyn.m4a
|
||||||
|
├── 14 Habitat.m4a
|
||||||
|
├── 15 Mr. Nigga.m4a
|
||||||
|
├── 16 Mathematics.m4a
|
||||||
|
├── 17 May‐December.m4a
|
||||||
|
└── cover.jpg
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
[OPTIONS]
|
||||||
|
\`--help\`: Show this help and exit
|
||||||
|
\`--reset-cache\`: Reset cache for specified \`MUSIC_DIRECTORY\`
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load configuration
|
||||||
|
[ "${1:?"You did not specify the music directory. Run $0 --help."}" ] && ROOT="$(realpath "$1")" && shift
|
||||||
|
ROOT=${ROOT%%/}
|
||||||
|
[ ! -d "${ROOT:-}" ] && echo "Faild to recognize music directory. Run $0 --help." && exit 1
|
||||||
|
DEPTH=$(echo "$ROOT" | awk -F'/' '{ print NF }')
|
||||||
|
|
||||||
|
# Cache support
|
||||||
|
ROOTHASH=$(echo "$ROOT" | sha1sum | cut -d ' ' -f 1)
|
||||||
|
CACHE_DIR="$HOME/.cache/$APP_NAME"
|
||||||
|
CACHE_FILE="$CACHE_DIR/$ROOTHASH"
|
||||||
|
|
||||||
|
# Theme
|
||||||
|
ARTIST_COLOR=${ARTIST_COLOR:-'\033[38;5;202m'}
|
||||||
|
RELEASE_COLOR=${RELEASE_COLOR:-'\033[38;5;208m'}
|
||||||
|
TRACK_COLOR=${TRACK_COLOR:-'\033[38;5;215m'}
|
||||||
|
OFF="\033[m"
|
||||||
|
AFMT="${AFMT:-"🎤 ${ARTIST_COLOR}%s${OFF}"}"
|
||||||
|
RFMT="${RFMT:-"💽 ${RELEASE_COLOR}%s${OFF}"}"
|
||||||
|
TFMT="${TFMT:-"🎵 ${TRACK_COLOR}%s${OFF}"}"
|
||||||
|
PLAYLIST_POINTER="${PLAYLIST_POINTER:-👉}"
|
||||||
|
EMPTY_PLAYLIST_STRING="${EMPTY_PLAYLIST_STRING:-"\033[38;5;251m(empty playlist)${OFF}"}"
|
||||||
|
SEARCH_PROMPT=${SEARCH_PROMPT:-"🔎 search〉"}
|
||||||
|
BROWSE_PROMPT_ROOT="${BROWSE_PROMPT_ROOT:-"$SEARCH_PROMPT"}"
|
||||||
|
BROWSE_PROMPT_ARTIST="${BROWSE_PROMPT_ARTIST:-"$AFMT 〉"}"
|
||||||
|
BROWSE_PROMPT_RELEASE="${BROWSE_PROMPT_RELEASE:-"$AFMT 》 $RFMT 〉"}"
|
||||||
|
|
||||||
|
# Modes
|
||||||
|
MODE_BROWSE="B"
|
||||||
|
MODE_SEARCH="S"
|
||||||
|
MODE_PLAYLIST="P"
|
||||||
|
|
||||||
|
# Make socket
|
||||||
|
MPV_SOCKET="$(mktemp --suffix=.sock)"
|
||||||
|
|
||||||
|
# Load
|
||||||
|
if [ "${1:-}" = "--reset-cache" ] || [ ! -f "$CACHE_FILE" ]; then
|
||||||
|
rm -f "$CACHE_FILE"
|
||||||
|
[ ! -d "$CACHE_DIR" ] && mkdir -p "$CACHE_DIR"
|
||||||
|
{
|
||||||
|
$FD --max-depth 2 --type d . "$ROOT"
|
||||||
|
$FD --exact-depth 3 --type f -i -e "mp3" -e "mp4" -e "m4a" -e "ogg" -e "flac" -e "wav" . "$ROOT"
|
||||||
|
} |
|
||||||
|
sort >"$CACHE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse
|
||||||
|
artists_file_browse=$(mktemp)
|
||||||
|
release_file_browse=$(mktemp)
|
||||||
|
release_file_search=$(mktemp)
|
||||||
|
tracks_file_browse=$(mktemp)
|
||||||
|
tracks_file_search=$(mktemp)
|
||||||
|
|
||||||
|
# Export
|
||||||
|
export ROOT MPV_SOCKET \
|
||||||
|
AFMT RFMT TFMT \
|
||||||
|
PLAYLIST_POINTER EMPTY_PLAYLIST_STRING \
|
||||||
|
SEARCH_PROMPT BROWSE_PROMPT_ROOT BROWSE_PROMPT_ARTIST BROWSE_PROMPT_RELEASE \
|
||||||
|
MODE_SEARCH MODE_BROWSE MODE_PLAYLIST \
|
||||||
|
artists_file_browse release_file_browse tracks_file_browse \
|
||||||
|
tracks_file_search release_file_search
|
||||||
|
|
||||||
|
awk \
|
||||||
|
-F'/' \
|
||||||
|
-v afmt="$AFMT" \
|
||||||
|
-v rfmt="$RFMT" \
|
||||||
|
-v tfmt="$TFMT" \
|
||||||
|
-v depth="$DEPTH" \
|
||||||
|
-v artists_file_browse="$artists_file_browse" \
|
||||||
|
-v release_file_browse="$release_file_browse" \
|
||||||
|
-v tracks_file_browse="$tracks_file_browse" \
|
||||||
|
-v release_file_search="$release_file_search" \
|
||||||
|
-v tracks_file_search="$tracks_file_search" \
|
||||||
|
-v mode_browse="$MODE_BROWSE" \
|
||||||
|
-v mode_search="$MODE_SEARCH" \
|
||||||
|
'BEGIN {
|
||||||
|
OFS="\t"
|
||||||
|
iartist = depth + 1
|
||||||
|
irelease = depth + 2
|
||||||
|
itrack = depth + 3
|
||||||
|
}
|
||||||
|
NF >= depth + 1 { ar = sprintf(afmt, $iartist) }
|
||||||
|
NF >= depth + 2 { rl = sprintf(rfmt, $irelease) }
|
||||||
|
NF >= depth + 3 { tr = $itrack; gsub(/\..*$/, "", tr); tr = sprintf(tfmt, tr) }
|
||||||
|
NF == depth + 1 { print ar, $0, mode_browse >> artists_file_browse }
|
||||||
|
NF == depth + 2 { print rl, $0, mode_browse >> release_file_browse }
|
||||||
|
NF == depth + 3 { print tr, $0, mode_browse >> tracks_file_browse }
|
||||||
|
NF == depth + 2 { print rl "|" ar, $0, mode_search >> release_file_search }
|
||||||
|
NF == depth + 3 { print tr "|" rl "|" ar, $0, mode_search >> tracks_file_search }
|
||||||
|
' <"$CACHE_FILE"
|
||||||
|
|
||||||
|
$MPV --no-config --no-terminal --input-ipc-server="$MPV_SOCKET" --idle --no-osc --no-input-default-bindings &
|
||||||
|
|
||||||
|
$FZF \
|
||||||
|
--reverse \
|
||||||
|
--ansi \
|
||||||
|
--delimiter="\t" \
|
||||||
|
--with-nth="{1}" \
|
||||||
|
--cycle \
|
||||||
|
--bind="ctrl-d:half-page-down,ctrl-u:half-page-up" \
|
||||||
|
--bind="down:preview-down,up:preview-up" \
|
||||||
|
--bind="ctrl-w:toggle-preview-wrap" \
|
||||||
|
--preview-window="right,30%" \
|
||||||
|
--preview="$0 --preview {2}" \
|
||||||
|
--bind="alt-1:reload:cat \"$artists_file_browse\"" \
|
||||||
|
--bind="alt-2:reload:column -t -s '|' -E 0 \"$release_file_search\"" \
|
||||||
|
--bind="alt-3:reload:column -t -s '|' -E 0 \"$tracks_file_search\"" \
|
||||||
|
--bind="ctrl-p:reload:$0 --show-playlist" \
|
||||||
|
--bind="enter:execute-silent:$0 --mpv-play {2} {3}" \
|
||||||
|
--bind="alt-enter:execute-silent:$0 --mpv-queue {2}" \
|
||||||
|
--bind="l:execute-silent:$0 --mpv-next" \
|
||||||
|
--bind="h:execute-silent:$0 --mpv-prev" \
|
||||||
|
--bind="right:execute-silent:$0 --mpv-seekf" \
|
||||||
|
--bind="left:execute-silent:$0 --mpv-seekb" \
|
||||||
|
--bind="space:execute-silent:$0 --mpv-toggle-pause" \
|
||||||
|
--bind="q:abort" \
|
||||||
|
--bind="load:transform:
|
||||||
|
if [ {3} = \"$MODE_PLAYLIST\" ]; then
|
||||||
|
printf \"+hide-input\"
|
||||||
|
printf \"+show-preview\"
|
||||||
|
printf \"+rebind(l)\"
|
||||||
|
printf \"+rebind(h)\"
|
||||||
|
printf \"+rebind(right)\"
|
||||||
|
printf \"+rebind(left)\"
|
||||||
|
printf \"+rebind(space)\"
|
||||||
|
printf \"+rebind(q)\"
|
||||||
|
fi
|
||||||
|
if [ {3} = \"$MODE_SEARCH\" ]; then
|
||||||
|
printf \"+show-input\"
|
||||||
|
printf \"+hide-preview\"
|
||||||
|
printf \"+unbind(l)\"
|
||||||
|
printf \"+unbind(h)\"
|
||||||
|
printf \"+unbind(right)\"
|
||||||
|
printf \"+unbind(left)\"
|
||||||
|
printf \"+unbind(space)\"
|
||||||
|
printf \"+unbind(q)\"
|
||||||
|
fi
|
||||||
|
if [ {3} = \"$MODE_BROWSE\" ]; then
|
||||||
|
printf \"+show-input\"
|
||||||
|
printf \"+show-preview\"
|
||||||
|
printf \"+unbind(l)\"
|
||||||
|
printf \"+unbind(h)\"
|
||||||
|
printf \"+unbind(right)\"
|
||||||
|
printf \"+unbind(left)\"
|
||||||
|
printf \"+unbind(space)\"
|
||||||
|
printf \"+unbind(q)\"
|
||||||
|
fi
|
||||||
|
printf \"+transform-prompt:$0 --prompt {2} {3}\"
|
||||||
|
" \
|
||||||
|
--bind="ctrl-l:reload($0 --browse l {2})+clear-query+pos(1)" \
|
||||||
|
--bind="ctrl-h:reload($0 --browse h {2})+clear-query+pos(1)" \
|
||||||
|
<"$artists_file_browse" || true
|
||||||
|
|
||||||
|
printf '{ "command": ["quit"] }\n' | socat - "$MPV_SOCKET"
|
||||||
|
rm -f "$MPV_SOCKET" \
|
||||||
|
"$artists_file_browse" \
|
||||||
|
"$release_file_browse" \
|
||||||
|
"$release_file_search" \
|
||||||
|
"$tracks_file_browse" \
|
||||||
|
"$tracks_file_search"
|
188
fuzzique
188
fuzzique
@@ -1,188 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
if [ "${1:-}" = "--info" ]; then
|
|
||||||
shift
|
|
||||||
file="$1"
|
|
||||||
rel=${file#"$ROOT/"}
|
|
||||||
artist=$(echo "$rel" | cut -d "/" -f 1)
|
|
||||||
release=$(echo "$rel" | cut -d "/" -f 2)
|
|
||||||
d=$(echo "$rel" | awk -F'/' '{ print NF }')
|
|
||||||
[ "$d" -eq 2 ] && printf ">> $AFMT" "$artist"
|
|
||||||
[ "$d" -eq 3 ] && printf ">> $AFMT >> $RFMT" "$artist" "$release"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${1:-}" = "--preview" ]; then
|
|
||||||
shift
|
|
||||||
file="$1"
|
|
||||||
tags=$(ffprobe -v quiet -show_entries format -of json "$file" | jq)
|
|
||||||
{
|
|
||||||
echo "## Lyrics"
|
|
||||||
echo ""
|
|
||||||
printf "%s" "$tags" | jq -C -r '.format.tags.LYRICS'
|
|
||||||
} | batcat --color always --number --language md
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${1:-}" = "--show-playlist" ]; then
|
|
||||||
[ ! -S "${MPV_SOCKET:-}" ] && exit 1
|
|
||||||
count=$(echo "{ \"command\": [\"expand-text\",\"\${playlist/count}\"] }" | socat - "$MPV_SOCKET" | jq -r '.data')
|
|
||||||
for i in $(seq 0 $((count - 1))); do
|
|
||||||
file=$(echo "{ \"command\": [\"expand-text\",\"\${playlist/$i/filename}\"] }" | socat - "$MPV_SOCKET" | jq -r '.data')
|
|
||||||
curr=$(echo "{ \"command\": [\"expand-text\",\"\${playlist/$i/current}\"] }" | socat - "$MPV_SOCKET" | jq -r '.data')
|
|
||||||
pnt=" "
|
|
||||||
[ "$curr" = "yes" ] && pnt="👉"
|
|
||||||
artist=$(echo "$file" | rev | cut -d "/" -f 3 | rev)
|
|
||||||
release=$(echo "$file" | rev | cut -d "/" -f 2 | rev)
|
|
||||||
title=$(echo "$file" | rev | cut -d "/" -f 1 | rev | sed 's/\..*$//')
|
|
||||||
artist=$(printf "$AFMT" "$artist")
|
|
||||||
release=$(printf "$RFMT" "$release")
|
|
||||||
title=$(printf "$TFMT" "$title")
|
|
||||||
printf "%s|%s|%s|%s\t%s\n" "$pnt" "$title" "$release" "$artist" "$file"
|
|
||||||
done | grep '.' | column -t -s '|'
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v "bat" >/dev/null; then
|
|
||||||
CAT="bat"
|
|
||||||
elif command -v "batcat" >/dev/null; then
|
|
||||||
CAT="batcat"
|
|
||||||
fi
|
|
||||||
CAT=${CAT:+$CAT --color=always --style=plain --language=md}
|
|
||||||
CAT=${CAT:-cat}
|
|
||||||
export CAT
|
|
||||||
|
|
||||||
if [ "${1:-}" = "--help" ]; then
|
|
||||||
$CAT <<EOF
|
|
||||||
Usage: \`$0 [ --help | [ MUSIC_DIRECTORY [ --reset-cache ] ] ]\`
|
|
||||||
|
|
||||||
The \`MUSIC_DIRECTORY\` is the path to a folder with the following tree
|
|
||||||
structure: \`./<artist>/<album>/<track>\`. For instance:
|
|
||||||
\`\`\`sh
|
|
||||||
$ tree /media/Musik
|
|
||||||
/media/Musik/Mos Def
|
|
||||||
└── Black on Both Sides
|
|
||||||
├── 01 Fear Not of Man.m4a
|
|
||||||
├── 02 Hip Hop.m4a
|
|
||||||
├── 03 Love.m4a
|
|
||||||
├── 04 Ms. Fat Booty.m4a
|
|
||||||
├── 05 Speed Law.m4a
|
|
||||||
├── 06 Do It Now.m4a
|
|
||||||
├── 07 Got.m4a
|
|
||||||
├── 08 Umi Says.m4a
|
|
||||||
├── 09 New World Water.m4a
|
|
||||||
├── 10 Rock n Roll.m4a
|
|
||||||
├── 11 Know That.m4a
|
|
||||||
├── 12 Climb.m4a
|
|
||||||
├── 13 Brooklyn.m4a
|
|
||||||
├── 14 Habitat.m4a
|
|
||||||
├── 15 Mr. Nigga.m4a
|
|
||||||
├── 16 Mathematics.m4a
|
|
||||||
├── 17 May‐December.m4a
|
|
||||||
└── cover.jpg
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
[OPTIONS]
|
|
||||||
\`--help\`: Show this help and exit
|
|
||||||
\`--reset-cache\`: Reset cache for specified \`MUSIC_DIRECTORY\`
|
|
||||||
EOF
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ "${1:-}" ] && ROOT="$1" && shift
|
|
||||||
[ ! -d "${ROOT:-}" ] && echo "Faild to recognize music directory. Run $0 --help." && exit 1
|
|
||||||
ROOT=${ROOT%%/}
|
|
||||||
CACHE="$HOME/.cache/fuzzique"
|
|
||||||
ROOTHASH=$(echo "$ROOT" | sha1sum | cut -d ' ' -f 1)
|
|
||||||
CACHE="$CACHE/$ROOTHASH"
|
|
||||||
DEPTH=$(echo "$ROOT" | awk -F'/' '{ print NF }')
|
|
||||||
FD=/usr/bin/fdfind
|
|
||||||
FZF=fzf
|
|
||||||
MPV=mpv
|
|
||||||
MPV_SOCKET="$(mktemp --suffix=.sock)"
|
|
||||||
ARTIST_COLOR='\033[38;5;202m'
|
|
||||||
RELEASE_COLOR='\033[38;5;208m'
|
|
||||||
TRACK_COLOR='\033[38;5;215m'
|
|
||||||
OFF="\033[m"
|
|
||||||
AFMT="🎤 ${ARTIST_COLOR}%s${OFF}"
|
|
||||||
RFMT="💽 ${RELEASE_COLOR}%s${OFF}"
|
|
||||||
TFMT="🎵 ${TRACK_COLOR}%s${OFF}"
|
|
||||||
export ROOT MPV_SOCKET AFMT RFMT TFMT
|
|
||||||
|
|
||||||
# Load
|
|
||||||
artists_file="$CACHE/artists"
|
|
||||||
release_file="$CACHE/release"
|
|
||||||
tracks_file="$CACHE/tracks"
|
|
||||||
if [ "${1:-}" = "--build-db" ] || [ ! -f "$artists_file" ]; then
|
|
||||||
rm -rf "$CACHE"
|
|
||||||
mkdir -p "$CACHE"
|
|
||||||
[ ! -d "$CACHE" ] && echo "Failed to create cache directory" && exit 1
|
|
||||||
{
|
|
||||||
$FD --max-depth 2 --type d . "$ROOT"
|
|
||||||
$FD --exact-depth 3 --type f -i -e "mp3" -e "mp4" -e "m4a" -e "ogg" -e "flac" -e "wav" . "$ROOT"
|
|
||||||
} |
|
|
||||||
sort |
|
|
||||||
awk \
|
|
||||||
-F'/' \
|
|
||||||
-v afmt="$AFMT" \
|
|
||||||
-v rfmt="$RFMT" \
|
|
||||||
-v tfmt="$TFMT" \
|
|
||||||
-v depth="$DEPTH" \
|
|
||||||
-v artists_file="$artists_file" \
|
|
||||||
-v release_file="$release_file" \
|
|
||||||
-v tracks_file="$tracks_file" \
|
|
||||||
'BEGIN {
|
|
||||||
OFS="\t"
|
|
||||||
iartist = depth + 1
|
|
||||||
irelease = depth + 2
|
|
||||||
itrack = depth + 3
|
|
||||||
}
|
|
||||||
NF >= depth + 1 { ar = sprintf(afmt, $iartist) }
|
|
||||||
NF >= depth + 2 { rl = sprintf(rfmt, $irelease) }
|
|
||||||
NF >= depth + 3 { tr = $itrack; gsub(/\..*$/, "", tr); tr = sprintf(tfmt, tr) }
|
|
||||||
NF == depth + 1 { print ar, $0 >> artists_file }
|
|
||||||
NF == depth + 2 { print rl, $0 >> release_file }
|
|
||||||
NF == depth + 3 { print tr, $0 >> tracks_file }
|
|
||||||
'
|
|
||||||
# NF == depth + 1 { print ar, $0 >> artists_file }
|
|
||||||
# NF == depth + 2 { print rl "|" ar, $0 >> release_file }
|
|
||||||
# NF == depth + 3 { print tr "|" rl "|" ar, $0 >> tracks_file }
|
|
||||||
fi
|
|
||||||
|
|
||||||
$MPV --no-config --no-terminal --input-ipc-server="$MPV_SOCKET" --idle &
|
|
||||||
|
|
||||||
$FZF \
|
|
||||||
--reverse \
|
|
||||||
--ansi \
|
|
||||||
--no-sort \
|
|
||||||
--delimiter="\t" \
|
|
||||||
--with-nth="{1}" \
|
|
||||||
--cycle \
|
|
||||||
--info=inline-right \
|
|
||||||
--info-command="$0 --info {2}" \
|
|
||||||
--bind="ctrl-d:half-page-down,ctrl-u:half-page-up" \
|
|
||||||
--bind="enter:execute:printf '{ \"command\": [\"loadfile\", \"%s\"] }\n' {2} | socat - \"$MPV_SOCKET\"" \
|
|
||||||
--bind="alt-enter:execute:printf '{ \"command\": [\"loadfile\", \"%s\", \"append-play\"] }\n' {2} | socat - \"$MPV_SOCKET\"" \
|
|
||||||
--bind="alt-t:show-input+reload:cat \"$tracks_file\" | column -t -s '|' -E 0" \
|
|
||||||
--bind="alt-r:show-input+reload:cat \"$release_file\" | column -t -s '|'" \
|
|
||||||
--bind="alt-a:show-input+reload:cat \"$artists_file\" | column -t -s '|'" \
|
|
||||||
--bind="ctrl-l:show-input+transform:
|
|
||||||
d=\$(echo {2} | awk -F'/' '{ print NF }')
|
|
||||||
[ \"\$d\" -eq \"$((DEPTH + 1))\" ] && echo \"reload:grep -F {2}/ \\\"$release_file\\\" | column -t -s '|' || true\"
|
|
||||||
[ \"\$d\" -eq \"$((DEPTH + 2))\" ] && echo \"reload:grep -F {2}/ \\\"$tracks_file\\\" | column -t -s '|' || true\"
|
|
||||||
" \
|
|
||||||
--bind="ctrl-h:show-input+transform:
|
|
||||||
d=\$(echo {2} | awk -F'/' '{ print NF }')
|
|
||||||
p=\$(echo {2} | rev | cut -d '/' -f 3- | rev)
|
|
||||||
[ \"\$d\" -eq \"$((DEPTH + 3))\" ] && echo \"reload:grep -F \\\"\$p/\\\" \\\"$release_file\\\" | column -t -s '|' || true\"
|
|
||||||
[ \"\$d\" -eq \"$((DEPTH + 2))\" ] && echo \"reload:grep -F \\\"\$p/\\\" \\\"$artists_file\\\" | column -t -s '|' || true\"
|
|
||||||
" \
|
|
||||||
--bind="ctrl-p:hide-input+reload:$0 --show-playlist" \
|
|
||||||
--preview="$0 --preview {2}" \
|
|
||||||
--bind="alt-/:toggle-preview" \
|
|
||||||
<"$artists_file" || true
|
|
||||||
|
|
||||||
printf '{ "command": ["quit"] }\n' | socat - "$MPV_SOCKET"
|
|
||||||
rm -f "$MPV_SOCKET"
|
|
Reference in New Issue
Block a user