kryten-webqueue 0.34.9__tar.gz → 0.34.11__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/CHANGELOG.md +32 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/PKG-INFO +1 -1
- kryten_webqueue-0.34.11/_tmp_check_covers.py +38 -0
- kryten_webqueue-0.34.11/_tmp_check_covers2.py +33 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/app.py +2 -3
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/images.py +60 -12
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/sync.py +11 -2
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/pyproject.toml +1 -1
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/.github/workflows/gitleaks.yml +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/.gitignore +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/.gitleaks.toml +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/.pre-commit-config.yaml +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/AGENT-WORKFLOW-GUIDE.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/README.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/config.example.json +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/Log for incorrect promo insertion problem.txt +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/SPEC_EMOTE_REHOST.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/db/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/db/_catalog.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/db/_connection.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/db/_feedback.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/db/_playlists.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/db/_queue.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/jobs/job_scheduler.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/queue/completion.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/queue/race_poller.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_feedback.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_moderation.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/feedback.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-playlists.js +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-promos.js +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-schedules.js +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin.js +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/feedback/index.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/race.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/__init__.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_feedback.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_race_poller.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_recently_played_hide.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/tests/test_search_facets.py +0 -0
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.34.11] - 2026-08-13
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Cover art now found for many more films.** Three improvements to the search
|
|
10
|
+
strategy in `CoverArtResolver`:
|
|
11
|
+
|
|
12
|
+
- **Retry thumbnail-source movies on every sync.** Previously any item with a
|
|
13
|
+
saved `cover_art_path` (including the thumbnail fallback) was never
|
|
14
|
+
re-checked against TMDB/OMDB. Now items with `cover_art_source='thumbnail'`
|
|
15
|
+
and `duration_sec > 1800` (movie-length) are retried on each catalog sync,
|
|
16
|
+
so a title that failed the first time around gets another chance.
|
|
17
|
+
|
|
18
|
+
- **Leading-year normalization.** Titles stored as `(1989) Godzilla vs.
|
|
19
|
+
Biollante` are now converted to `Godzilla vs. Biollante (1989)` before
|
|
20
|
+
the first TMDB/OMDB search attempt so the API sees a properly-formatted
|
|
21
|
+
query. (`_normalize_leading_year` helper; applied in `_search_tmdb` and
|
|
22
|
+
`_search_omdb`.)
|
|
23
|
+
|
|
24
|
+
- **Smarter `_clean_title`.** Year extraction now prefers a
|
|
25
|
+
parenthesised year over a bare leading year (fixes `1917 (2019)` extracting
|
|
26
|
+
year 2019, not 1917). Added noise-stripping for `(Dubbed)`, `(English)`,
|
|
27
|
+
`(Uncut)` and similar language/edition tags; trailing `Svengoolie`
|
|
28
|
+
attribution; and bare trailing quality markers like `HD`.
|
|
29
|
+
|
|
30
|
+
## [0.34.10] - 2026-08-13
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **Startup crash.** `app.py` referenced the now-removed `playlist_pass` key in
|
|
35
|
+
the return value of `purge_promo_hide_state()`.
|
|
36
|
+
|
|
5
37
|
## [0.34.9] - 2026-08-13
|
|
6
38
|
|
|
7
39
|
### Changed
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
|
|
3
|
+
DB = "/var/lib/kryten-webqueue/webqueue.db"
|
|
4
|
+
conn = sqlite3.connect(f"file:{DB}?mode=ro", uri=True)
|
|
5
|
+
conn.row_factory = sqlite3.Row
|
|
6
|
+
|
|
7
|
+
# Check specific item
|
|
8
|
+
cur = conn.execute(
|
|
9
|
+
"SELECT friendly_token, title, cover_art_path, cover_art_source FROM catalog WHERE friendly_token='0A9oGeit2'"
|
|
10
|
+
)
|
|
11
|
+
print("=== Deathsport item ===")
|
|
12
|
+
for r in cur:
|
|
13
|
+
print(dict(r))
|
|
14
|
+
|
|
15
|
+
# Overall cover art stats
|
|
16
|
+
print("\n=== Cover art stats ===")
|
|
17
|
+
cur2 = conn.execute(
|
|
18
|
+
"SELECT cover_art_source, COUNT(*) as cnt FROM catalog GROUP BY cover_art_source ORDER BY cnt DESC"
|
|
19
|
+
)
|
|
20
|
+
for r in cur2:
|
|
21
|
+
print(dict(r))
|
|
22
|
+
|
|
23
|
+
# How many items have no cover art at all?
|
|
24
|
+
cur3 = conn.execute(
|
|
25
|
+
"SELECT COUNT(*) as cnt FROM catalog WHERE cover_art_path IS NULL OR cover_art_path = ''"
|
|
26
|
+
)
|
|
27
|
+
row = cur3.fetchone()
|
|
28
|
+
print(f"\nItems with no cover art path: {row['cnt']}")
|
|
29
|
+
|
|
30
|
+
# Sample of items with no cover art (movies - duration > 3600)
|
|
31
|
+
print("\n=== Sample no-art items (movies) ===")
|
|
32
|
+
cur4 = conn.execute(
|
|
33
|
+
"SELECT friendly_token, title, cover_art_source FROM catalog WHERE (cover_art_path IS NULL OR cover_art_path = '') AND duration_sec > 3600 LIMIT 20"
|
|
34
|
+
)
|
|
35
|
+
for r in cur4:
|
|
36
|
+
print(dict(r))
|
|
37
|
+
|
|
38
|
+
conn.close()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
|
|
3
|
+
conn = sqlite3.connect("file:/var/lib/kryten-webqueue/webqueue.db?mode=ro", uri=True)
|
|
4
|
+
conn.row_factory = sqlite3.Row
|
|
5
|
+
|
|
6
|
+
print("=== Thumbnail-source movies (sample) ===")
|
|
7
|
+
cur = conn.execute(
|
|
8
|
+
"SELECT friendly_token, title FROM catalog "
|
|
9
|
+
"WHERE cover_art_source='thumbnail' AND duration_sec > 3600 "
|
|
10
|
+
"ORDER BY title LIMIT 40"
|
|
11
|
+
)
|
|
12
|
+
for r in cur:
|
|
13
|
+
print(f"{r['friendly_token']:12} {r['title']}")
|
|
14
|
+
|
|
15
|
+
print("\n=== TMDB-matched movies (sample for comparison) ===")
|
|
16
|
+
cur2 = conn.execute(
|
|
17
|
+
"SELECT friendly_token, title FROM catalog "
|
|
18
|
+
"WHERE cover_art_source='tmdb' AND duration_sec > 3600 "
|
|
19
|
+
"ORDER BY title LIMIT 10"
|
|
20
|
+
)
|
|
21
|
+
for r in cur2:
|
|
22
|
+
print(f"{r['friendly_token']:12} {r['title']}")
|
|
23
|
+
|
|
24
|
+
print("\n=== thumbnail-source items where title looks clean (year + normal title) ===")
|
|
25
|
+
cur3 = conn.execute(
|
|
26
|
+
"SELECT COUNT(*) as cnt FROM catalog "
|
|
27
|
+
"WHERE cover_art_source='thumbnail' AND duration_sec > 3600 "
|
|
28
|
+
"AND title GLOB '* (19[0-9][0-9])' OR title GLOB '* (20[0-9][0-9])'"
|
|
29
|
+
)
|
|
30
|
+
row = cur3.fetchone()
|
|
31
|
+
print(f"Items with '(YYYY)' in title: {row['cnt']}")
|
|
32
|
+
|
|
33
|
+
conn.close()
|
|
@@ -57,11 +57,10 @@ async def lifespan(app: FastAPI):
|
|
|
57
57
|
# recently-played rules). Cheap and idempotent; also cleans rows written by
|
|
58
58
|
# older builds that classified promos only by pool membership.
|
|
59
59
|
purged = await db.purge_promo_hide_state()
|
|
60
|
-
if purged["completions"]
|
|
60
|
+
if purged["completions"]:
|
|
61
61
|
logger.info(
|
|
62
|
-
"Purged promo recently-played state: %d completion(s)
|
|
62
|
+
"Purged promo recently-played state: %d completion(s)",
|
|
63
63
|
purged["completions"],
|
|
64
|
-
purged["playlist_pass"],
|
|
65
64
|
)
|
|
66
65
|
# Any job run still marked 'running' is an orphan from a prior crash/restart
|
|
67
66
|
# (the running flag is in-memory only). Reconcile before registering jobs.
|
|
@@ -11,15 +11,41 @@ from PIL import Image
|
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def _normalize_leading_year(title: str) -> str:
|
|
15
|
+
"""Move a leading parenthesised year to standard trailing position.
|
|
16
|
+
|
|
17
|
+
``(1989) Godzilla vs. Biollante`` → ``Godzilla vs. Biollante (1989)``
|
|
18
|
+
Has no effect when the year is already trailing or absent.
|
|
19
|
+
"""
|
|
20
|
+
m = re.match(r"^\s*[\(\[]((?:19|20)\d{2})[\)\]]\s*(.+)", title)
|
|
21
|
+
if m:
|
|
22
|
+
return f"{m.group(2).strip()} ({m.group(1)})"
|
|
23
|
+
return title
|
|
24
|
+
|
|
25
|
+
|
|
14
26
|
def _clean_title(title: str) -> tuple[str, str | None]:
|
|
15
27
|
"""Return (cleaned_title, year_or_None) stripping common noise."""
|
|
16
|
-
#
|
|
17
|
-
year
|
|
18
|
-
|
|
28
|
+
# Prefer a year in parentheses/brackets — it's almost always the movie year.
|
|
29
|
+
# A bare leading year like "1917 (2019)" has the year as its TITLE, not its
|
|
30
|
+
# release year, so the parenthesised form wins when both are present.
|
|
31
|
+
m = re.search(r"[\(\[]((?:19|20)\d{2})[\)\]]", title)
|
|
19
32
|
if m:
|
|
20
|
-
year = m.group(1)
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
year: str | None = m.group(1)
|
|
34
|
+
else:
|
|
35
|
+
m = re.search(r"\b((?:19|20)\d{2})\b", title)
|
|
36
|
+
year = m.group(1) if m else None
|
|
37
|
+
|
|
38
|
+
# Remove ONLY the identified year (with its surrounding brackets if any).
|
|
39
|
+
if year:
|
|
40
|
+
cleaned = re.sub(r"\s*[\(\[]\s*" + year + r"\s*[\)\]]", "", title)
|
|
41
|
+
# Also remove a bare occurrence of the same year if it has word boundaries
|
|
42
|
+
# on both sides (handles "Title 1978" format) but NOT when the year IS
|
|
43
|
+
# the title ("1917").
|
|
44
|
+
cleaned = re.sub(r"(?<=\s)" + year + r"(?=\s|$)", "", cleaned).strip()
|
|
45
|
+
else:
|
|
46
|
+
cleaned = title
|
|
47
|
+
|
|
48
|
+
# Episode tags, resolution/encoding, dub/language noise.
|
|
23
49
|
cleaned = re.sub(r"\s*[Ss]\d{1,2}[Ee]\d{1,2}.*", "", cleaned)
|
|
24
50
|
cleaned = re.sub(
|
|
25
51
|
r"\s*\b(?:720p|1080p|2160p|4K|HDR|BluRay|BDRip|WEB[-.]?DL|HDTV)\b.*",
|
|
@@ -27,6 +53,22 @@ def _clean_title(title: str) -> tuple[str, str | None]:
|
|
|
27
53
|
cleaned,
|
|
28
54
|
flags=re.IGNORECASE,
|
|
29
55
|
)
|
|
56
|
+
cleaned = re.sub(
|
|
57
|
+
r"\s*[\(\[]\s*(?:Dubbed|English Dubbed|English Dub|Dub|Sub(?:bed)?|"
|
|
58
|
+
r"English|Uncut|Extended(?: Cut)?|Director'?s? Cut|Remastered|HD|SD)\s*[\)\]]",
|
|
59
|
+
"",
|
|
60
|
+
cleaned,
|
|
61
|
+
flags=re.IGNORECASE,
|
|
62
|
+
)
|
|
63
|
+
# Strip trailing Svengoolie attribution (e.g. "Tarantula Svengoolie").
|
|
64
|
+
cleaned = re.sub(r"\s+Svengoolie\b.*", "", cleaned, flags=re.IGNORECASE)
|
|
65
|
+
# Strip trailing bare quality/edition markers (e.g. "Title HD" or "Title Uncut").
|
|
66
|
+
cleaned = re.sub(
|
|
67
|
+
r"\s+\b(?:HD|SD|4K|Uncut|Remastered|Extended)\b\s*$",
|
|
68
|
+
"",
|
|
69
|
+
cleaned,
|
|
70
|
+
flags=re.IGNORECASE,
|
|
71
|
+
)
|
|
30
72
|
cleaned = cleaned.strip(" .-")
|
|
31
73
|
return cleaned or title, year
|
|
32
74
|
|
|
@@ -149,12 +191,15 @@ class CoverArtResolver:
|
|
|
149
191
|
|
|
150
192
|
async def _search_tmdb(self, title: str) -> str | None:
|
|
151
193
|
"""Search TMDB for a poster: tries movie+TV in parallel, retries with cleaned title."""
|
|
152
|
-
|
|
194
|
+
# Normalise leading-year format before the first attempt so TMDB sees
|
|
195
|
+
# 'Title (Year)' rather than '(Year) Title'.
|
|
196
|
+
normalized = _normalize_leading_year(title)
|
|
197
|
+
result = await self._tmdb_search_both(normalized)
|
|
153
198
|
if result:
|
|
154
199
|
return result
|
|
155
|
-
# Retry with cleaned title
|
|
156
|
-
cleaned, year = _clean_title(
|
|
157
|
-
if cleaned !=
|
|
200
|
+
# Retry with fully-cleaned title + year filter.
|
|
201
|
+
cleaned, year = _clean_title(normalized)
|
|
202
|
+
if cleaned != normalized:
|
|
158
203
|
result = await self._tmdb_search_both(cleaned, year=year)
|
|
159
204
|
return result
|
|
160
205
|
|
|
@@ -205,8 +250,11 @@ class CoverArtResolver:
|
|
|
205
250
|
return None, 0.0
|
|
206
251
|
|
|
207
252
|
async def _search_omdb(self, title: str) -> str | None:
|
|
208
|
-
|
|
209
|
-
|
|
253
|
+
normalized = _normalize_leading_year(title)
|
|
254
|
+
cleaned, year = _clean_title(normalized)
|
|
255
|
+
for t in dict.fromkeys(
|
|
256
|
+
[normalized, cleaned]
|
|
257
|
+
): # try normalized then cleaned, deduped
|
|
210
258
|
params: dict = {"apikey": self._omdb_key, "t": t}
|
|
211
259
|
if year:
|
|
212
260
|
params["y"] = year
|
|
@@ -160,8 +160,17 @@ class CatalogSync:
|
|
|
160
160
|
except Exception as e:
|
|
161
161
|
logger.debug(f"Facet sync failed for {token}: {e}")
|
|
162
162
|
|
|
163
|
-
# Fetch TMDB/OMDB cover art
|
|
164
|
-
if
|
|
163
|
+
# Fetch TMDB/OMDB cover art.
|
|
164
|
+
# Skip only if we already have a real poster (tmdb/omdb source).
|
|
165
|
+
# Retry thumbnail-source items that are long enough to be movies — the
|
|
166
|
+
# thumbnail fallback fires when TMDB/OMDB return nothing, but a better
|
|
167
|
+
# title match on a subsequent sync may now succeed.
|
|
168
|
+
has_real_art = existing and existing.get("cover_art_source") in ("tmdb", "omdb")
|
|
169
|
+
is_movie_length = (row.get("duration_sec") or 0) > 1800
|
|
170
|
+
needs_cover = not has_real_art and (
|
|
171
|
+
not existing or is_movie_length or not existing.get("cover_art_path")
|
|
172
|
+
)
|
|
173
|
+
if self._cover_art and needs_cover:
|
|
165
174
|
try:
|
|
166
175
|
await self._cover_art.resolve(token, row["title"], self._db)
|
|
167
176
|
except Exception as e:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/catalog/db/_connection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/cmsutils/enrichtv.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/ytpipe/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/integrations/ytpipe/downloader.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_job_schedules.py
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_moderation.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_playlists.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/routes/admin_schedules.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-playlists.js
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-promos.js
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-queue-mgmt.js
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/static/js/admin-schedules.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/promos.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/auth/login.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/feedback/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/queue/index.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.34.9 → kryten_webqueue-0.34.11}/kryten_webqueue/templates/user/dashboard.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|