kryten-webqueue 0.32.2__tar.gz → 0.32.3__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.32.2 → kryten_webqueue-0.32.3}/CHANGELOG.md +7 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/PKG-INFO +1 -1
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/app.py +10 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/db/_catalog.py +43 -16
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/pyproject.toml +1 -1
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_recently_played_hide.py +33 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/.gitignore +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/README.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/config.example.json +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/Log for incorrect promo insertion problem.txt +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/db/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/db/_connection.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/db/_feedback.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/db/_playlists.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/db/_queue.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/queue/completion.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/queue/race_poller.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_feedback.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_moderation.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/feedback.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin-playlists.js +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin-promos.js +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin-schedules.js +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin.js +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/feedback/index.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/race.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/__init__.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_feedback.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_race_poller.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/tests/test_search_facets.py +0 -0
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.32.3] — 2026-07-09
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Tag/category-classified promos are now also exempt from recently-played hiding.** v0.32.2 only exempted promo-*pool* clips, but most station promos/bumpers (e.g. “CHANNEL Z …”) are classified by hidden category (`Z Channel Promos`) or tag (`channelz`, `bumpers`, …) instead, so they were still being recorded and appeared in the recently-played debug list. `record_play_completion` now skips any item hidden from the public catalog by a promo pool **or** a hidden category/tag. On startup the service self-heals by purging any such rows written by earlier builds (`purge_promo_hide_state`, now covering the tag/category case).
|
|
13
|
+
|
|
8
14
|
## [0.32.2] — 2026-07-09
|
|
9
15
|
|
|
10
16
|
### Fixed
|
|
@@ -35,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
35
41
|
[0.32.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.32.0
|
|
36
42
|
[0.32.1]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.32.1
|
|
37
43
|
[0.32.2]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.32.2
|
|
44
|
+
[0.32.3]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.32.3
|
|
38
45
|
|
|
39
46
|
## [0.31.0] — 2026-07-04
|
|
40
47
|
|
|
@@ -51,6 +51,16 @@ async def lifespan(app: FastAPI):
|
|
|
51
51
|
db = Database(config.db_path)
|
|
52
52
|
await db.connect()
|
|
53
53
|
await db.run_migrations()
|
|
54
|
+
# Self-heal: purge any recently-played hide state recorded for promos/bumpers
|
|
55
|
+
# (they are excluded from the public catalog and must never be hidden by the
|
|
56
|
+
# recently-played rules). Cheap and idempotent; also cleans rows written by
|
|
57
|
+
# older builds that classified promos only by pool membership.
|
|
58
|
+
purged = await db.purge_promo_hide_state()
|
|
59
|
+
if purged["completions"] or purged["playlist_pass"]:
|
|
60
|
+
logger.info(
|
|
61
|
+
"Purged promo recently-played state: %d completion(s), %d pass row(s)",
|
|
62
|
+
purged["completions"], purged["playlist_pass"],
|
|
63
|
+
)
|
|
54
64
|
# Any job run still marked 'running' is an orphan from a prior crash/restart
|
|
55
65
|
# (the running flag is in-memory only). Reconcile before registering jobs.
|
|
56
66
|
orphaned = await db.reconcile_orphaned_job_runs()
|
|
@@ -72,6 +72,33 @@ def _hidden_exclusion(alias: str = "c") -> tuple[str, list]:
|
|
|
72
72
|
return sql, [*HIDDEN_CATEGORY_NAMES, *HIDDEN_TAG_NAMES]
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def _promo_hidden_media_subquery() -> tuple[str, list]:
|
|
76
|
+
"""Subquery (+ params) yielding media_ids that are promos/bumpers.
|
|
77
|
+
|
|
78
|
+
"Promo/bumper" content is anything hidden from the public catalog by a promo
|
|
79
|
+
pool OR a hidden category OR a hidden tag (e.g. ``Z Channel Promos`` /
|
|
80
|
+
``channelz``). These items never appear to regular users, so they must never
|
|
81
|
+
be recorded as recently-played. Immutable *event* playlists are intentionally
|
|
82
|
+
excluded here — they are their own class, not promos.
|
|
83
|
+
"""
|
|
84
|
+
cat_ph = ",".join("?" * len(HIDDEN_CATEGORY_NAMES))
|
|
85
|
+
tag_ph = ",".join("?" * len(HIDDEN_TAG_NAMES))
|
|
86
|
+
sql = f"""
|
|
87
|
+
SELECT spi.media_id FROM saved_playlist_items spi
|
|
88
|
+
JOIN saved_playlists sp ON sp.id = spi.playlist_id
|
|
89
|
+
WHERE sp.promo_type IS NOT NULL AND spi.media_type = 'cm'
|
|
90
|
+
UNION
|
|
91
|
+
SELECT cc.friendly_token FROM catalog_categories cc
|
|
92
|
+
JOIN categories cat ON cc.category_id = cat.id
|
|
93
|
+
WHERE cat.name IN ({cat_ph})
|
|
94
|
+
UNION
|
|
95
|
+
SELECT ct.friendly_token FROM catalog_tags ct
|
|
96
|
+
JOIN tags t ON ct.tag_id = t.id
|
|
97
|
+
WHERE t.name IN ({tag_ph})
|
|
98
|
+
"""
|
|
99
|
+
return sql, [*HIDDEN_CATEGORY_NAMES, *HIDDEN_TAG_NAMES]
|
|
100
|
+
|
|
101
|
+
|
|
75
102
|
def _recently_played_exclusion(alias: str, days: int) -> tuple[str, list]:
|
|
76
103
|
"""SQL fragment (+ params) hiding recently-played items from regular users.
|
|
77
104
|
|
|
@@ -363,13 +390,14 @@ class _CatalogMixin:
|
|
|
363
390
|
|
|
364
391
|
Promo-pool clips are exempt entirely: they are already excluded from the
|
|
365
392
|
public catalog and must never be treated like normal (mutable/immutable)
|
|
366
|
-
playlist items, so playing one records nothing.
|
|
393
|
+
playlist items, so playing one records nothing. The same exemption covers
|
|
394
|
+
any promo/bumper hidden by category or tag (e.g. ``Z Channel Promos`` /
|
|
395
|
+
``channelz``), which is how most station promos are actually classified.
|
|
367
396
|
"""
|
|
397
|
+
sub, sub_params = _promo_hidden_media_subquery()
|
|
368
398
|
promo = await self._fetch_one(
|
|
369
|
-
"SELECT 1
|
|
370
|
-
|
|
371
|
-
"WHERE spi.media_id = ? AND spi.media_type = ? AND sp.promo_type IS NOT NULL LIMIT 1",
|
|
372
|
-
[friendly_token, media_type],
|
|
399
|
+
f"SELECT 1 WHERE ? IN ({sub})",
|
|
400
|
+
[friendly_token, *sub_params],
|
|
373
401
|
)
|
|
374
402
|
if promo:
|
|
375
403
|
return
|
|
@@ -448,26 +476,25 @@ class _CatalogMixin:
|
|
|
448
476
|
return {"completions": pc["c"] if pc else 0, "playlist_pass": pip["c"] if pip else 0}
|
|
449
477
|
|
|
450
478
|
async def purge_promo_hide_state(self) -> dict:
|
|
451
|
-
"""Remove any recently-played hide state recorded for
|
|
479
|
+
"""Remove any recently-played hide state recorded for promos/bumpers.
|
|
452
480
|
|
|
453
481
|
Promos must never be subject to recently-played hiding. New completions
|
|
454
482
|
are already skipped in ``record_play_completion``; this purges rows
|
|
455
|
-
written before that exemption existed (
|
|
483
|
+
written before that exemption existed (run once at startup). Covers promo
|
|
484
|
+
pools plus hidden-category/tag promos (the usual station-promo case).
|
|
456
485
|
Returns the row counts removed.
|
|
457
486
|
"""
|
|
458
|
-
|
|
459
|
-
"SELECT spi.media_id FROM saved_playlist_items spi "
|
|
460
|
-
"JOIN saved_playlists sp ON sp.id = spi.playlist_id "
|
|
461
|
-
"WHERE sp.promo_type IS NOT NULL AND spi.media_type = 'cm'"
|
|
462
|
-
)
|
|
487
|
+
sub, sub_params = _promo_hidden_media_subquery()
|
|
463
488
|
pc = await self._fetch_one(
|
|
464
|
-
f"SELECT COUNT(*) AS c FROM play_completions WHERE media_id IN ({
|
|
489
|
+
f"SELECT COUNT(*) AS c FROM play_completions WHERE media_id IN ({sub})",
|
|
490
|
+
sub_params,
|
|
465
491
|
)
|
|
466
492
|
pip = await self._fetch_one(
|
|
467
|
-
f"SELECT COUNT(*) AS c FROM playlist_item_played WHERE media_id IN ({
|
|
493
|
+
f"SELECT COUNT(*) AS c FROM playlist_item_played WHERE media_id IN ({sub})",
|
|
494
|
+
sub_params,
|
|
468
495
|
)
|
|
469
|
-
await self._execute(f"DELETE FROM play_completions WHERE media_id IN ({
|
|
470
|
-
await self._execute(f"DELETE FROM playlist_item_played WHERE media_id IN ({
|
|
496
|
+
await self._execute(f"DELETE FROM play_completions WHERE media_id IN ({sub})", sub_params)
|
|
497
|
+
await self._execute(f"DELETE FROM playlist_item_played WHERE media_id IN ({sub})", sub_params)
|
|
471
498
|
return {"completions": pc["c"] if pc else 0, "playlist_pass": pip["c"] if pip else 0}
|
|
472
499
|
|
|
473
500
|
async def get_recently_played_debug(self, days: int) -> dict:
|
|
@@ -395,6 +395,39 @@ async def test_purge_promo_hide_state_removes_stale_rows(db):
|
|
|
395
395
|
assert (await db.get_recently_played_debug(21))["by_completion"] == []
|
|
396
396
|
|
|
397
397
|
|
|
398
|
+
async def test_hidden_tag_promo_is_exempt_from_hiding(db):
|
|
399
|
+
# Station promos/bumpers are usually classified by hidden tag/category
|
|
400
|
+
# (e.g. `channelz`), not promo-pool membership. They must still be exempt.
|
|
401
|
+
await _add_catalog(db, "bumper", "CHANNEL Z - ITS 10PM", duration_sec=15)
|
|
402
|
+
tid = await db.upsert_tag("channelz")
|
|
403
|
+
await db.set_catalog_tags("bumper", [tid])
|
|
404
|
+
|
|
405
|
+
await db.record_play_completion(friendly_token="bumper", duration_sec=15)
|
|
406
|
+
assert await db._fetch_all("SELECT media_id FROM play_completions") == []
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
async def test_hidden_category_promo_is_exempt_from_hiding(db):
|
|
410
|
+
await _add_catalog(db, "bumper", "Z Promo", duration_sec=15)
|
|
411
|
+
cid = await db.upsert_category("Z Channel Promos")
|
|
412
|
+
await db.set_catalog_categories("bumper", [cid])
|
|
413
|
+
|
|
414
|
+
await db.record_play_completion(friendly_token="bumper", duration_sec=15)
|
|
415
|
+
assert await db._fetch_all("SELECT media_id FROM play_completions") == []
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
async def test_purge_removes_hidden_tag_promo_rows(db):
|
|
419
|
+
# A stale row written before the tag/category exemption is cleaned by purge.
|
|
420
|
+
await _add_catalog(db, "bumper", "CHANNEL Z - ITS 10PM", duration_sec=15)
|
|
421
|
+
tid = await db.upsert_tag("channelz")
|
|
422
|
+
await db.set_catalog_tags("bumper", [tid])
|
|
423
|
+
await db._execute("INSERT INTO play_completions (media_type, media_id) VALUES ('cm', 'bumper')")
|
|
424
|
+
|
|
425
|
+
removed = await db.purge_promo_hide_state()
|
|
426
|
+
assert removed["completions"] == 1
|
|
427
|
+
assert await db._fetch_all("SELECT media_id FROM play_completions") == []
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
|
|
398
431
|
|
|
399
432
|
|
|
400
433
|
|
|
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.32.2 → kryten_webqueue-0.32.3}/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
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/enrichtv.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/cmsutils/fetchurls.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/integrations/ytpipe/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/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
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/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.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin-playlists.js
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin-queue-mgmt.js
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/static/js/admin-schedules.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/promos.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/catalog/item_detail.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/feedback/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/kryten_webqueue/templates/queue/index.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.32.2 → kryten_webqueue-0.32.3}/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
|