kryten-webqueue 0.36.4__tar.gz → 0.36.6__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.36.4 → kryten_webqueue-0.36.6}/CHANGELOG.md +26 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/PKG-INFO +1 -1
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/classify.py +4 -28
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/normalise.py +54 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/tags.py +22 -37
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/title.py +4 -3
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/pyproject.toml +1 -1
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/.github/workflows/gitleaks.yml +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/.gitignore +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/.gitleaks.toml +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/.pre-commit-config.yaml +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/AGENT-WORKFLOW-GUIDE.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/README.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/config.example.json +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/Log for incorrect promo insertion problem.txt +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/SPEC_EMOTE_REHOST.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/app.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_catalog.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_connection.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_feedback.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_people.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_playlists.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_queue.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/report.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/jobs/job_scheduler.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/queue/completion.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/queue/race_poller.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_feedback.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_moderation.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/feedback.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin-playlists.js +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin-promos.js +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin-schedules.js +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin.js +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/feedback/index.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/race.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/user/my_list.html +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/scripts/patch_mediacms_bulk_facets.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/scripts/patch_mediacms_tags.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/scripts/trigger_enrich.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/__init__.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_classify.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_feedback.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_people_facets.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_providers.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_race_poller.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_recently_played_hide.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_search_facets.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_title_normalization.py +0 -0
- {kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/tests/test_watchlist.py +0 -0
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.36.6] - 2026-08-14
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`(Dubbed)` / `(Subbed)` markers no longer break art lookup.** `(dubbed)` is
|
|
10
|
+
stripped from both the display title and the search query (dubbed is the
|
|
11
|
+
default). `(subbed)`/`(subs)`/`(subtitles)` stay in the display title but are
|
|
12
|
+
omitted from the metadata/art search.
|
|
13
|
+
- **YouTube “full movie” pipe titles are cleaned properly.** Detection now matches
|
|
14
|
+
non-adjacent variants like “Full **Horror** Movie”, the title is taken from the
|
|
15
|
+
first segment before the pipe, and the year is extracted from the whole string
|
|
16
|
+
(it often trails after the last pipe, e.g. “… | Larry Bucha (1961)”). Both the
|
|
17
|
+
search query and the display title use this cleanup consistently (previously the
|
|
18
|
+
display title was never tidied for pipe uploads).
|
|
19
|
+
|
|
20
|
+
## [0.36.5] - 2026-08-14
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Tags step no longer reads every item.** With `mediacms_manage_all_media` set,
|
|
25
|
+
the tags step now issues a single idempotent `add_tags` POST per item (the
|
|
26
|
+
server skips tags already present) instead of a per-item `GET` to read current
|
|
27
|
+
tags/owner. The redundant per-item reverse-sync `GET` was also removed — catalog
|
|
28
|
+
sync already mirrors CMS tags in bulk via `/api/v1/media_facets`. Stock-CMS
|
|
29
|
+
(owner-scoped) deployments still do the ownership `GET` on the fallback path.
|
|
30
|
+
|
|
5
31
|
## [0.36.4] - 2026-08-14
|
|
6
32
|
|
|
7
33
|
### Changed
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/classify.py
RENAMED
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
import re
|
|
10
10
|
from dataclasses import dataclass, field
|
|
11
11
|
|
|
12
|
-
from .normalise import
|
|
12
|
+
from .normalise import normalize_movie_title
|
|
13
13
|
|
|
14
14
|
# ---------------------------------------------------------------------------
|
|
15
15
|
# Hosted show registry
|
|
@@ -250,13 +250,6 @@ class ItemClassification:
|
|
|
250
250
|
has_real_art: bool = False
|
|
251
251
|
|
|
252
252
|
|
|
253
|
-
# YouTube "Title | English Full Movie | Genre1 Genre2" pattern
|
|
254
|
-
# Also handles variants like "Title | Full Movie HD | Description"
|
|
255
|
-
_YT_FULL_MOVIE_RE = re.compile(
|
|
256
|
-
r"^(?P<title>.+?)\s*\|.*(Full\s+(?:Movie|Film|Length|Feature)).*$", re.I
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
|
|
260
253
|
def classify_item(
|
|
261
254
|
friendly_token: str,
|
|
262
255
|
raw_title: str,
|
|
@@ -326,26 +319,9 @@ def classify_item(
|
|
|
326
319
|
|
|
327
320
|
# 4. Movie (>= 30 min)
|
|
328
321
|
if duration_sec >= 1800:
|
|
329
|
-
# YouTube
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
clean_title = yt.group("title").strip()
|
|
333
|
-
# Extract year from the clean title part
|
|
334
|
-
clean_title, year = normalize_and_clean(clean_title)
|
|
335
|
-
return ItemClassification(
|
|
336
|
-
friendly_token=friendly_token,
|
|
337
|
-
raw_title=raw_title,
|
|
338
|
-
content_type="movie",
|
|
339
|
-
hosted=None,
|
|
340
|
-
lookup_title=clean_title,
|
|
341
|
-
lookup_year=year,
|
|
342
|
-
genre_hints=[],
|
|
343
|
-
duration_sec=duration_sec,
|
|
344
|
-
description_score=description_score,
|
|
345
|
-
has_real_art=has_real_art,
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
clean_title, year = normalize_and_clean(raw_title)
|
|
322
|
+
# Handles YouTube pipe titles and dub/sub markers; year is pulled from
|
|
323
|
+
# the whole string so a trailing "(1961)" after pipes is still captured.
|
|
324
|
+
clean_title, year = normalize_movie_title(raw_title, for_search=True)
|
|
349
325
|
return ItemClassification(
|
|
350
326
|
friendly_token=friendly_token,
|
|
351
327
|
raw_title=raw_title,
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/normalise.py
RENAMED
|
@@ -103,3 +103,57 @@ def normalize_and_clean(title: str) -> tuple[str, str | None]:
|
|
|
103
103
|
t = strip_extension(t.strip(" .-"))
|
|
104
104
|
t = re.sub(r"\s+", " ", t).strip()
|
|
105
105
|
return t or title, year
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# Dub / sub markers and YouTube "full movie" pipe titles
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
# "dubbed" is the default expectation → stripped from title and search.
|
|
113
|
+
_DUB_TERMS = ["dubbed"]
|
|
114
|
+
# sub markers stay in the display title but are omitted from art/metadata search.
|
|
115
|
+
_SUB_TERMS = ["subbed", "subtitled", "subtitles", "subs"]
|
|
116
|
+
|
|
117
|
+
# A pipe-delimited YouTube upload advertising a full movie, e.g.
|
|
118
|
+
# "Title | English Full Movie | ...", "Title | Full Horror Movie | ...".
|
|
119
|
+
_YT_FULL_MOVIE_RE = re.compile(
|
|
120
|
+
r"\bfull\s+(?:\w+\s+)?(?:movie|film|feature|length)\b", re.I
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _strip_terms(title: str, terms: list[str]) -> str:
|
|
125
|
+
"""Remove bracketed and bare occurrences of ``terms`` from ``title``."""
|
|
126
|
+
pat = "|".join(terms)
|
|
127
|
+
title = re.sub(rf"\s*[\(\[\{{]\s*(?:{pat})\s*[\)\]\}}]", "", title, flags=re.I)
|
|
128
|
+
title = re.sub(rf"\s*\b(?:{pat})\b", "", title, flags=re.I)
|
|
129
|
+
return re.sub(r"\s+", " ", title).strip()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _pre_pipe_title(title: str) -> str | None:
|
|
133
|
+
"""First segment of a pipe-delimited YouTube 'full movie' title, else None."""
|
|
134
|
+
if "|" not in title or not _YT_FULL_MOVIE_RE.search(title):
|
|
135
|
+
return None
|
|
136
|
+
return title.split("|", 1)[0].strip()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def normalize_movie_title(raw: str, *, for_search: bool) -> tuple[str, str | None]:
|
|
140
|
+
"""Return ``(clean_title, year_or_None)`` for a plain movie title.
|
|
141
|
+
|
|
142
|
+
Handles two cases the base cleaner misses:
|
|
143
|
+
- YouTube pipe titles ("Title | Full Movie | ..."): uses the first segment
|
|
144
|
+
before the pipe, but extracts the year from the *whole* string (it often
|
|
145
|
+
trails after the last pipe, e.g. "... | Larry Bucha (1961)").
|
|
146
|
+
- Dub/sub markers: ``(dubbed)`` is always removed; sub markers are removed
|
|
147
|
+
only when ``for_search`` (they stay in the display title).
|
|
148
|
+
"""
|
|
149
|
+
# Year first, from the full string, so a trailing "(1961)" is captured even
|
|
150
|
+
# when the title before the pipe has none.
|
|
151
|
+
_, full_year = normalize_and_clean(raw)
|
|
152
|
+
base = _pre_pipe_title(raw) or raw
|
|
153
|
+
base = (
|
|
154
|
+
_strip_terms(base, _DUB_TERMS + _SUB_TERMS)
|
|
155
|
+
if for_search
|
|
156
|
+
else _strip_terms(base, _DUB_TERMS)
|
|
157
|
+
)
|
|
158
|
+
clean, base_year = normalize_and_clean(base)
|
|
159
|
+
return clean, base_year or full_year
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/tags.py
RENAMED
|
@@ -117,8 +117,6 @@ class TagsStep:
|
|
|
117
117
|
desired,
|
|
118
118
|
api_user=api_user,
|
|
119
119
|
)
|
|
120
|
-
# Reverse sync: pull any CMS-only tags back to local DB
|
|
121
|
-
await self._reverse_sync(client, cls.friendly_token)
|
|
122
120
|
if changed:
|
|
123
121
|
result.changed += 1
|
|
124
122
|
else:
|
|
@@ -148,50 +146,37 @@ class TagsStep:
|
|
|
148
146
|
|
|
149
147
|
The ``PUT /media/{token}`` endpoint only accepts title/description/media_file
|
|
150
148
|
and silently ignores a ``tags`` field, so tag changes must go through
|
|
151
|
-
``POST /media/user/bulk_actions`` with ``action: add_tags``.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
``POST /media/user/bulk_actions`` with ``action: add_tags``.
|
|
150
|
+
|
|
151
|
+
When ``manage_all`` is set the server-side ``add_tags`` is idempotent
|
|
152
|
+
(it skips tags already present), so we POST directly with no per-item GET.
|
|
153
|
+
On stock MediaCMS the endpoint is owner-scoped, so we must first GET the
|
|
154
|
+
item to check ownership and skip the push for media we do not own.
|
|
155
155
|
"""
|
|
156
|
-
detail_url = f"{self._base}/api/v1/media/{token}"
|
|
157
156
|
try:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
if not self._manage_all:
|
|
158
|
+
resp = await client.get(f"{self._base}/api/v1/media/{token}")
|
|
159
|
+
if resp.status_code != 200:
|
|
160
|
+
return False
|
|
161
|
+
data = resp.json()
|
|
162
|
+
owner = data.get("user")
|
|
163
|
+
if api_user is None or owner != api_user:
|
|
164
|
+
return False
|
|
165
|
+
current = {
|
|
166
|
+
(t.get("title") if isinstance(t, dict) else t)
|
|
167
|
+
for t in (data.get("tags_info") or [])
|
|
168
|
+
}
|
|
169
|
+
new_tags = [t for t in new_tags if t not in current]
|
|
170
|
+
if not new_tags:
|
|
171
|
+
return False
|
|
172
172
|
put = await client.post(
|
|
173
173
|
f"{self._base}/api/v1/media/user/bulk_actions",
|
|
174
174
|
json={
|
|
175
175
|
"action": "add_tags",
|
|
176
176
|
"media_ids": [token],
|
|
177
|
-
"tag_titles":
|
|
177
|
+
"tag_titles": new_tags,
|
|
178
178
|
},
|
|
179
179
|
)
|
|
180
180
|
return put.status_code in (200, 201)
|
|
181
181
|
except (httpx.HTTPError, json.JSONDecodeError, ValueError):
|
|
182
182
|
return False
|
|
183
|
-
|
|
184
|
-
async def _reverse_sync(self, client: httpx.AsyncClient, token: str) -> None:
|
|
185
|
-
"""Insert any CMS-only tags into the local catalog_tags table."""
|
|
186
|
-
url = f"{self._base}/api/v1/media/{token}"
|
|
187
|
-
try:
|
|
188
|
-
resp = await client.get(url)
|
|
189
|
-
if resp.status_code != 200:
|
|
190
|
-
return
|
|
191
|
-
data = resp.json()
|
|
192
|
-
for t in data.get("tags_info") or []:
|
|
193
|
-
name = t.get("title") if isinstance(t, dict) else t
|
|
194
|
-
if name:
|
|
195
|
-
await self._db.add_catalog_tag(token, str(name))
|
|
196
|
-
except (httpx.HTTPError, json.JSONDecodeError, ValueError) as exc:
|
|
197
|
-
logger.warning("[tags] Failed reverse-sync for %s: %s", token, exc)
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/title.py
RENAMED
|
@@ -7,7 +7,7 @@ from datetime import datetime, UTC
|
|
|
7
7
|
import httpx
|
|
8
8
|
|
|
9
9
|
from ..classify import ItemClassification
|
|
10
|
-
from ..normalise import
|
|
10
|
+
from ..normalise import normalize_movie_title
|
|
11
11
|
from ..report import StepResult
|
|
12
12
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
@@ -83,7 +83,9 @@ class TitleStep:
|
|
|
83
83
|
"""Return the desired CMS title, or None if no change needed."""
|
|
84
84
|
ct = cls.content_type
|
|
85
85
|
if ct == "movie":
|
|
86
|
-
|
|
86
|
+
# Display title keeps sub markers (dubbed is stripped); mirrors the
|
|
87
|
+
# search cleanup so pipe/YouTube titles are tidied consistently.
|
|
88
|
+
clean, year = normalize_movie_title(cls.raw_title, for_search=False)
|
|
87
89
|
if year:
|
|
88
90
|
candidate = f"{clean} ({year})"
|
|
89
91
|
else:
|
|
@@ -100,7 +102,6 @@ class TitleStep:
|
|
|
100
102
|
candidate = f"{t} - {show}"
|
|
101
103
|
return candidate if candidate != cls.raw_title else None
|
|
102
104
|
if ct == "riffed_movie" and cls.hosted:
|
|
103
|
-
entry_template = cls.hosted # HostedInfo has show_name
|
|
104
105
|
if "{title}" in (cls.hosted.show_name or ""):
|
|
105
106
|
return None # template handled below
|
|
106
107
|
# Only Rifftrax gets reformat; MST3K keeps original
|
|
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.36.4 → kryten_webqueue-0.36.6}/docs/Log for incorrect promo insertion problem.txt
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
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/pipeline.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/providers.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/report.py
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/art.py
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/meta.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/catalog/enrichment/steps/sync.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/enrichtv.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/cmsutils/fetchurls.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/integrations/ytpipe/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/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.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_job_schedules.py
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/routes/admin_moderation.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
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin-playlists.js
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin-queue-mgmt.js
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/static/js/admin-schedules.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/promos.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/catalog/item_detail.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/feedback/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/queue/index.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/user/dashboard.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.4 → kryten_webqueue-0.36.6}/kryten_webqueue/templates/user/my_list.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|