kryten-webqueue 0.29.0__tar.gz → 0.31.0__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.29.0 → kryten_webqueue-0.31.0}/CHANGELOG.md +21 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/PKG-INFO +1 -1
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/app.py +3 -1
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/sync.py +13 -2
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/jobs/manager.py +30 -7
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin.js +6 -1
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/pyproject.toml +1 -1
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/.gitignore +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/README.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/config.example.json +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/Log for incorrect promo insertion problem.txt +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/db/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/db/_catalog.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/db/_connection.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/db/_feedback.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/db/_playlists.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/db/_queue.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/queue/race_poller.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_feedback.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_moderation.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/feedback.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin-playlists.js +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin-promos.js +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin-schedules.js +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/feedback/index.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/race.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/__init__.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_feedback.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_race_poller.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/tests/test_search_facets.py +0 -0
|
@@ -5,6 +5,27 @@ 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.31.0] — 2026-07-04
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Moderation list "Remove" button now works.** The button was rendered with `onclick="removeModEntry(${JSON.stringify(e.username)})"` — `JSON.stringify` wraps the value in double quotes that immediately terminate the `onclick="…"` attribute, so browsers reported *"unexpected end of input at admin:1:16"* and the function was never called. The button now uses a `data-mod-rm-username` attribute with a delegated `click` listener on the stable `#mod-entries-list` container, consistent with how the "Moderate…" buttons in the Recent Users table work. The existing `confirm()` dialog inside `removeModEntry` is unaffected and will now prompt as intended.
|
|
13
|
+
|
|
14
|
+
[0.31.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.31.0
|
|
15
|
+
|
|
16
|
+
## [0.30.0] — 2026-07-04
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Clean shutdown when a catalog sync (or other job) is in progress.** Previously, shutting the server down while a background job was running caused a cascade of WARNING/ERROR log lines as the database connection and HTTP client were closed underneath the still-running job task. Three changes fix this:
|
|
21
|
+
- `JobManager` gains a `stop()` method that cancels all in-flight task and awaits them.
|
|
22
|
+
- The lifespan shutdown now calls `job_manager.stop()` *before* closing the database, HTTP clients, or any other shared resource, so the cancelled tasks can still record their final status cleanly.
|
|
23
|
+
- Background loop tasks (`bg_tasks`) are now properly awaited after cancellation, preventing "Task exception was never retrieved" warnings.
|
|
24
|
+
- `JobManager._execute` wraps every database write in a defensive `try/except` so that a race between task teardown and resource teardown never produces an unhandled exception or spurious ERROR log.
|
|
25
|
+
- `CatalogSync.sync` adds an explicit `asyncio.CancelledError` handler that logs a clean info message and re-raises (letting `JobManager` record the `cancelled` status), and wraps the error-path `finish_sync_log` calls defensively for the same reason.
|
|
26
|
+
|
|
27
|
+
[0.30.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.30.0
|
|
28
|
+
|
|
8
29
|
## [0.29.0] — 2026-07-04
|
|
9
30
|
|
|
10
31
|
### Changed
|
|
@@ -227,9 +227,11 @@ async def lifespan(app: FastAPI):
|
|
|
227
227
|
|
|
228
228
|
yield
|
|
229
229
|
|
|
230
|
-
# Shutdown
|
|
230
|
+
# Shutdown — order matters: cancel in-flight work before closing resources.
|
|
231
231
|
for task in bg_tasks:
|
|
232
232
|
task.cancel()
|
|
233
|
+
await asyncio.gather(*bg_tasks, return_exceptions=True)
|
|
234
|
+
await job_manager.stop() # cancel running jobs while DB/client still open
|
|
233
235
|
await poller.stop()
|
|
234
236
|
await race_poller.stop()
|
|
235
237
|
await scheduler.stop()
|
|
@@ -96,14 +96,25 @@ class CatalogSync:
|
|
|
96
96
|
|
|
97
97
|
await self._db.finish_sync_log(log_id, stats, "completed")
|
|
98
98
|
logger.info(f"Catalog sync complete: {stats} ({page} pages)")
|
|
99
|
+
except asyncio.CancelledError:
|
|
100
|
+
# Server is shutting down — propagate cleanly so JobManager can
|
|
101
|
+
# record the cancelled status while the DB is still open.
|
|
102
|
+
logger.info("Catalog sync cancelled (shutdown in progress)")
|
|
103
|
+
raise
|
|
99
104
|
except httpx.TransportError as exc:
|
|
100
105
|
logger.error(_describe_httpx_error(exc, f"{self._url}/api/v1/media"))
|
|
101
106
|
stats["errors"] += 1
|
|
102
|
-
|
|
107
|
+
try:
|
|
108
|
+
await self._db.finish_sync_log(log_id, stats, "error")
|
|
109
|
+
except Exception: # noqa: BLE001
|
|
110
|
+
logger.debug("Could not persist sync error status (DB unavailable)")
|
|
103
111
|
except Exception as exc:
|
|
104
112
|
logger.exception(f"Catalog sync failed: {type(exc).__name__}: {exc}")
|
|
105
113
|
stats["errors"] += 1
|
|
106
|
-
|
|
114
|
+
try:
|
|
115
|
+
await self._db.finish_sync_log(log_id, stats, "error")
|
|
116
|
+
except Exception: # noqa: BLE001
|
|
117
|
+
logger.debug("Could not persist sync error status (DB unavailable)")
|
|
107
118
|
|
|
108
119
|
async def _process_item(self, media: dict, stats: dict):
|
|
109
120
|
token = media.get("friendly_token")
|
|
@@ -158,6 +158,20 @@ class JobManager:
|
|
|
158
158
|
def is_running(self, name: str) -> bool:
|
|
159
159
|
return name in self._running
|
|
160
160
|
|
|
161
|
+
async def stop(self) -> None:
|
|
162
|
+
"""Cancel all running job tasks and wait for them to finish.
|
|
163
|
+
|
|
164
|
+
Call this during application shutdown *before* closing shared resources
|
|
165
|
+
(database, HTTP clients) so that in-progress tasks receive
|
|
166
|
+
``CancelledError`` while those resources are still usable.
|
|
167
|
+
"""
|
|
168
|
+
if not self._running:
|
|
169
|
+
return
|
|
170
|
+
logger.info("Stopping %d running job(s) for shutdown", len(self._running))
|
|
171
|
+
for task in list(self._running.values()):
|
|
172
|
+
task.cancel()
|
|
173
|
+
await asyncio.gather(*self._running.values(), return_exceptions=True)
|
|
174
|
+
|
|
161
175
|
async def run(self, name: str, *, triggered_by: str | None = None,
|
|
162
176
|
params: dict | None = None) -> dict:
|
|
163
177
|
"""Start a job in the background. Returns immediately.
|
|
@@ -191,19 +205,28 @@ class JobManager:
|
|
|
191
205
|
detail = json.dumps(result) if result is not None else None
|
|
192
206
|
await self._db.finish_job_run(run_id, "completed", detail)
|
|
193
207
|
except asyncio.CancelledError:
|
|
194
|
-
|
|
208
|
+
try:
|
|
209
|
+
await self._db.finish_job_run(run_id, "cancelled", None)
|
|
210
|
+
except Exception: # noqa: BLE001
|
|
211
|
+
logger.debug("Could not persist 'cancelled' status for job '%s' (DB unavailable)", name)
|
|
195
212
|
raise
|
|
196
213
|
except JobError as exc:
|
|
197
214
|
# Expected, user-facing failure (bad input/config): record a clean
|
|
198
215
|
# message and log without a stack trace.
|
|
199
216
|
logger.warning("Job '%s' failed: %s", name, exc)
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
217
|
+
try:
|
|
218
|
+
await self._db.finish_job_run(
|
|
219
|
+
run_id, "failed", json.dumps({"error": str(exc)})
|
|
220
|
+
)
|
|
221
|
+
except Exception: # noqa: BLE001
|
|
222
|
+
logger.debug("Could not persist 'failed' status for job '%s' (DB unavailable)", name)
|
|
203
223
|
except Exception as exc: # noqa: BLE001 - record any failure
|
|
204
224
|
logger.exception("Job '%s' failed", name)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
225
|
+
try:
|
|
226
|
+
await self._db.finish_job_run(
|
|
227
|
+
run_id, "failed", json.dumps({"error": f"{type(exc).__name__}: {exc}"})
|
|
228
|
+
)
|
|
229
|
+
except Exception: # noqa: BLE001
|
|
230
|
+
logger.debug("Could not persist 'failed' status for job '%s' (DB unavailable)", name)
|
|
208
231
|
finally:
|
|
209
232
|
self._running.pop(name, None)
|
|
@@ -527,7 +527,7 @@ function renderModEntries(el, entries) {
|
|
|
527
527
|
<td>${escapeHtml(e.reason || '—')}</td>
|
|
528
528
|
<td>${escapeHtml(e.moderator || '—')}</td>
|
|
529
529
|
<td>${e.added_at ? formatLocalDateTime(e.added_at) : '—'}</td>
|
|
530
|
-
<td><button class="btn btn-sm btn-danger"
|
|
530
|
+
<td><button class="btn btn-sm btn-danger" data-mod-rm-username="${escapeHtml(e.username)}">Remove</button></td>
|
|
531
531
|
</tr>`).join('')}
|
|
532
532
|
</table>`;
|
|
533
533
|
}
|
|
@@ -561,6 +561,11 @@ async function submitAddModEntry() {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
document.getElementById('mod-entries-refresh').addEventListener('click', loadModEntries);
|
|
564
|
+
|
|
565
|
+
document.getElementById('mod-entries-list').addEventListener('click', (e) => {
|
|
566
|
+
const btn = e.target.closest('[data-mod-rm-username]');
|
|
567
|
+
if (btn) removeModEntry(btn.dataset.modRmUsername);
|
|
568
|
+
});
|
|
564
569
|
document.querySelectorAll('.mod-filter-btn').forEach(btn => {
|
|
565
570
|
btn.addEventListener('click', () => {
|
|
566
571
|
document.querySelectorAll('.mod-filter-btn').forEach(b => b.classList.remove('active'));
|
|
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.29.0 → kryten_webqueue-0.31.0}/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.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/enrichtv.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/cmsutils/fetchurls.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/integrations/ytpipe/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/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
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/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.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin-playlists.js
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin-queue-mgmt.js
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/static/js/admin-schedules.js
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/promos.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/catalog/item_detail.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/feedback/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/kryten_webqueue/templates/queue/index.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.29.0 → kryten_webqueue-0.31.0}/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
|