From 7aab65a73aacc32e29b4a5b3102a52fbdcb296c8 Mon Sep 17 00:00:00 2001 From: Danilo Reyes Date: Sat, 28 Feb 2026 22:20:11 -0600 Subject: [PATCH] fzf into download --- pkgs/download.nix | 2 + src/download/admin.py | 14 ++-- src/download/admin_links.py | 125 +++++++++++++++++++++++++++-------- src/download/select_links.py | 51 ++++++++++++++ src/download/setup.cfg | 3 + 5 files changed, 162 insertions(+), 33 deletions(-) create mode 100644 src/download/select_links.py diff --git a/pkgs/download.nix b/pkgs/download.nix index f99e4ec..1da6881 100644 --- a/pkgs/download.nix +++ b/pkgs/download.nix @@ -7,6 +7,7 @@ gallery-dl, ffmpeg, webcomix, + fzf, ... }: let @@ -32,5 +33,6 @@ buildPythonApplication { types-pyyaml yt-dlp webcomix + fzf ]; } diff --git a/src/download/admin.py b/src/download/admin.py index 648c710..0222082 100644 --- a/src/download/admin.py +++ b/src/download/admin.py @@ -30,34 +30,34 @@ def build_parser() -> argparse.ArgumentParser: p_disable = sub.add_parser("disable") p_disable.add_argument("user") - p_disable.add_argument("url") + p_disable.add_argument("url", nargs="?") p_disable.set_defaults(func=cmd_disable) p_enable = sub.add_parser("enable") p_enable.add_argument("user") - p_enable.add_argument("url") + p_enable.add_argument("url", nargs="?") p_enable.set_defaults(func=cmd_enable) p_ban = sub.add_parser("ban") p_ban.add_argument("user") - p_ban.add_argument("url") + p_ban.add_argument("url", nargs="?") p_ban.add_argument("--reason") p_ban.set_defaults(func=cmd_ban) p_unban = sub.add_parser("unban") p_unban.add_argument("user") - p_unban.add_argument("url") + p_unban.add_argument("url", nargs="?") p_unban.set_defaults(func=cmd_unban) p_remove = sub.add_parser("remove") p_remove.add_argument("user") - p_remove.add_argument("url") + p_remove.add_argument("url", nargs="?") p_remove.set_defaults(func=cmd_remove) p_rename = sub.add_parser("rename") p_rename.add_argument("user") - p_rename.add_argument("old_url") - p_rename.add_argument("new_url") + p_rename.add_argument("old_url", nargs="?") + p_rename.add_argument("new_url", nargs="?") p_rename.set_defaults(func=cmd_rename) p_list = sub.add_parser("list") diff --git a/src/download/admin_links.py b/src/download/admin_links.py index 69ace29..0240557 100644 --- a/src/download/admin_links.py +++ b/src/download/admin_links.py @@ -3,6 +3,8 @@ from __future__ import annotations import argparse +import shutil +import subprocess from pathlib import Path import db @@ -59,48 +61,54 @@ def cmd_add(args: argparse.Namespace) -> None: def cmd_disable(args: argparse.Namespace) -> None: - with db.connect() as conn: - ok = db.set_enabled(conn, args.user, args.url, enabled=False) - if ok: - conn.commit() - print("ok" if ok else "not found") + _apply_to_links( + args, + lambda conn, user, url: db.set_enabled(conn, user, url, enabled=False), + selector_filter="disable", + ) def cmd_enable(args: argparse.Namespace) -> None: - with db.connect() as conn: - ok = db.set_enabled(conn, args.user, args.url, enabled=True) - if ok: - conn.commit() - print("ok" if ok else "not found") + _apply_to_links( + args, + lambda conn, user, url: db.set_enabled(conn, user, url, enabled=True), + selector_filter="enable", + ) def cmd_ban(args: argparse.Namespace) -> None: - with db.connect() as conn: - ok = db.set_banned(conn, args.user, args.url, banned=True, reason=args.reason) - if ok: - conn.commit() - print("ok" if ok else "not found") + _apply_to_links( + args, + lambda conn, user, url: db.set_banned( + conn, user, url, banned=True, reason=args.reason + ), + selector_filter="ban", + ) def cmd_unban(args: argparse.Namespace) -> None: - with db.connect() as conn: - ok = db.set_banned(conn, args.user, args.url, banned=False) - if ok: - conn.commit() - print("ok" if ok else "not found") + _apply_to_links( + args, + lambda conn, user, url: db.set_banned(conn, user, url, banned=False), + selector_filter="unban", + ) def cmd_remove(args: argparse.Namespace) -> None: - with db.connect() as conn: - ok = db.remove_link(conn, args.user, args.url) - if ok: - conn.commit() - print("ok" if ok else "not found") + _apply_to_links(args, lambda conn, user, url: db.remove_link(conn, user, url), "any") def cmd_rename(args: argparse.Namespace) -> None: + old_url = args.old_url + if not old_url: + selection = _select_links(args.user, multi=False, selector_filter="any") + if not selection: + print("not found") + return + old_url = selection[0] + new_url = args.new_url or input("New URL: ").strip() with db.connect() as conn: - result = db.rename_link(conn, args.user, args.old_url, args.new_url) + result = db.rename_link(conn, args.user, old_url, new_url) if result["status"] == "renamed": conn.commit() print(result["status"]) @@ -191,3 +199,68 @@ def cmd_validate_import(_: argparse.Namespace) -> None: [missing_enabled, missing_disabled, extra_enabled, extra_disabled] ): print(" OK") + + +def _fzf_select(lines: list[str], multi: bool) -> list[str]: + if not lines: + return [] + if shutil.which("fzf") is None: + print("fzf not found.") + return [] + args = ["fzf"] + if multi: + args.append("--multi") + proc = subprocess.run( + args, + input="\n".join(lines), + text=True, + capture_output=True, + check=False, + ) + if proc.returncode != 0: + return [] + return [ln for ln in proc.stdout.splitlines() if ln.strip()] + + +def _select_links(user: str, multi: bool, selector_filter: str) -> list[str]: + with db.connect() as conn: + rows = db.get_links(conn, users=[user], include_disabled=True, include_banned=True) + links = [] + for row in rows: + enabled = bool(row["enabled"]) + banned = bool(row["banned_at"]) + if selector_filter == "enable" and enabled: + continue + if selector_filter == "disable" and not enabled: + continue + if selector_filter == "ban" and banned: + continue + if selector_filter == "unban" and not banned: + continue + links.append(row["url_original"]) + return _fzf_select(links, multi=multi) + + +def _apply_to_links(args: argparse.Namespace, fn, selector_filter: str) -> None: + if args.url: + with db.connect() as conn: + ok = fn(conn, args.user, args.url) + if ok: + conn.commit() + print("ok" if ok else "not found") + return + + selections = _select_links(args.user, multi=True, selector_filter=selector_filter) + if not selections: + print("not found") + return + + with db.connect() as conn: + changed = 0 + for url in selections: + ok = fn(conn, args.user, url) + if ok: + changed += 1 + if changed: + conn.commit() + print(f"ok ({changed})") diff --git a/src/download/select_links.py b/src/download/select_links.py new file mode 100644 index 0000000..7f29144 --- /dev/null +++ b/src/download/select_links.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""fzf-based selectors for comic and gallery links.""" +from __future__ import annotations + +import re +import subprocess + +import db + +USER = "jawz" + +RGX_COMIC = re.compile("readcomiconline|mangahere|mangadex|webtoons|manganato") + + +def _select_links(urls: list[str]) -> list[str]: + if not urls: + return [] + proc = subprocess.run( + ["fzf", "--multi", "--exact", "-i"], + input="\n".join(urls), + text=True, + capture_output=True, + check=False, + ) + if proc.returncode != 0: + return [] + return [ln for ln in proc.stdout.splitlines() if ln.strip()] + + +def _run_download(selected: list[str]) -> None: + if not selected: + return + subprocess.run(["download", "-u", USER, "-i", *selected], check=False) + + +def comic_main() -> None: + with db.connect() as conn: + rows = db.get_links(conn, users=[USER], include_disabled=False, include_banned=False) + urls = [row["url_original"] for row in rows if RGX_COMIC.search(row["url_original"])] + _run_download(_select_links(urls)) + + +def gallery_main() -> None: + with db.connect() as conn: + rows = db.get_links(conn, users=[USER], include_disabled=False, include_banned=False) + urls = [row["url_original"] for row in rows if not RGX_COMIC.search(row["url_original"])] + _run_download(_select_links(urls)) + + +if __name__ == "__main__": + gallery_main() diff --git a/src/download/setup.cfg b/src/download/setup.cfg index eb630aa..83d2852 100644 --- a/src/download/setup.cfg +++ b/src/download/setup.cfg @@ -10,6 +10,7 @@ py_modules = admin admin_links admin_users + select_links classes.gallery classes.user @@ -17,3 +18,5 @@ py_modules = console_scripts = download = download:main download-admin = admin:main + comic = select_links:comic_main + gallery = select_links:gallery_main