Highlight fully ready finished watchlist cards
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 0.32.4 - 2026-05-17
|
||||
|
||||
- Highlighted fully ready finished watchlist entries with a thin green card border when both subbed and dubbed episodes are complete, making download-complete anime easier to scan quickly.
|
||||
|
||||
## 0.32.3 - 2026-05-17
|
||||
|
||||
- Fixed watchlist download sync so a successful download now reuses the existing tracked entry when the resolved `show_id` changes, preserving its category and marking it downloaded instead of creating a duplicate finished-only row.
|
||||
|
||||
@@ -157,6 +157,7 @@ The app currently includes:
|
||||
- Per-show watchlist refresh queueing now also deduplicates in-flight refreshes, so the same show is not fetched twice back-to-back while one refresh is already running.
|
||||
- The watchlist page now silently refreshes itself while queued per-show refresh work is still being processed, so queued entries settle into their updated state without a manual reload.
|
||||
- The watchlist page now also avoids re-triggering the same unresolved thumbnail warm-up request on every silent poll cycle while a show's thumbnail state is unchanged.
|
||||
- Finished watchlist cards with both sub and dub fully ready now get a thin green border for quicker visual scanning.
|
||||
- Background `Refresh All` execution with status polling, no inline thumbnail downloads during the bulk job, and a configurable delay between shows to reduce upstream request bursts.
|
||||
- Optional scheduled background watchlist refreshes with Config page controls for enable/disable and refresh period.
|
||||
- Scheduled background refresh runs now log queue, start, and completion status to stdout so container logs show whether they finished successfully, with errors, or failed.
|
||||
|
||||
+1
-1
@@ -16,7 +16,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.32.3"
|
||||
VERSION = "0.32.4"
|
||||
ALLANIME_BASE = "allanime.day"
|
||||
ALLANIME_API = f"https://api.{ALLANIME_BASE}"
|
||||
ALLANIME_REFERER = "https://allmanga.to"
|
||||
|
||||
@@ -1481,6 +1481,11 @@ class TemplateHelperTests(unittest.TestCase):
|
||||
self.assertIn("else if (state.queuedCount > 0)", APP.WATCHLIST_HTML)
|
||||
self.assertIn("fetchWatchlist(state.watchlistPage, { silent: true })", APP.WATCHLIST_HTML)
|
||||
|
||||
def test_watchlist_page_highlights_fully_ready_finished_cards(self):
|
||||
self.assertIn(".anime-card.fully-ready", APP.WATCHLIST_HTML)
|
||||
self.assertIn('item.category === "finished" && item.sub_complete && item.dub_complete', APP.WATCHLIST_HTML)
|
||||
self.assertIn("card.className = watchlistCardClassName(item);", APP.WATCHLIST_HTML)
|
||||
|
||||
def test_watchlist_thumbnail_warmup_skips_repeated_attempts(self):
|
||||
self.assertIn("thumbnailWarmupPending: new Set()", APP.WATCHLIST_HTML)
|
||||
self.assertIn("function thumbnailWarmupToken(item)", APP.WATCHLIST_HTML)
|
||||
|
||||
+15
-1
@@ -303,10 +303,19 @@ WATCHLIST_HTML = r"""<!doctype html>
|
||||
box-shadow: 0 18px 38px rgba(5, 2, 15, 0.32);
|
||||
transition: transform 0.18s ease, border-color 0.18s ease;
|
||||
}
|
||||
.anime-card.fully-ready {
|
||||
border-color: rgba(149, 228, 186, 0.72);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(149, 228, 186, 0.18),
|
||||
0 18px 38px rgba(5, 2, 15, 0.32);
|
||||
}
|
||||
.anime-card:hover {
|
||||
border-color: var(--line-strong);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.anime-card.fully-ready:hover {
|
||||
border-color: rgba(149, 228, 186, 0.88);
|
||||
}
|
||||
.anime-card.error {
|
||||
border-color: rgba(255, 144, 164, 0.3);
|
||||
}
|
||||
@@ -699,6 +708,11 @@ WATCHLIST_HTML = r"""<!doctype html>
|
||||
return parts.map((part) => part[0]).join("").slice(0, 2);
|
||||
}
|
||||
|
||||
function watchlistCardClassName(item) {
|
||||
const isFullyReady = item.category === "finished" && item.sub_complete && item.dub_complete;
|
||||
return `anime-card${item.status === "error" ? " error" : ""}${isFullyReady ? " fully-ready" : ""}`;
|
||||
}
|
||||
|
||||
function formatModeProgress(currentCount, expectedCount) {
|
||||
const current = Number(currentCount || 0);
|
||||
const expected = Number(expectedCount || 0);
|
||||
@@ -965,7 +979,7 @@ WATCHLIST_HTML = r"""<!doctype html>
|
||||
for (const item of items) {
|
||||
const card = document.createElement("article");
|
||||
const checked = item.last_checked ? new Date(item.last_checked).toLocaleString() : "Never";
|
||||
card.className = `anime-card${item.status === "error" ? " error" : ""}`;
|
||||
card.className = watchlistCardClassName(item);
|
||||
card.dataset.showId = item.show_id;
|
||||
card.title = item.thumbnail_debug || "Thumbnail source: unresolved";
|
||||
card.innerHTML = `
|
||||
|
||||
Reference in New Issue
Block a user