diff --git a/bin/mpvctl b/bin/mpvctl index 41bc722..c59f19b 100755 --- a/bin/mpvctl +++ b/bin/mpvctl @@ -18,26 +18,26 @@ SOCKET=/tmp/mpv.sock # Error function _die() { - printf '[Error] %s\n' "$@" >&2 - exit 1 + printf '[Error] %s\n' "$@" >&2 + exit 1 } # Ensure dependencies _checkDep() { - type mpv > /dev/null 2>&1 || _die "Cannot find mpv in your \$PATH" - type yt-dlp > /dev/null 2>&1 || _die "Cannot find yt-dlp in your \$PATH" - type nc > /dev/null 2>&1 && SOCKCMD="nc -U -N $SOCKET" - type socat > /dev/null 2>&1 && SOCKCMD="socat - $SOCKET" - [ "$SOCKCMD" ] || _die "Cannot find socat or nc in your \$PATH. Install before continue" + type mpv > /dev/null 2>&1 || _die "Cannot find mpv in your \$PATH" + type yt-dlp > /dev/null 2>&1 || _die "Cannot find yt-dlp in your \$PATH" + type nc > /dev/null 2>&1 && SOCKCMD="nc -U -N $SOCKET" + type socat > /dev/null 2>&1 && SOCKCMD="socat - $SOCKET" + [ "$SOCKCMD" ] || _die "Cannot find socat or nc in your \$PATH. Install before continue" } # Check if sock is idle, otherwise exit _checkSock() { - [[ -S $SOCKET ]] || _die "Cannot find mpv socket file" - local return_code - printf '{ "command:" [ "get_version" ] }' | $SOCKCMD &> /dev/null - return_code=$? - [[ "$return_code" -eq 0 ]] || _die "Cannot connect to mpv socket, connection refused" + [[ -S $SOCKET ]] || _die "Cannot find mpv socket file" + local return_code + printf '{ "command:" [ "get_version" ] }' | $SOCKCMD &> /dev/null + return_code=$? + [[ "$return_code" -eq 0 ]] || _die "Cannot connect to mpv socket, connection refused" } # Usage (help) message @@ -71,172 +71,172 @@ EOF # Check sock status, only for information _getSock() { - if [ ! -S $SOCKET ]; then - printf 'disabled\n' - exit 0 - fi - printf '{ "command:" [ "get_version" ] }' | $SOCKCMD &> /dev/null - local return_code=$? - if [ ! "$return_code" -eq 0 ]; then - printf 'disabled\n' - exit 0 - fi - printf 'enabled\n' - exit 0 + if [ ! -S $SOCKET ]; then + printf 'disabled\n' + exit 0 + fi + printf '{ "command:" [ "get_version" ] }' | $SOCKCMD &> /dev/null + local return_code=$? + if [ ! "$return_code" -eq 0 ]; then + printf 'disabled\n' + exit 0 + fi + printf 'enabled\n' + exit 0 } # Get current playlist formatted and sorted _getPlaylist() { - _checkSock - # track numbers - local trnum - trnum=$(_getProperty 'get_property_string' 'playlist-count') - # current track number - local trcur - trcur=$(_getProperty 'get_property_string' 'playlist-pos') + _checkSock + # track numbers + local trnum + trnum=$(_getProperty 'get_property_string' 'playlist-count') + # current track number + local trcur + trcur=$(_getProperty 'get_property_string' 'playlist-pos') - local count - count=0 - while [ "$count" -lt "$trnum" ]; do - # current track mark - local trcurmark - trcurmark=' ' - # track name - local trname - local trcurmark - trname=$(_getProperty 'get_property_string' "playlist/$count/filename") - if [ "$count" -eq "$trcur" ]; then trcurmark='*'; fi - # check if local or yt media - if [[ $trname =~ ^ytdl://(.*)$ ]]; then - # track id - local trid - trid=${trname:7} - # escape quotes - trid=$(printf '%s' "$trid" | sed "s/'/''/g") - # if cache db given, search yt media using it - if [ -n "$1" ] && [ -f "$1" ]; then - local db - db=$1; - # search for track title - local trtitle - trtitle="$(sqlite3 "${db}" \ + local count + count=0 + while [ "$count" -lt "$trnum" ]; do + # current track mark + local trcurmark + trcurmark=' ' + # track name + local trname + local trcurmark + trname=$(_getProperty 'get_property_string' "playlist/$count/filename") + if [ "$count" -eq "$trcur" ]; then trcurmark='*'; fi + # check if local or yt media + if [[ $trname =~ ^ytdl://(.*)$ ]]; then + # track id + local trid + trid=${trname:7} + # escape quotes + trid=$(printf '%s' "$trid" | sed "s/'/''/g") + # if cache db given, search yt media using it + if [ -n "$1" ] && [ -f "$1" ]; then + local db + db=$1; + # search for track title + local trtitle + trtitle="$(sqlite3 "${db}" \ "select distinct title from main where id='${trid}'" 2> /dev/null)" + if [ -z "$trtitle" ]; then + local trtitle + trtitle=$(yt-dlp --get-filename "$trid" -o "%(title)s" 2> /dev/null) + # cache this single search as relative to a NULL query + sqlite3 "${db}" \ + "insert into main (query,id,title) values ('NULL','${trid}','${trtitle}')" \ + 2> /dev/null + fi + else + # searching track title, using ytdl + local trtitle + trtitle=$(yt-dlp --get-filename "$trid" -o "%(title)s" 2> /dev/null) + fi if [ -z "$trtitle" ]; then - local trtitle - trtitle=$(yt-dlp --get-filename "$trid" -o "%(title)s" 2> /dev/null) - # cache this single search as relative to a NULL query - sqlite3 "${db}" \ - "insert into main (query,id,title) values ('NULL','${trid}','${trtitle}')" \ - 2> /dev/null + printf "[Warning] yt-dlp title search fail\n" >&2 + local trtitle + trtitle="NULL" fi - else - # searching track title, using ytdl - local trtitle - trtitle=$(yt-dlp --get-filename "$trid" -o "%(title)s" 2> /dev/null) - fi - if [ -z "$trtitle" ]; then - printf "[Warning] yt-dlp title search fail\n" >&2 + else + # other media file type, not yt url local trtitle - trtitle="NULL" - fi - else - # other media file type, not yt url - local trtitle - trtitle=$trname - fi + trtitle=$trname + fi - local zerocount - zerocount=$(printf '%s\n' "$((count+1))" | sed 's/\<[0-9]\>/0&/') - printf '%s)%s %s\n' "$zerocount" "$trcurmark" "$trtitle" - count=$((count + 1)) - done + local zerocount + zerocount=$(printf '%s\n' "$((count+1))" | sed 's/\<[0-9]\>/0&/') + printf '%s)%s %s\n' "$zerocount" "$trcurmark" "$trtitle" + count=$((count + 1)) + done } # Save current playlist to given file _savePlaylist() { - _checkSock - [[ -n "$1" ]] || _die 'None path given' - [[ -d "$(dirname "$1")" ]] || _die 'Invalid path given' - # track numbers - local trnum - trnum=$(_getProperty 'get_property_string' 'playlist-count') - local count - count=0 - while [ "$count" -lt "$trnum" ]; do - # track name - local trname - trname=$(_getProperty 'get_property_string' "playlist/$count/filename") - printf '%s\n' "$trname" - count=$((count + 1)) - done > "$1" + _checkSock + [[ -n "$1" ]] || _die 'None path given' + [[ -d "$(dirname "$1")" ]] || _die 'Invalid path given' + # track numbers + local trnum + trnum=$(_getProperty 'get_property_string' 'playlist-count') + local count + count=0 + while [ "$count" -lt "$trnum" ]; do + # track name + local trname + trname=$(_getProperty 'get_property_string' "playlist/$count/filename") + printf '%s\n' "$trname" + count=$((count + 1)) + done > "$1" } # Load playlist from given file _loadPlaylist() { - [[ -n "$1" ]] || _die 'None path given' - [[ -f "$1" ]] || _die 'Invalid path given' - for track in $(<"$1"); do - _setProperty 'loadfile' "$track" 'append-play' - done + [[ -n "$1" ]] || _die 'None path given' + [[ -f "$1" ]] || _die 'Invalid path given' + for track in $(<"$1"); do + _setProperty 'loadfile' "$track" 'append-play' + done } # Get loop status _getLoop() { - #loop state - local lstate - lstate=$(_getProperty 'get_property_string' 'loop-playlist' \ - | sed "s/inf/on/" | sed "s/no/off/") - if [ -z "$lstate" ]; then - exit 1 - fi - printf '%s\n' "$lstate" + #loop state + local lstate + lstate=$(_getProperty 'get_property_string' 'loop-playlist' \ + | sed "s/inf/on/" | sed "s/no/off/") + if [ -z "$lstate" ]; then + exit 1 + fi + printf '%s\n' "$lstate" } # Toggle loop playlist _toggleLoop() { - local lstate - lstate=$(_getLoop) - if [ -z "$lstate" ]; then - exit 1 - fi - if [ "$lstate" == "off" ]; then - _setProperty 'set_property' 'loop-playlist' 'inf'; - else - _setProperty 'set_property' 'loop-playlist' 'no'; - fi + local lstate + lstate=$(_getLoop) + if [ -z "$lstate" ]; then + exit 1 + fi + if [ "$lstate" == "off" ]; then + _setProperty 'set_property' 'loop-playlist' 'inf'; + else + _setProperty 'set_property' 'loop-playlist' 'no'; + fi } # Get method to read from socket _getProperty() { - _checkSock - local tosend - tosend='{ "command": [' - for arg in "$@"; do - tosend="$tosend \"$arg\"," - done - tosend=${tosend%?}' ] }' - local property - property=$(printf '%s\n' "$tosend" | $SOCKCMD 2> /dev/null \ - | cut -d'"' -f 4 | rev | cut -d'.' -f 2- | rev) - printf '%s\n' "$property" + _checkSock + local tosend + tosend='{ "command": [' + for arg in "$@"; do + tosend="$tosend \"$arg\"," + done + tosend=${tosend%?}' ] }' + local property + property=$(printf '%s\n' "$tosend" | $SOCKCMD 2> /dev/null \ + | cut -d'"' -f 4 | rev | cut -d'.' -f 2- | rev) + printf '%s\n' "$property" } # Set method to write from socket _setProperty() { - _checkSock - local tosend - tosend='{ "command": [' - for arg in "$@"; do - tosend="$tosend \"$arg\"," - done - tosend=${tosend%?}' ] }' - printf '%s\n' "$tosend" | $SOCKCMD &> /dev/null + _checkSock + local tosend + tosend='{ "command": [' + for arg in "$@"; do + tosend="$tosend \"$arg\"," + done + tosend=${tosend%?}' ] }' + printf '%s\n' "$tosend" | $SOCKCMD &> /dev/null } # Parse optional argument case "$1" in - -s | --socket) shift; [ -n "$1" ] && SOCKET="$1"; shift;; + -s | --socket) shift; [ -n "$1" ] && SOCKET="$1"; shift ;; esac # Check deps @@ -244,20 +244,20 @@ _checkDep # Parse positional argument case "$1" in - add) shift; for track in "$@"; do _setProperty 'loadfile' "$track" 'append-play'; done;; - check) _getSock;; - clear) _setProperty 'playlist_clear';; - load) _loadPlaylist "$2";; - loop) _toggleLoop;; - loop-status) _getLoop;; - next) _setProperty 'playlist_next';; - playlist) _getPlaylist "$2" "$3";; - prev) _setProperty 'playlist_prev';; - rm) _setProperty 'playlist_remove' "$2";; - save) _savePlaylist "$2";; - stop) _setProperty 'quit';; - toggle) _setProperty 'cycle' 'pause';; - track) _setProperty 'set_property' 'playlist-pos' "$2";; - -h | --help) shift; _usage; exit 0;; - *) shift; _usage; exit 1;; + add) shift; for track in "$@"; do _setProperty 'loadfile' "$track" 'append-play'; done ;; + check) _getSock ;; + clear) _setProperty 'playlist_clear' ;; + load) _loadPlaylist "$2" ;; + loop) _toggleLoop ;; + loop-status) _getLoop ;; + next) _setProperty 'playlist_next' ;; + playlist) _getPlaylist "$2" "$3" ;; + prev) _setProperty 'playlist_prev' ;; + rm) _setProperty 'playlist_remove' "$2" ;; + save) _savePlaylist "$2" ;; + stop) _setProperty 'quit' ;; + toggle) _setProperty 'cycle' 'pause' ;; + track) _setProperty 'set_property' 'playlist-pos' "$2" ;; + -h | --help) shift; _usage; exit 0 ;; + *) shift; _usage; exit 1 ;; esac diff --git a/bin/ytdl-mpv b/bin/ytdl-mpv index 881bd26..90b8c73 100755 --- a/bin/ytdl-mpv +++ b/bin/ytdl-mpv @@ -15,47 +15,47 @@ VERSION="v0.4.0" # Default functions _rofi() { - rofi -dmenu -no-auto-select -i -p "$PROMPT" "$@" + rofi -dmenu -no-auto-select -i -p "$PROMPT" "$@" } _rofi_error() { - rofi -e "$1" + rofi -e "$1" } _copyId() { - if [ "$XCLIP" ]; then - printf '%s' "$1" | xclip -i -selection "clipboard" - _info "Copy in clipboard ... ${1:7}" - fi + if [ "$XCLIP" ]; then + printf '%s' "$1" | xclip -i -selection "clipboard" + _info "Copy in clipboard ... ${1:7}" + fi } _ytdl_mpvctl() { - mpvctl --socket "${SOCKET}" "$@" + mpvctl --socket "${SOCKET}" "$@" } _playAudio() { - mpv --no-terminal --input-ipc-server="${SOCKET}" --ytdl-format=bestaudio "$1" &> /dev/null & - _info "Audio playback ... ${1:7}" + mpv --no-terminal --input-ipc-server="${SOCKET}" --ytdl-format=bestaudio "$1" &> /dev/null & + _info "Audio playback ... ${1:7}" } _playVideo() { - mpv --input-ipc-server="$SOCKET" \ - --ytdl-format="bestvideo[height<=?720][fps<=?30][vcodec!=?vp9]+bestaudio/best" "$1" &> /dev/null & - _info "Video playback ... ${1:7}" + mpv --input-ipc-server="$SOCKET" \ + --ytdl-format="bestvideo[height<=?720][fps<=?30][vcodec!=?vp9]+bestaudio/best" "$1" &> /dev/null & + _info "Video playback ... ${1:7}" } _appendTrack() { - _ytdl_mpvctl add "$1" && _info "Add track to cur playlist ... ${1:7}" + _ytdl_mpvctl add "$1" && _info "Add track to cur playlist ... ${1:7}" } _savePlaylist() { - _ytdl_mpvctl save "$1" && _info "Current playlist saved to ... \"$1\"" + _ytdl_mpvctl save "$1" && _info "Current playlist saved to ... \"$1\"" } _loadPlaylist() { - _ytdl_mpvctl load "$1" && _info "Playlist loaded from ... \"$1\"" + _ytdl_mpvctl load "$1" && _info "Playlist loaded from ... \"$1\"" } _flushCache() { - rm -fr "$CACHEDIR" && _info "ytdl-mpv cache flushed" + rm -fr "$CACHEDIR" && _info "ytdl-mpv cache flushed" } _flushHist() { - rm -fr "$HISTORY" && _info "ytdl-mpv search history flushed" + rm -fr "$HISTORY" && _info "ytdl-mpv search history flushed" } _helpPlay() { - local STYLE="window {width: 32%;} listview {lines: 6;}" - cat << EOF | \ + local STYLE="window {width: 32%;} listview {lines: 6;}" + cat << EOF | \ _rofi -theme-str "$STYLE" -mesg "-- play menu key bindings --" > /dev/null [Enter] | Default action [${play_audio}] | Start audio playback @@ -66,8 +66,8 @@ _helpPlay() { EOF } _helpSearch() { - local STYLE="window {width: 30%;} listview {lines: 5;}" - cat << EOF | \ + local STYLE="window {width: 30%;} listview {lines: 5;}" + cat << EOF | \ _rofi -theme-str "$STYLE" -mesg "-- search menu key bindings --" > /dev/null [Enter] | Search item [${re_cache}] | Recache item @@ -75,8 +75,8 @@ _helpSearch() { EOF } _helpEdit() { - local STYLE="window {width: 32%;} listview {lines: 5;}" - cat << EOF | \ + local STYLE="window {width: 32%;} listview {lines: 5;}" + cat << EOF | \ _rofi -theme-str "$STYLE" -mesg "-- edit menu key bindings --" > /dev/null [Enter] | Play playlist item [${remove_track}] | Remove playlist item @@ -111,41 +111,41 @@ XCLIP=1 # Error function _die() { - local err_msg="[Error] $*" - _rofi_error "$err_msg" - printf '%s\n' "$err_msg" >&2 - exit 1 + local err_msg="[Error] $*" + _rofi_error "$err_msg" + printf '%s\n' "$err_msg" >&2 + exit 1 } # Info function _info() { - printf '[Info] %s\n' "$@" + printf '[Info] %s\n' "$@" } # Ensure dependencies _checkDep() { - local deps - deps=(mpv mpvctl nc rofi sqlite3 yt-dlp xargs xclip) - for dep in "${deps[@]}"; do - type "$dep" > /dev/null 2>&1 || { - if [ "$dep" == "xclip" ]; then - XCLIP=0 - else - _die "Cannot find ${dep} in your \$PATH" - fi - } - done + local deps + deps=(mpv mpvctl nc rofi sqlite3 yt-dlp xargs xclip) + for dep in "${deps[@]}"; do + type "$dep" > /dev/null 2>&1 || { + if [ "$dep" == "xclip" ]; then + XCLIP=0 + else + _die "Cannot find ${dep} in your \$PATH" + fi + } + done } # Ensure internet connection is on _checkCon() { - ping -c1 youtube.com &> /dev/null || _die "Unable to ping youtube.com, check your connection" + ping -c1 youtube.com &> /dev/null || _die "Unable to ping youtube.com, check your connection" } # Usage (help) message _usage() { cat >&2 << EOF -usage: $(basename "$0") [-h] [--number NUMBER] [--linen LINEN] [--socket SOCKET] [--width WIDTH] +usage: $(basename "$0") [-h] [-V] [--number NUMBER] [--linen LINEN] [--socket SOCKET] [--width WIDTH] YTDL-MPV - Browse and play yt contents from rofi using ytdl and mpv @@ -161,7 +161,7 @@ EOF # Cat ytdl-mpv main menu _getMainMenu() { - cat << EOF + cat << EOF < Exit aa) [ Add/play to cur playlist ] > ep) [ Edit cur playlist ] > @@ -180,358 +180,363 @@ EOF # Hash a string and encode it _hashStr() { - local hash - hash=$(printf '%s' "$1" | sha256sum | base64) - printf '%s' "${hash::19}" + local hash + hash=$(printf '%s' "$1" | sha256sum | base64) + printf '%s' "${hash::19}" } # Format and numbering plain file _getView() { - if [ -f "$1" ]; then - awk '{ print FNR ") " $0 }' < "$1" | sed 's/\<[0-9]\>/0&/' - elif [ -d "$1" ]; then - find "$1" -type f -name '*' -exec basename -a -- {} + \ - | sort | awk '{ print FNR ") " $0 }' | sed 's/\<[0-9]\>/0&/' - else - return - fi + if [ -f "$1" ]; then + awk '{ print FNR ") " $0 }' < "$1" | sed 's/\<[0-9]\>/0&/' + elif [ -d "$1" ]; then + find "$1" -type f -name '*' -exec basename -a -- {} + \ + | sort | awk '{ print FNR ") " $0 }' | sed 's/\<[0-9]\>/0&/' + else + return + fi } # Check if a query was cached inside a table _isCachedQuery() { - local query - query="$1" - if [ -f "$DB" ]; then - local count - count="$(sqlite3 "${DB}" \ + local query + query="$1" + if [ -f "$DB" ]; then + local count + count="$(sqlite3 "${DB}" \ "select count(*) from main where query='${query}'" 2> /dev/null)" - if [[ "$count" -gt 0 ]]; then printf "cached"; fi - fi + if [[ "$count" -gt 0 ]]; then printf "cached"; fi + fi } # Get a query that was cached inside main table # and stdout it formatted for rofi _getCachedQuery() { - local query - query="$1" - sqlite3 "${DB}" \ - "select title from main where query='${query}'" 2> /dev/null \ - | awk '{ print FNR ") " $0 }' | sed 's/\<[0-9]\>/0&/' + local query + query="$1" + sqlite3 "${DB}" \ + "select title from main where query='${query}'" 2> /dev/null \ + | awk '{ print FNR ") " $0 }' | sed 's/\<[0-9]\>/0&/' } # Get id of yt content from cached table _getCachedIdQuery() { - local query - local title - query="$1" - title="$2" - # escape quotes - title=$(printf '%s' "$title" | sed "s/'/''/g") - printf '%s' "$(sqlite3 "${DB}" \ + local query + local title + query="$1" + title="$2" + # escape quotes + title=$(printf '%s' "$title" | sed "s/'/''/g") + printf '%s' "$(sqlite3 "${DB}" \ "select id from main where query='${query}' and title='${title}'" 2> /dev/null)" } # Cache a query inside main table _cacheQuery() { - local query - query="$1" - # create main table and cache items - sqlite3 "${DB}" "create table if not exists main (query str, id str, title str, unique(title))" - sed "s/;//g" "$CACHEDIR/$query" | sed "s/\"/\'/g" | sed -E "N;s/(.*)\n(.*)/${query};\2;\1/" \ - | sqlite3 -separator ';' "${DB}" ".import /dev/stdin main" + local query + query="$1" + # create main table and cache items + sqlite3 "${DB}" "create table if not exists main (query str, id str, title str, unique(title))" + sed "s/;//g" "$CACHEDIR/$query" | sed "s/\"/\'/g" | sed -E "N;s/(.*)\n(.*)/${query};\2;\1/" \ + | sqlite3 -separator ';' "${DB}" ".import /dev/stdin main" } # Delete a cached query inside main table _deleteQuery() { - local query - query="$1" - sqlite3 "${DB}" "delete from main where query='${query}'" 2> /dev/null + local query + query="$1" + sqlite3 "${DB}" "delete from main where query='${query}'" 2> /dev/null } # ytdl-mpv main interactive menu _mainMenu() { - local action - local STYLE="window {width: 50%;} listview {lines: 13;}" - action="$(_getMainMenu \ + local action + local STYLE="window {width: 50%;} listview {lines: 13;}" + action="$(_getMainMenu \ | _rofi -theme-str "$STYLE" -no-custom -mesg "-- main menu: select an action --" \ | awk '{$1=tolower($1);print $1}')" - action="${action::2}" - case "$action" in - aa) _searchMenu;; - ep) _editMenu;; - fc) _flushCache;; - fh) _flushHist;; - ld) _loadMenu;; - lp) _ytdl_mpvctl loop;; - nx) _ytdl_mpvctl next;; - pc) _ytdl_mpvctl clear;; - pp) _ytdl_mpvctl toggle;; - pv) _ytdl_mpvctl prev;; - sp) _ytdl_mpvctl stop;; - sv) _saveMenu;; - \<) _info "Quitting"; exit 0;; - *) _info "Nothing selected"; exit 0;; - esac + action="${action::2}" + case "$action" in + aa) _searchMenu ;; + ep) _editMenu ;; + fc) _flushCache ;; + fh) _flushHist ;; + ld) _loadMenu ;; + lp) _ytdl_mpvctl loop ;; + nx) _ytdl_mpvctl next ;; + pc) _ytdl_mpvctl clear ;; + pp) _ytdl_mpvctl toggle ;; + pv) _ytdl_mpvctl prev ;; + sp) _ytdl_mpvctl stop ;; + sv) _saveMenu ;; + \<) _info "Quitting"; exit 0 ;; + *) _info "Nothing selected"; exit 0 ;; + esac } # Edit menu, # display playlist state, possible actions remove or select a track from playlist _editMenu() { - # check sock status using loop status command - _ytdl_mpvctl loop-status &> /dev/null || exit 1 - local args - local STYLE="window {width: ${WIDTH}%;} listview {lines: ${LINEN};}" - args=( -kb-custom-1 "${remove_track}" - -kb-custom-4 "${key_help}" - -kb-custom-5 "${key_return}" - -kb-accept-alt "${multi_select}" - -theme-str "$STYLE" - -multi-select - -no-custom - -mesg "-- loop [$(_ytdl_mpvctl loop-status)] -- edit menu: edit playlist, help [Alt+h] --" ) - # get current playlist - local pl - pl="$(_ytdl_mpvctl playlist "${DB}")" - # selected track - local rofi_exit - strs="$(printf '%s' "${pl}" | _rofi "${args[@]}")" - rofi_exit="$?" - # check if help requested - if [[ "${rofi_exit}" -eq 13 ]]; then - _helpEdit; _editMenu; - else - if [ -z "$strs" ]; then + # check sock status using loop status command + _ytdl_mpvctl loop-status &> /dev/null || exit 1 + local args + local STYLE="window {width: ${WIDTH}%;} listview {lines: ${LINEN};}" + args=( -kb-custom-1 "${remove_track}" + -kb-custom-4 "${key_help}" + -kb-custom-5 "${key_return}" + -kb-accept-alt "${multi_select}" + -theme-str "$STYLE" + -multi-select + -no-custom + -mesg "-- loop [$(_ytdl_mpvctl loop-status)] -- edit menu: edit playlist, help [Alt+h] --" ) + # get current playlist + local pl + pl="$(_ytdl_mpvctl playlist "${DB}")" + # selected track + local rofi_exit + strs="$(printf '%s' "${pl}" | _rofi "${args[@]}")" + rofi_exit="$?" + # check if help requested + if [[ "${rofi_exit}" -eq 13 ]]; then + _helpEdit; _editMenu; + else + if [ -z "$strs" ]; then _info "Nothing selected" exit 0 - elif [[ "${rofi_exit}" -eq 14 ]]; then + elif [[ "${rofi_exit}" -eq 14 ]]; then _info "Back to main menu" _mainMenu; return; - else + else local i local IFS i=1 IFS=$'\n' for str in $strs; do - local stn - # get track number - stn="$(printf '%s' "${str::2}" | sed 's/^0*//')" - case "${rofi_exit}" in - 0) _ytdl_mpvctl track "$((stn-i))"; return;; - 10) _ytdl_mpvctl rm "$((stn-i))";; - esac - i=$((i+1)) + local stn + # get track number + stn="$(printf '%s' "${str::2}" | sed 's/^0*//')" + case "${rofi_exit}" in + 0) _ytdl_mpvctl track "$((stn-i))"; return ;; + 10) _ytdl_mpvctl rm "$((stn-i))" ;; + esac + i=$((i+1)) done # recursive until explicit exit sleep $DELAY; _editMenu - fi - fi + fi + fi } # Save menu, # save the current playlist as text file _saveMenu() { - mkdir -p "$PLAYDIR" - # saved playlists - local saved - local rofi_exit - local STYLE="window {width: 50%;} listview {lines: 13;}" - saved="$(_getView "${PLAYDIR}" \ + mkdir -p "$PLAYDIR" + # saved playlists + local saved + local rofi_exit + local STYLE="window {width: 50%;} listview {lines: 13;}" + saved="$(_getView "${PLAYDIR}" \ | _rofi -theme-str "$STYLE" -kb-custom-5 "${key_return}" \ -mesg "-- save menu: save current playlist as --")" - rofi_exit="$?" - saved="$(printf '%s' "${saved}" | xargs | tr '[:upper:]' '[:lower:]')" - if [ -z "$saved" ]; then - _info "Nothing selected or searched" - exit 0 - elif [[ "${rofi_exit}" -eq 14 ]]; then - _info "Back to main menu" - _mainMenu; - else - # slice only selected items and not typed items - if [[ $saved =~ ^[0-9][0-9]\)\ (.*)$ ]]; then saved="${saved:4}"; fi; - _savePlaylist "$PLAYDIR/$saved" - fi + rofi_exit="$?" + saved="$(printf '%s' "${saved}" | xargs | tr '[:upper:]' '[:lower:]')" + if [ -z "$saved" ]; then + _info "Nothing selected or searched" + exit 0 + elif [[ "${rofi_exit}" -eq 14 ]]; then + _info "Back to main menu" + _mainMenu; + else + # slice only selected items and not typed items + if [[ $saved =~ ^[0-9][0-9]\)\ (.*)$ ]]; then saved="${saved:4}"; fi; + _savePlaylist "$PLAYDIR/$saved" + fi } # Load menu, # load a playlist from text file, only audio playback _loadMenu() { - # saved playlists - local saved - local rofi_exit - local STYLE="window {width: 50%;} listview {lines: 13;}" - saved="$(_getView "${PLAYDIR}" \ + # saved playlists + local saved + local rofi_exit + local STYLE="window {width: 50%;} listview {lines: 13;}" + saved="$(_getView "${PLAYDIR}" \ | _rofi -theme-str "$STYLE" -no-custom -kb-custom-5 "${key_return}" \ -mesg "-- load menu: load playlist for audio playback --")" - rofi_exit="$?" - saved="$(printf '%s' "${saved}" | xargs | tr '[:upper:]' '[:lower:]')" - if [ -z "$saved" ]; then - _info "Nothing selected or searched" - exit 0 - elif [[ "${rofi_exit}" -eq 14 ]]; then - _info "Back to main menu" - _mainMenu; - else - # slice only selected items and not typed items - if [[ $saved =~ ^[0-9][0-9]\)\ (.*)$ ]]; then saved="${saved:4}"; fi; - # check if ytdl socket is idle, if yes append instead play - if [ "$(_ytdl_mpvctl check)" == "disabled" ]; then - # check if playlist file exist - [[ -f "$PLAYDIR/$saved" ]] || _die "Invalid path given" - # selected track is the first one of the playlist - _playAudio "$(head -n1 "$PLAYDIR/$saved")"; sleep $DELAY; - # append remaining tracks - local rtracks - rtracks="$(tail -n $(( $(wc -l "$PLAYDIR/$saved" | awk '{print $1}') - 1 )) "$PLAYDIR/$saved")" - for rtrack in $rtracks; do - _appendTrack "$rtrack" - done - else - _loadPlaylist "$PLAYDIR/$saved"; - fi - fi + rofi_exit="$?" + saved="$(printf '%s' "${saved}" | xargs | tr '[:upper:]' '[:lower:]')" + if [ -z "$saved" ]; then + _info "Nothing selected or searched" + exit 0 + elif [[ "${rofi_exit}" -eq 14 ]]; then + _info "Back to main menu" + _mainMenu; + else + # slice only selected items and not typed items + if [[ $saved =~ ^[0-9][0-9]\)\ (.*)$ ]]; then saved="${saved:4}"; fi; + # check if ytdl socket is idle, if yes append instead play + if [ "$(_ytdl_mpvctl check)" == "disabled" ]; then + # check if playlist file exist + [[ -f "$PLAYDIR/$saved" ]] || _die "Invalid path given" + # selected track is the first one of the playlist + _playAudio "$(head -n1 "$PLAYDIR/$saved")"; sleep $DELAY; + # append remaining tracks + local rtracks + rtracks="$(tail -n $(( $(wc -l "$PLAYDIR/$saved" | awk '{print $1}') - 1 )) "$PLAYDIR/$saved")" + for rtrack in $rtracks; do + _appendTrack "$rtrack" + done + else + _loadPlaylist "$PLAYDIR/$saved"; + fi + fi } # Search menu, # select keywords from history, start a search _searchMenu() { - touch "$HISTORY" - local args - local rofi_exit - local STYLE="window {width: 50%;} listview {lines: 13;}" - args=( -kb-custom-1 "${re_cache}" - -kb-custom-4 "${key_help}" - -kb-custom-5 "${key_return}" - -theme-str "$STYLE" - -mesg "-- search menu: search something, help [Alt+h] --" ) - # select from history or type something - search="$(_getView "$HISTORY" | _rofi "${args[@]}")" - rofi_exit="$?" - # check if help requested - if [[ "${rofi_exit}" -eq 13 ]]; then - _helpSearch; _searchMenu; - else - # check if this search must be recached - if [ "${rofi_exit}" -eq 10 ]; then to_recache=1; else to_recache=0; fi - # trim white spaces and lower case - search="$(printf '%s' "$search" | xargs -0 | tr '[:upper:]' '[:lower:]')" - if [ -z "$search" ]; then - _info "Nothing selected or searched" - exit 0 - elif [[ "${rofi_exit}" -eq 14 ]]; then - _info "Back to main menu" - _mainMenu; - else - # slice only selected items and not typed items - if [[ $search =~ ^[0-9][0-9]\)\ (.*)$ ]]; then search="${search:4}"; fi; - # remove trailing spaces - printf '%s\n' "$search" | sed 's/[ \t]*$//' >> "$HISTORY" - # unique and sorted entries inside history - local new_hist - new_hist="$(sort -u "$HISTORY")" - printf '%s\n' "$new_hist" > "$HISTORY" - _info "Searching for ... ${search}" - _startPlay - fi - fi + touch "$HISTORY" + local args + local rofi_exit + local STYLE="window {width: 50%;} listview {lines: 13;}" + args=( -kb-custom-1 "${re_cache}" + -kb-custom-4 "${key_help}" + -kb-custom-5 "${key_return}" + -theme-str "$STYLE" + -mesg "-- search menu: search something, help [Alt+h] --" ) + # select from history or type something + search="$(_getView "$HISTORY" | _rofi "${args[@]}")" + rofi_exit="$?" + # check if help requested + if [[ "${rofi_exit}" -eq 13 ]]; then + _helpSearch; _searchMenu; + else + # check if this search must be recached + if [ "${rofi_exit}" -eq 10 ]; then to_recache=1; else to_recache=0; fi + # trim white spaces and lower case + search="$(printf '%s' "$search" | xargs -0 | tr '[:upper:]' '[:lower:]')" + if [ -z "$search" ]; then + _info "Nothing selected or searched" + exit 0 + elif [[ "${rofi_exit}" -eq 14 ]]; then + _info "Back to main menu" + _mainMenu; + else + # slice only selected items and not typed items + if [[ $search =~ ^[0-9][0-9]\)\ (.*)$ ]]; then search="${search:4}"; fi; + # remove trailing spaces + printf '%s\n' "$search" | sed 's/[ \t]*$//' >> "$HISTORY" + # unique and sorted entries inside history + local new_hist + new_hist="$(sort -u "$HISTORY")" + printf '%s\n' "$new_hist" > "$HISTORY" + _info "Searching for ... ${search}" + _startPlay + fi + fi } # Start ytdl search using keywords, and then start/append to playback _startPlay() { - # yt-dlp search - local query - query="$(_hashStr "${search}:${NUMBER}")" - mkdir -p "$CACHEDIR" - # if not cached or marked as to_recache - # search it and cache it - local cache - cache="$(_isCachedQuery "$query")" - if [ -z "$cache" ] || [ "$to_recache" -eq 1 ]; then - if [ "$to_recache" -eq 1 ]; then _deleteQuery "$query"; fi - yt-dlp --default-search \ - ytsearch"$NUMBER" "$search" --get-id --get-title \ - 2> /dev/null > "$CACHEDIR/$query" & - wait "$!"; yt_dlp_exit="$?" - [[ "$yt_dlp_exit" -eq 0 ]] || _die "yt-dlp search fail, exit code ${yt_dlp_exit}" - _cacheQuery "$query" 2> /dev/null - rm -f "$CACHEDIR/$query" - fi - # check if ytdl-mpv is already running, if yes append track to playlist - local args - local STYLE="window {width: ${WIDTH}%;} listview {lines: ${LINEN};}" - if [ "$(_ytdl_mpvctl check)" == "disabled" ]; then - args=( -kb-custom-1 "${play_audio}" - -kb-custom-2 "${play_video}" - -kb-custom-3 "${copy_id}" - -kb-custom-4 "${key_help}" - -kb-custom-5 "${key_return}" - -kb-accept-alt "${multi_select}" - -theme-str "$STYLE" - -multi-select - -no-custom - -mesg "-- play menu: start audio or video playback, help [Alt+h] --" ) - else - args=( -kb-custom-5 "${key_return}" - -kb-accept-alt "${multi_select}" - -theme-str "$STYLE" - -multi-select - -no-custom - -mesg "-- play menu: add track to current playlist, simply [Enter] --" ) - fi - # selected track - local strack - local rofi_exit - stracks="$(_getCachedQuery "$query" | _rofi "${args[@]}")" - rofi_exit="$?" - # check if help requested - if [[ "${rofi_exit}" -eq 13 ]]; then - _helpPlay; _startPlay; - else - if [ -z "$stracks" ]; then - _info "Nothing selected" - exit 0 - elif [[ "${rofi_exit}" -eq 14 ]]; then - _info "Back to search menu" - _searchMenu; return; - else - local IFS - IFS=$'\n' - for strack in $stracks; do - strack="${strack:4}" - local id - id="ytdl://$(_getCachedIdQuery "$query" "$strack")" - # check if ytdl socket is idle, if yes append instead play - if [ "$(_ytdl_mpvctl check)" == "disabled" ]; then - case "${rofi_exit}" in - 0) "${default_do}" "$id";; - 10) _playAudio "$id";; - 11) _playVideo "$id";; - 12) _copyId "$id"; return;; - esac - sleep 1; - else - _appendTrack "$id"; - fi - done - # recursive until explicit exit - _searchMenu - fi - fi + # yt-dlp search + local query + query="$(_hashStr "${search}:${NUMBER}")" + mkdir -p "$CACHEDIR" + # if not cached or marked as to_recache + # search it and cache it + local cache + cache="$(_isCachedQuery "$query")" + if [ -z "$cache" ] || [ "$to_recache" -eq 1 ]; then + if [ "$to_recache" -eq 1 ]; then _deleteQuery "$query"; fi + yt-dlp --default-search \ + ytsearch"$NUMBER" "$search" --get-id --get-title \ + 2> /dev/null > "$CACHEDIR/$query" & + wait "$!"; yt_dlp_exit="$?" + [[ "$yt_dlp_exit" -eq 0 ]] || _die "yt-dlp search fail, exit code ${yt_dlp_exit}" + _cacheQuery "$query" 2> /dev/null + rm -f "$CACHEDIR/$query" + fi + # check if ytdl-mpv is already running, if yes append track to playlist + local args + local STYLE="window {width: ${WIDTH}%;} listview {lines: ${LINEN};}" + if [ "$(_ytdl_mpvctl check)" == "disabled" ]; then + args=( -kb-custom-1 "${play_audio}" + -kb-custom-2 "${play_video}" + -kb-custom-3 "${copy_id}" + -kb-custom-4 "${key_help}" + -kb-custom-5 "${key_return}" + -kb-accept-alt "${multi_select}" + -theme-str "$STYLE" + -multi-select + -no-custom + -mesg "-- play menu: start audio or video playback, help [Alt+h] --" ) + else + args=( -kb-custom-5 "${key_return}" + -kb-accept-alt "${multi_select}" + -theme-str "$STYLE" + -multi-select + -no-custom + -mesg "-- play menu: add track to current playlist, simply [Enter] --" ) + fi + # selected track + local strack + local rofi_exit + stracks="$(_getCachedQuery "$query" | _rofi "${args[@]}")" + rofi_exit="$?" + # check if help requested + if [[ "${rofi_exit}" -eq 13 ]]; then + _helpPlay; _startPlay; + else + if [ -z "$stracks" ]; then + _info "Nothing selected" + exit 0 + elif [[ "${rofi_exit}" -eq 14 ]]; then + _info "Back to search menu" + _searchMenu; return; + else + local IFS + IFS=$'\n' + for strack in $stracks; do + strack="${strack:4}" + local id + id="ytdl://$(_getCachedIdQuery "$query" "$strack")" + # check if ytdl socket is idle, if yes append instead play + if [ "$(_ytdl_mpvctl check)" == "disabled" ]; then + case "${rofi_exit}" in + 0) "${default_do}" "$id" ;; + 10) _playAudio "$id" ;; + 11) _playVideo "$id" ;; + 12) _copyId "$id"; return ;; + esac + sleep 1; + else + _appendTrack "$id"; + fi + done + # recursive until explicit exit + _searchMenu + fi + fi } # Parse optional argument while :; do - case "$1" in - -n|--number) shift; [ -n "$1" ] && [[ $1 =~ ^[0-9]+$ ]] && NUMBER="$1";; - -l|--linen) shift; [ -n "$1" ] && [[ $1 =~ ^[0-9]+$ ]] && LINEN="$1";; - -h|--help) shift; _usage; exit 0;; - -V|--version) shift; printf 'ytdl-mpv: %s\n' "${VERSION}"; exit 0;; - -s|--socket) shift; [ -n "$1" ] && SOCKET="$1";; - -w|--width) shift; [ -n "$1" ] && [[ $1 =~ ^[0-9]+$ ]] && WIDTH="$1";; - *) break;; - esac - shift + [ -z "$1" ] && break + case "$1" in + -n|--number) shift; { [ -n "$1" ] && [[ $1 =~ ^[0-9]+$ ]] && NUMBER="$1"; } \ + || _die "Invalid search results number: $1" ;; + -l|--linen) shift; { [ -n "$1" ] && [[ $1 =~ ^[0-9]+$ ]] && LINEN="$1"; } \ + || _die "Invalid rofi vertical lines number: $1" ;; + -h|--help) shift; _usage; exit 0 ;; + -V|--version) shift; printf 'ytdl-mpv: %s\n' "${VERSION}"; exit 0 ;; + -s|--socket) shift; { [ -n "$1" ] && [ -S "$1" ] && SOCKET="$1"; } \ + || _die "Invalid socket file: $1" ;; + -w|--width) shift; { [ -n "$1" ]; [[ $1 =~ ^[0-9]+$ ]] && WIDTH="$1"; } \ + || _die "Invalid rofi width: $1" ;; + *) _die "Invalid flag: $1, for help run ytdl-mpv -h" ;; + esac + shift done _checkDep # Run deps test