From aef3be9d2d61f5e2661bf5fd094cce5790b423fc Mon Sep 17 00:00:00 2001 From: Dymas Date: Mon, 25 May 2026 17:20:55 +0200 Subject: [PATCH] Add host path browser to config page --- CHANGELOG.md | 5 + README.md | 4 +- VERSION | 2 +- app_support.py | 2 +- config_page.py | 239 +++++++++++++++++++++++++++++++++++++++++++++++- http_handler.py | 54 +++++++++++ test_app.py | 18 ++++ 7 files changed, 318 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32d7a5f..466076b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.44.0 - 2026-05-25 + +- Added host filesystem browse buttons to the Config page path fields so default download and Jellyfin library folders can be selected from a built-in browser modal instead of typed manually. +- Added an authenticated `/api/fs/browse` endpoint for directory and file browsing to support the new host path picker workflow. + ## 0.43.0 - 2026-05-25 - Added a Jellyfin section on the Config page with enable or disable control, separate TV and movie destination folders, and a manual `Run now` handoff trigger. diff --git a/README.md b/README.md index a993a2d..394c438 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Small local web UI for a system-wide `ani-cli` install. -Current version: `0.43.0` +Current version: `0.44.0` ## Project Layout @@ -130,6 +130,7 @@ Container notes: - Cache watchlist thumbnails locally. - Sync successful downloads back into the watchlist. - Keep queue and watchlist data in SQLite under `.ani-cli-web/`. +- Browse host folders directly from Config page path fields when choosing library locations. ## Pages @@ -195,6 +196,7 @@ Layout: - `Dependencies` stays at the top as a full-width runtime status card. - The remaining Config sections use two columns on larger screens and collapse to one column at `1080px` width and below. - Runtime info shows both the `ani-cli-web` version and the installed `ani-cli` version. +- Path fields include `Browse` buttons that open a host-side folder picker modal in the browser. Saved settings are written to `.ani-cli-web/config.json`. diff --git a/VERSION b/VERSION index 8298bb0..a8ab6c9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.43.0 +0.44.0 diff --git a/app_support.py b/app_support.py index e58f6f2..75a130e 100644 --- a/app_support.py +++ b/app_support.py @@ -20,7 +20,7 @@ from uuid import uuid4 PROJECT_ROOT = Path(__file__).resolve().parent ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli" APP_NAME = "ani-cli-web" -VERSION = "0.43.0" +VERSION = "0.44.0" ALLANIME_BASE = "allanime.day" ALLANIME_API = f"https://api.{ALLANIME_BASE}" ALLANIME_REFERER = "https://allmanga.to" diff --git a/config_page.py b/config_page.py index 6cf0347..804f18b 100644 --- a/config_page.py +++ b/config_page.py @@ -243,6 +243,12 @@ CONFIG_HTML = r""" color: var(--muted); font-size: 12px; } + .path-input { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + } .form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); @@ -297,6 +303,79 @@ CONFIG_HTML = r""" .settings-main, .deps { width: 100%; } + .overlay[hidden] { + display: none !important; + } + .overlay { + position: fixed; + inset: 0; + z-index: 40; + display: grid; + place-items: center; + padding: 22px; + background: rgba(4, 3, 10, 0.78); + backdrop-filter: blur(10px); + } + .browser-modal { + width: min(100%, 860px); + max-height: min(84vh, 760px); + overflow: hidden; + border: 1px solid var(--line); + border-radius: 22px; + background: linear-gradient(180deg, rgba(29, 22, 47, 0.96), rgba(17, 13, 29, 0.98)); + box-shadow: var(--shadow); + display: grid; + grid-template-rows: auto auto auto 1fr auto; + gap: 12px; + padding: 18px; + } + .browser-header, + .browser-actions, + .browser-footer { + display: flex; + gap: 10px; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + } + .browser-path { + min-height: 42px; + border: 1px solid var(--line); + border-radius: 14px; + background: rgba(10, 7, 18, 0.82); + color: var(--text); + padding: 10px 13px; + overflow-wrap: anywhere; + } + .browser-list { + overflow: auto; + border: 1px solid var(--line); + border-radius: 18px; + background: rgba(255, 255, 255, 0.03); + padding: 8px; + display: grid; + gap: 8px; + min-height: 220px; + } + .browser-entry { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + min-height: 44px; + padding: 0 14px; + border: 1px solid var(--line); + border-radius: 14px; + background: rgba(255, 255, 255, 0.03); + color: var(--text); + text-align: left; + } + .browser-entry .muted { + font-size: 12px; + } + .browser-entry-name { + overflow-wrap: anywhere; + } @media (max-width: 900px) { .app { grid-template-columns: 1fr; } aside { border-right: 0; border-bottom: 1px solid var(--line); } @@ -353,7 +432,10 @@ CONFIG_HTML = r"""
@@ -447,10 +529,16 @@ CONFIG_HTML = r"""
TV entries move only after the show is marked `Finished` and at least one downloaded language covers the expected episode count. Movie entries move after they are downloaded. The manual trigger scans the current watchlist and moves anything already eligible.
@@ -481,6 +569,32 @@ CONFIG_HTML = r""" + +