summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-05-02 08:26:21 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-05-02 08:26:21 +0900
commitca495a07901065a973cfc141fe867da1cf248298 (patch)
treee75e9ab5ce8edc24e930c5127d107a0ad4c22df5
parent3a0d331755bd9c1d7a4f3e858d42e902b8a7c3eb (diff)
modified bin/qndl
-rwxr-xr-xar/.local/bin/qndl151
1 files changed, 144 insertions, 7 deletions
diff --git a/ar/.local/bin/qndl b/ar/.local/bin/qndl
index f522ec3..3eaf7d2 100755
--- a/ar/.local/bin/qndl
+++ b/ar/.local/bin/qndl
@@ -73,6 +73,14 @@ get_type() {
printf 'restore'
return 0
;;
+ -l | --list | l | list)
+ printf 'list'
+ return 0
+ ;;
+ -k | --kill | k | kill)
+ printf 'kill'
+ return 0
+ ;;
*)
printf '%s' "$_arg"
return 0
@@ -95,10 +103,12 @@ get_filename() {
enqueue() {
_dl_type="$1"
_url="$2"
- shift 2
+ _title="$3"
+ shift 3
_cookies="$(get_cookies)"
_music_dir="${XDG_MUSIC_DIR:-$HOME/Music}"
+ _cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/qndl"
# Build argument list without string concatenation chains
set -- \
@@ -126,6 +136,11 @@ enqueue() {
_idnum="$(tsp bash -c "$_ytdl_cmd")"
pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+ # tsp output doesn't carry titles; cache them at queue time so `qndl -l/-k`
+ # can show meaningful labels instead of bare URLs.
+ mkdir -p "$_cache_dir"
+ printf '%s\t%s\t%s\n' "$_idnum" "$_dl_type" "${_title:-$_url}" >> "$_cache_dir/titles"
+
# tsp -D runs next job only if dependency succeeded (exit 0)
# Success notification — runs only if download succeeds
tsp -D "$_idnum" notify-send -- "✅ ${_dl_type} download complete:" "$_url"
@@ -199,7 +214,7 @@ download_music() {
_filename="$(get_filename "$_url")"
notify "📥 Queuing music download:" "$_filename"
- enqueue "music" "$_url" \
+ enqueue "music" "$_url" "$_filename" \
"$_pl_flag" \
--extract-audio \
--audio-format mp3 \
@@ -243,7 +258,7 @@ download_video() {
notify "📥 Queuing video download:" "$_filename"
if [ -n "$_recode_ext" ]; then
- enqueue "video" "$_url" \
+ enqueue "video" "$_url" "$_filename" \
"$_pl_flag" \
--buffer-size 1M \
--embed-thumbnail \
@@ -252,7 +267,7 @@ download_video() {
--recode-video "$_recode_ext" \
--output "$_fmt"
else
- enqueue "video" "$_url" \
+ enqueue "video" "$_url" "$_filename" \
"$_pl_flag" \
--buffer-size 1M \
--embed-thumbnail \
@@ -310,7 +325,12 @@ restore_archive() {
printf '%s\n' "$_selected" >"$_tmpfile"
while IFS= read -r _id; do
[ -z "$_id" ] && continue
- enqueue "music" "https://www.youtube.com/watch?v=$_id" \
+ if [ -f "$_titles" ]; then
+ _t="$(awk -F'\t' -v id="$_id" '$1 == id {print $2; exit}' "$_titles")"
+ else
+ _t=""
+ fi
+ enqueue "music" "https://www.youtube.com/watch?v=$_id" "${_t:-$_id}" \
--no-playlist \
--extract-audio \
--audio-format mp3 \
@@ -322,6 +342,117 @@ restore_archive() {
}
# ---------------------------------------------------------------------------
+# Listing & Cancellation
+# ---------------------------------------------------------------------------
+
+# Two consecutive tsp IDs after the main download are notification helpers
+# (success via `tsp -D`, failure via separate bash script). Music adds a
+# 3rd helper for `mpc update`. When cancelling a job we remove main + its
+# helpers so stray ❌ notifications don't fire.
+_helper_offset_for() {
+ case "$1" in
+ music) printf '3' ;;
+ *) printf '2' ;;
+ esac
+}
+
+list_queue() {
+ _cache="${XDG_CACHE_HOME:-$HOME/.cache}/qndl/titles"
+ [ -f "$_cache" ] || _cache=/dev/null
+ _tab="$(printf '\t')"
+
+ {
+ printf 'ID\tState\tType\tTitle\tURL\n'
+ tsp | awk -v cache="$_cache" '
+ BEGIN {
+ while ((getline line < cache) > 0) {
+ n = split(line, a, "\t")
+ if (n >= 3) { types[a[1]] = a[2]; titles[a[1]] = a[3] }
+ }
+ }
+ NR == 1 { next }
+ /yt-dlp/ && ($2 == "queued" || $2 == "running") {
+ match($0, /https?:\/\/[^ '\'']+/)
+ url = RSTART ? substr($0, RSTART, RLENGTH) : "-"
+ t = ($1 in types) ? types[$1] : "?"
+ title = ($1 in titles) ? titles[$1] : "-"
+ printf "%s\t%s\t%s\t%s\t%s\n", $1, $2, t, title, url
+ }
+ '
+ } | column -t -s "$_tab"
+}
+
+kill_job() {
+ command -v fzf >/dev/null 2>&1 || die "⛔ fzf not installed" "Install fzf to use qndl -k."
+
+ _cache="${XDG_CACHE_HOME:-$HOME/.cache}/qndl/titles"
+ [ -f "$_cache" ] || _cache=/dev/null
+
+ # Build fzf input: <id>\t<displayed line>. --with-nth hides column 1, cut grabs it back.
+ _entries="$(tsp | awk -v cache="$_cache" '
+ BEGIN {
+ while ((getline line < cache) > 0) {
+ n = split(line, a, "\t")
+ if (n >= 3) { types[a[1]] = a[2]; titles[a[1]] = a[3] }
+ }
+ }
+ /yt-dlp/ && ($2 == "queued" || $2 == "running") {
+ match($0, /https?:\/\/[^ '\'']+/)
+ url = RSTART ? substr($0, RSTART, RLENGTH) : "-"
+ t = ($1 in types) ? types[$1] : "?"
+ title = ($1 in titles) ? titles[$1] : "-"
+ printf "%s\t[%s] %-9s %-5s %s :: %s\n", $1, t, $2, $1, title, url
+ }
+ ')"
+
+ [ -z "$_entries" ] && die "⛔ No active downloads" "Nothing to cancel."
+
+ _selected="$(printf '%s\n' "$_entries" |
+ fzf -m \
+ --with-nth=2.. \
+ --delimiter='\t' \
+ --prompt='Cancel: ' \
+ --header='TAB to mark multiple, Enter to confirm' |
+ cut -f1)"
+
+ [ -z "$_selected" ] && return 0
+
+ _ids_file="$(mktemp)"
+ printf '%s\n' "$_selected" >"$_ids_file"
+
+ _killed=0
+ while IFS= read -r _id; do
+ [ -z "$_id" ] && continue
+
+ _t="$(awk -F'\t' -v id="$_id" '$1 == id {print $2; exit}' "$_cache" 2>/dev/null)"
+ _max="$(_helper_offset_for "${_t:-video}")"
+
+ _off=0
+ while [ "$_off" -le "$_max" ]; do
+ _target=$((_id + _off))
+ # `-r` for queued, `-k` for running; only one applies, ignore the other's error.
+ tsp -r "$_target" 2>/dev/null
+ tsp -k "$_target" 2>/dev/null
+ _off=$((_off + 1))
+ done
+ _killed=$((_killed + 1))
+ done <"$_ids_file"
+
+ # Drop cancelled IDs from the title cache in one pass.
+ if [ -f "$_cache" ] && [ "$_cache" != "/dev/null" ]; then
+ _new_cache="$(mktemp)"
+ awk -F'\t' 'NR==FNR { drop[$1]=1; next } !($1 in drop)' \
+ "$_ids_file" "$_cache" >"$_new_cache"
+ mv "$_new_cache" "$_cache"
+ fi
+
+ rm -f "$_ids_file"
+
+ notify "🗑️ Cancelled $_killed download(s)" "Helper notifications removed too."
+ pkill -RTMIN+16 "${STATUSBAR:-dwmblocks}"
+}
+
+# ---------------------------------------------------------------------------
# Entry Point
# ---------------------------------------------------------------------------
@@ -342,11 +473,17 @@ main() {
restore)
restore_archive
;;
+ list)
+ list_queue
+ ;;
+ kill)
+ kill_job
+ ;;
"")
- die "⛔ No type specified" "Provide: music, video, or restore."
+ die "⛔ No type specified" "Provide: music, video, restore, list, or kill."
;;
*)
- die "⛔ Invalid type: $_type" "Recognized types: music, video, restore."
+ die "⛔ Invalid type: $_type" "Recognized types: music, video, restore, list, kill."
;;
esac
}