kryten-webqueue 0.27.1__tar.gz → 0.28.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.27.1 → kryten_webqueue-0.28.0}/CHANGELOG.md +17 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/PKG-INFO +1 -1
- kryten_webqueue-0.28.0/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +111 -0
- kryten_webqueue-0.28.0/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +64 -0
- kryten_webqueue-0.28.0/docs/Log for incorrect promo insertion problem.txt +233 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/api_gate/client.py +70 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/app.py +2 -0
- kryten_webqueue-0.28.0/kryten_webqueue/catalog/db/__init__.py +17 -0
- kryten_webqueue-0.28.0/kryten_webqueue/catalog/db/_catalog.py +619 -0
- kryten_webqueue-0.28.0/kryten_webqueue/catalog/db/_connection.py +311 -0
- kryten_webqueue-0.28.0/kryten_webqueue/catalog/db/_feedback.py +99 -0
- kryten_webqueue-0.28.0/kryten_webqueue/catalog/db/_playlists.py +280 -0
- kryten_webqueue-0.28.0/kryten_webqueue/catalog/db/_queue.py +99 -0
- kryten_webqueue-0.28.0/kryten_webqueue/routes/admin_moderation.py +150 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/static/css/main.css +102 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/admin/index.html +368 -1
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/pyproject.toml +1 -1
- kryten_webqueue-0.27.1/kryten_webqueue/catalog/db.py +0 -1393
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/.gitignore +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/README.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/config.example.json +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/queue/race_poller.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/admin_feedback.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/feedback.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/feedback/index.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/race.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/__init__.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_feedback.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_race_poller.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.27.1 → kryten_webqueue-0.28.0}/tests/test_search_facets.py +0 -0
|
@@ -5,6 +5,23 @@ 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.28.0] — 2026-07-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Moderation tab on the admin dashboard.** A new "Moderation" tab provides full-coverage access to the kryten-moderator service without leaving the admin panel. Four sections are lazy-loaded on first open:
|
|
13
|
+
- *Service Status* — compact stat grid showing live health, version, uptime, users tracked, bans enforced, and mutes enforced (with a Refresh button).
|
|
14
|
+
- *Moderation List* — filterable table (All / Ban / SMute / Mute) of active entries, showing username, action badge, reason, issuing moderator, and timestamp. Entries can be removed individually (with confirmation); new entries are added via an inline "Add entry…" collapsible form (username, action, optional reason). The logged-in admin's username is recorded as the moderator automatically.
|
|
15
|
+
- *Username Patterns* — table of banned username patterns with regex indicator, action badge, description, and who added them. Patterns can be removed individually; new ones are added via an inline "Add pattern…" form with a regex toggle.
|
|
16
|
+
- *Recent Users* — configurable look-back window (minutes) loads a table of users recently seen in the channel, sorted by last-seen descending. Unmoderated users show a "Moderate…" button that opens a modal to pick an action and optional reason, immediately adding a moderation entry and refreshing both the recent-users and moderation-list tables.
|
|
17
|
+
- New `GET|POST|DELETE /admin/moderation/entries[/{username}]`, `GET|POST|DELETE /admin/moderation/patterns[/{pattern}]`, `GET /admin/moderation/recent`, and `GET /admin/moderation/status` proxy endpoints (admin-only); the configured `channel` is resolved server-side so it never needs to be specified in the UI.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **`catalog/db.py` split into `catalog/db/` package.** The monolithic database module has been broken into four focused mixins (`_CatalogMixin`, `_PlaylistsMixin`, `_QueueMixin`, `_FeedbackMixin`) wired together in a single `Database` class. Public API is unchanged.
|
|
22
|
+
|
|
23
|
+
[0.28.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.28.0
|
|
24
|
+
|
|
8
25
|
## [0.27.1] — 2026-06-23
|
|
9
26
|
|
|
10
27
|
### Fixed
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
## Kryten-Economy Service Survey Report
|
|
2
|
+
|
|
3
|
+
### 1. What It Does
|
|
4
|
+
|
|
5
|
+
**kryten-economy** (v0.13.0) is a standalone NATS-based microservice providing a channel engagement currency system for CyTube. It depends on `kryten-py` (NATS client) and operates independently — **no other workspace imports it as a Python dependency**.
|
|
6
|
+
|
|
7
|
+
| Workspace | References kryten-economy? |
|
|
8
|
+
|-----------|---------------------------|
|
|
9
|
+
| kryten-api-gate | **No** Python imports |
|
|
10
|
+
| kryten-py | **No** references |
|
|
11
|
+
| kryten-cli | **No** references |
|
|
12
|
+
| Kryten-Robot | **No** references |
|
|
13
|
+
| kryten-webqueue | HTTP reference via config (`web_queue_url`), no Python import |
|
|
14
|
+
|
|
15
|
+
It communicates via NATS pub-sub with Kryten-Robot (for user presence events) and kryten-api-gate (for queue spending). The architecture is clean and well-isolated.
|
|
16
|
+
|
|
17
|
+
### 2. File Size Analysis (Maintenance Concern)
|
|
18
|
+
|
|
19
|
+
The service consists of 35 source files totaling ~727 KB. Two files dominate:
|
|
20
|
+
|
|
21
|
+
| File | Size | % of Codebase | Issue |
|
|
22
|
+
|------|------|---------------|-------|
|
|
23
|
+
| `database.py` | **146 KB** | 20% | Single SQLite data-access layer with all tables, migrations, account ops, gambling, tips, schedules, etc. |
|
|
24
|
+
| `pm_handler.py` | **141 KB** | 19% | Massive PM command dispatcher handling all user-facing commands inline |
|
|
25
|
+
| `command_handler.py` | **62 KB** | 8.5% | NATS request-reply handler |
|
|
26
|
+
| `gambling_engine.py` | **39 KB** | 5.4% | Slots, flips, challenges, heists |
|
|
27
|
+
| `race_engine.py` | **42 KB** | 5.8% | Race betting engine |
|
|
28
|
+
| `config.py` | **45 KB** | 6.2% | Pydantic models for all 9 sprints |
|
|
29
|
+
| **Top 2 files** | **287 KB** | **39%** | The two largest files account for nearly 40% of the codebase |
|
|
30
|
+
|
|
31
|
+
### 3. Recommendations: Remove / Clean Up
|
|
32
|
+
|
|
33
|
+
#### A. `database.py` (146 KB) — **SPLIT INTO SUB-MODULES**
|
|
34
|
+
This is the single biggest maintainability issue. The file contains:
|
|
35
|
+
- `_create_tables()` with 30+ table definitions and migration logic (~50 lines of inline ALTER TABLE migrations)
|
|
36
|
+
- 60+ public async methods covering accounts, transactions, daily activity, streaks, bridges, hourly milestones, gambling stats, challenges, race results, trivia stats, blackjack stats, tips, approvals, vanity items, achievements, bounties, snapshots, bans, queue-spend requests, service metrics, and more
|
|
37
|
+
|
|
38
|
+
**Recommendation**: Split into:
|
|
39
|
+
- `database/__init__.py` — connection helpers, initialization, migrations
|
|
40
|
+
- `database/accounts.py` — get/create account, balance, credit/debit/refund
|
|
41
|
+
- `database/gambling.py` — spins, flips, challenges, race results, trivia, blackjack
|
|
42
|
+
- `database/transactions.py` — transaction history, daily activity
|
|
43
|
+
- `database/playlists.py` — queue spend requests
|
|
44
|
+
- `database/social.py` — tips, streaks, hourly milestones, vanity items
|
|
45
|
+
- `database/admin.py` — snapshots, bans, service metrics, bounties
|
|
46
|
+
|
|
47
|
+
#### B. `pm_handler.py` (141 KB) — **SPLIT INTO SUB-MODULES**
|
|
48
|
+
This is a single-file command dispatcher handling every user-facing command (`balance`, `tip`, `queue`, `search`, `spin`, `flip`, `heist`, `race`, `trivia`, `blackjack`, `achievements`, `rank`, `bounty`, `shop`, `fortune`, `shoutout`, `greeting`, `color`, `gif`, `help`, plus admin commands for bans, snapshots, config reload, etc.). It's 3,231 lines.
|
|
49
|
+
|
|
50
|
+
**Recommendation**: Split into:
|
|
51
|
+
- `pm_handler/__init__.py` — dispatcher, rate limiter, PM worker
|
|
52
|
+
- `pm_handler/gambling.py` — spin, flip, challenge, heist commands
|
|
53
|
+
- `pm_handler/games.py` — race, trivia, blackjack commands
|
|
54
|
+
- `pm_handler/shop.py` — vanity purchases (color, greeting, gif, shoutout, fortune)
|
|
55
|
+
- `pm_handler/admin.py` — admin commands (ban, snapshot, config reload, etc.)
|
|
56
|
+
- `pm_handler/queue.py` — queue, search, playnext commands
|
|
57
|
+
|
|
58
|
+
#### C. `config.py` (45 KB) — **CONSIDER REDUCING VERBOSITY**
|
|
59
|
+
The config has 50+ Pydantic models organized by sprint labels. While well-structured, many sub-configs reference LLM back-ends (`HeistLLMConfig`, `RaceLLMConfig`) that default to Ollama endpoints and may not be actively used depending on the `mode` setting (`static`/`llm`/`hybrid`). Similarly, `RaceOddsProfileConfig` with three complete 8-racer profiles defaults adds ~100 lines of inline defaults.
|
|
60
|
+
|
|
61
|
+
**Recommendation**: Move large default values (race odds profiles, payout tables) into separate data files or constants, keeping the config model lean.
|
|
62
|
+
|
|
63
|
+
#### D. Unused/Dead Config Fields
|
|
64
|
+
- `HeistConfig.enabled` defaults to `False` — heists are disabled by default and the feature depends on an LLM narrative mode. If not deployed, the entire heist subsystem (`heist_narrator.py`, `heist_narratives.py`) is dead code.
|
|
65
|
+
- `RaceLLMConfig` defaults to `llama3` on `localhost:11434` — the commentary `mode` defaults to `static`, so LLM-based race commentary is opt-in. The LLM config is only live if operators configure it.
|
|
66
|
+
|
|
67
|
+
#### E. Narrative Modules — CONDITIONAL DEAD CODE
|
|
68
|
+
- `heist_narratives.py` (22 KB) — built-in narrative templates, only used when `HeistConfig.narrative.mode` is `static` (the default, since `llm` requires an LLM endpoint)
|
|
69
|
+
- `heist_narrator.py` (13 KB) — bridges narratives to LLM or static library
|
|
70
|
+
- `race_narratives.py` (8 KB) — built-in race commentary templates
|
|
71
|
+
- `race_narrator.py` (17 KB) — bridges race commentary to LLM or static library
|
|
72
|
+
|
|
73
|
+
These are well-factored with a clean static/LLM/hybrid architecture. They're not *dead* code, but they account for 60 KB of narrative content that may be partially unused depending on deployment config. Consider documenting the dependency chain clearly.
|
|
74
|
+
|
|
75
|
+
### 4. Architectural Observations
|
|
76
|
+
|
|
77
|
+
#### A. Database Connection Pattern
|
|
78
|
+
Every public method opens a new SQLite connection via `run_in_executor`, and each one creates a WAL-mode connection with a 30-second busy timeout. While this works (SQLite WAL supports concurrent readers), it creates overhead for simple operations like `get_balance`. A connection pool (even a simple one with 1-2 cached connections) would improve latency for high-frequency operations.
|
|
79
|
+
|
|
80
|
+
#### B. Test Suite Health
|
|
81
|
+
|
|
82
|
+
The test suite is **comprehensive**: 90+ test files with a well-designed `conftest.py` providing shared fixtures for all major engines. Key observations:
|
|
83
|
+
- `conftest.py` (510 lines) is well-organized by sprint, with good use of `AsyncMock` and `MockKrytenClient`
|
|
84
|
+
- Tests import virtually everything from production code — no orphaned test files detected
|
|
85
|
+
- The `MockKrytenClient` class in conftest is excellent for integration tests
|
|
86
|
+
- No obviously skipped or always-failing tests found in the checked files
|
|
87
|
+
|
|
88
|
+
#### C. Cross-Workspace Isolation
|
|
89
|
+
|
|
90
|
+
Economy is architecturally pure: zero Python imports from other workspaces, communicates only via NATS. This is a strength — no coupling issues to worry about.
|
|
91
|
+
|
|
92
|
+
#### D. EventAnnouncer + GreetingHandler (Sprint 9)
|
|
93
|
+
These are clean, well-designed modules that centralize chat announcements through a deduplication/rate-limiting layer. They're properly wired in `EconomyApp.start()` and appear to be actively used.
|
|
94
|
+
|
|
95
|
+
#### E. MulitplierEngine + ScheduledEventManager
|
|
96
|
+
These form a clean multiplier pipeline. The `MultiplierEngine` correctly uses multiplicative composition and handles scheduled/adhoc/holiday/population multipliers. No issues found.
|
|
97
|
+
|
|
98
|
+
### 5. Summary of Actionable Recommendations
|
|
99
|
+
|
|
100
|
+
| Priority | Recommendation | Effort | Impact |
|
|
101
|
+
|----------|---------------|--------|--------|
|
|
102
|
+
| **High** | Split `database.py` (146 KB) into sub-modules | 1-2 days | Major — reduces cognitive load, enables focused testing |
|
|
103
|
+
| **High** | Split `pm_handler.py` (141 KB) into sub-modules | 1-2 days | Major — the file is too large to maintain effectively |
|
|
104
|
+
| **Medium** | Document which narrative/game features are active by default vs. opt-in | 1 hour | Prevents confusion about what's dead vs. dormant |
|
|
105
|
+
| **Medium** | Consider connection pooling for `database.py` | 0.5 day | Minor latency improvement for high-frequency ops |
|
|
106
|
+
| **Low** | Trim default race odds profiles from config.py into data file | 0.5 day | Minor — reduces config verbosity |
|
|
107
|
+
| **Low** | Default heist to enabled or document why it's off | 1 hour | Clarifies intent |
|
|
108
|
+
|
|
109
|
+
### 6. Overall Assessment
|
|
110
|
+
|
|
111
|
+
The codebase is **well-architected, well-documented, and actively maintained**. It follows a consistent pattern (sprint-based feature additions), has comprehensive test coverage (~90+ test files), and uses modern async Python patterns throughout. The major issues are structural: two files (`database.py` and `pm_handler.py`) together account for 40% of the entire codebase, making them difficult to navigate and maintain. Splitting these into sub-modules would significantly improve developer experience. No actual dead code requiring removal was found — the narrative/game modules are conditionally used based on configuration, which is an intentional architectural choice.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
## Kryten-Webqueue Service Survey Report
|
|
2
|
+
|
|
3
|
+
### 1. What It Does
|
|
4
|
+
|
|
5
|
+
kryten-webqueue is a standalone FastAPI web service (v0.27.1) that provides a catalog browser and pay-to-play queue management for CyTube, integrating with `kryten-api-gate` as its backend. It exposes:
|
|
6
|
+
|
|
7
|
+
- **Catalog** — A Netflix/Tubi-style browsable media catalog synced from MediaCMS, with TMDB/OMDB cover art resolution, FTS5 search, and facet filtering
|
|
8
|
+
- **Queue** — Live CyTube playlist mirroring (shadow), WebSocket broadcasts of queue state, estimated start times, and promo insertion
|
|
9
|
+
- **Auth** — OTP-based login + JWT sessions for the web UI
|
|
10
|
+
- **Scheduled Events** — Recurring and one-shot playlist scheduling with immutability locks
|
|
11
|
+
- **Promos** — Just-in-time insertion of promo clips (channel identity, events, mod shoutouts) between content, plus lead-in promos before movies/pay items
|
|
12
|
+
- **Jobs** — Background enrichment (title/meta/TV metadata from cmsutils), video fetching (yt-pipe downloader), and weekend workbook URL resolution (fetchurls from SharePoint)
|
|
13
|
+
- **Feedback** — Viewer feedback/suggestion triage
|
|
14
|
+
- **Presence Refund** — Auto-cancel+refund of pending paid items when the owner leaves/AFK
|
|
15
|
+
|
|
16
|
+
### 2. Service Dependencies
|
|
17
|
+
|
|
18
|
+
The webqueue service is a **consumer** of `kryten-api-gate` (via HTTP client in `api_gate/client.py`), not a Python dependency of any other service. Cross-workspace search results:
|
|
19
|
+
|
|
20
|
+
| Workspace | Imports kryten-webqueue? |
|
|
21
|
+
|-----------|-------------------------|
|
|
22
|
+
| kryten-api-gate | **No** Python imports |
|
|
23
|
+
| kryten-py | **No** references |
|
|
24
|
+
| kryten-cli | **No** references |
|
|
25
|
+
| Kryten-Robot | **No** Python imports (only CHANGELOG mentions) |
|
|
26
|
+
| kryten-economy | References via HTTP URL (`queue_url`), not Python imports |
|
|
27
|
+
|
|
28
|
+
It is self-contained — communicates with api-gate over HTTP, not via shared Python code.
|
|
29
|
+
|
|
30
|
+
### 3. Suggested Improvements
|
|
31
|
+
|
|
32
|
+
#### Remove / Clean Up
|
|
33
|
+
|
|
34
|
+
- **`config.prometheus_port` (config.py:160)** — Declared but never used. app.py mounts no Prometheus endpoint, no middleware references it. Remove the field or implement the endpoint.
|
|
35
|
+
- **`catalog.db._facet_filter()` (db.py:76-103)** — Defined but never called. The `browse()`, `browse_count()`, `search()`, and `search_count()` methods inline their own category/tag filtering SQL instead of using this helper. Remove as dead code.
|
|
36
|
+
- **`catalog.db._slugify()` (db.py:7-12)** — Only used inside `upsert_category()`. Could be inlined or left as is (low priority).
|
|
37
|
+
- **`integrations/` vendored code** — Contains vendored copies of `cmsutils` and `yt-pipe` tools (noted in `integrations/__init__.py`). These are duplicated from the `cmsutils` repo. Consider whether they could be installed as a proper Python dependency instead of vendored, to reduce drift. The overhead may be justified by operating in an air-gapped environment.
|
|
38
|
+
- **Empty `__init__.py` files** — `api_gate/__init__.py`, `auth/__init__.py`, `catalog/__init__.py`, `queue/__init__.py`, `playlists/__init__.py`, `ws/__init__.py` are empty. Not a problem per se, but if all are namespace-only they could be consolidated.
|
|
39
|
+
|
|
40
|
+
#### Architectural / Maintainability
|
|
41
|
+
|
|
42
|
+
- **`catalog/db.py` (1,393 lines)** — This file is a massive SQLite data-access layer covering catalog, OTPs, queue shadow, playlists, schedules, feedback, spend requests, and job runs. Consider splitting into sub-modules:
|
|
43
|
+
- `db/__init__.py` (connection, migrations)
|
|
44
|
+
- `db/catalog.py` (browse, search, facets, sync_log)
|
|
45
|
+
- `db/playlists.py` (CRUD, schedules, active schedule)
|
|
46
|
+
- `db/queue.py` (shadow, spend requests, history)
|
|
47
|
+
- `db/feedback.py` (feedback, title suggestions)
|
|
48
|
+
- **`promos/director.py` (547 lines)** — Very dense single class. Consider splitting the promo decision logic (`_general_due`, `_pick_general_type`, `_select_clip`, `_leadin_type_for`) into a separate strategy module.
|
|
49
|
+
- **`app.py` lifespan (lines 44-240)** — The single `lifespan` function initializes 15+ services. Consider extracting startup into a dedicated `services.py` module with factory functions.
|
|
50
|
+
- **Duplicate order logic in `queue/ordering.py`** — Unclear from naming, but there may be overlap with shadow position logic. Verify if intended.
|
|
51
|
+
|
|
52
|
+
#### Minor Observations
|
|
53
|
+
|
|
54
|
+
- **`pygame` or missing optional deps** — `pyproject.toml` lists `openpyxl`, `msal`, `yt-dlp` as core deps, but some are only used by the fetchurls/fetch jobs. Could be moved to `[project.optional-dependencies] jobs` to keep the base install lean.
|
|
55
|
+
- **`promo_log_level` in config.py:116** — Well-documented and used, good practice. Commendable logging design.
|
|
56
|
+
- **`_thread_safe_progress` closure in jobs/tasks.py:39** — Uses `asyncio.run_coroutine_threadsafe` with fire-and-forget. Consider adding a bounded queue or callback list instead if dropped progress callbacks become an issue.
|
|
57
|
+
|
|
58
|
+
### 4. Overall Assessment
|
|
59
|
+
|
|
60
|
+
**The codebase is healthy, well-maintained, and actively developed.** The code is well-documented with docstrings, inline comments, and thoughtful edge-case handling (orphaned job recovery, auto-lifting event locks, thread-safe progress reporting).
|
|
61
|
+
|
|
62
|
+
No dead, dangerously unused, or duplicate code was found — the two exceptions (`prometheus_port`, `_facet_filter`) are minor. The service occupies a clear architectural niche: it's the frontend-facing web service that bridges CyTube/MediaCMS with the internal api-gate, and it's not duplicated elsewhere.
|
|
63
|
+
|
|
64
|
+
The biggest actionable recommendation is **splitting `catalog/db.py`** into manageable sub-modules and either **removing or implementing the `prometheus_port` config field**.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
2026-06-17 08:06:00 INFO apscheduler.scheduler: Removed job schedule_10
|
|
2
|
+
2026-06-17 08:06:00 INFO apscheduler.executors.default: Running job "PlaylistScheduler._fire (trigger: date[2026-06-17 08:06:00 UTC], next run at: 2026-06-17 08:06:00 UTC)" (scheduled at 2026-06-17 08:06:00+00:00)
|
|
3
|
+
2026-06-17 08:06:00 INFO httpx: HTTP Request: DELETE https://www.dropsugar.co:8443/api/v1/playlist/ "HTTP/1.1 200 OK"
|
|
4
|
+
2026-06-17 08:06:00 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
5
|
+
2026-06-17 08:06:00 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
6
|
+
2026-06-17 08:06:00 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
7
|
+
2026-06-17 08:06:00 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
8
|
+
2026-06-17 08:06:01 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
9
|
+
2026-06-17 08:06:02 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
10
|
+
2026-06-17 08:06:02 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
11
|
+
2026-06-17 08:06:02 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
12
|
+
2026-06-17 08:06:02 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
13
|
+
2026-06-17 08:06:02 INFO uvicorn.error: connection closed
|
|
14
|
+
2026-06-17 08:06:03 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
15
|
+
2026-06-17 08:06:03 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
16
|
+
2026-06-17 08:06:03 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
17
|
+
2026-06-17 08:06:03 DEBUG kryten_webqueue.promos.director: Now-playing advanced: 20222 -> 20268 (is_promo=False) content_since_last_general=1
|
|
18
|
+
2026-06-17 08:06:03 DEBUG kryten_webqueue.promos.director: General promo due: content_since_last=1 >= every_n_items=1
|
|
19
|
+
2026-06-17 08:06:03 DEBUG kryten_webqueue.promos.director: Promo general-type pick: chosen=event candidates=['channel_identity', 'event', 'mod_shoutout'] weights=[1, 3, 2] skipped=[]
|
|
20
|
+
2026-06-17 08:06:03 DEBUG kryten_webqueue.promos.director: Promo clip select [event] order=random pool_size=1 no_repeat=True avoided_repeat=False -> media_id=https://www.dropsugar.co/api/v1/media/cytube/ifwkEvlG5.json?format=json title='Weekend At Channel Z'
|
|
21
|
+
2026-06-17 08:06:03 WARNING kryten_webqueue.promos.director: Promo pool for 'event' has a single clip; it will repeat every time (media_id=https://www.dropsugar.co/api/v1/media/cytube/ifwkEvlG5.json?format=json). Add more clips to vary this promo type.
|
|
22
|
+
2026-06-17 08:06:03 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
23
|
+
2026-06-17 08:06:03 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
24
|
+
2026-06-17 08:06:03 INFO httpx: HTTP Request: PUT https://www.dropsugar.co:8443/api/v1/playlist/20273/move "HTTP/1.1 200 OK"
|
|
25
|
+
2026-06-17 08:06:03 DEBUG kryten_webqueue.promos.director: Promo move ok: uid=20273 after_uid=20268
|
|
26
|
+
2026-06-17 08:06:03 INFO kryten_webqueue.promos.director: Inserted event promo uid=20273 (Weekend At Channel Z) (general)
|
|
27
|
+
2026-06-17 08:06:03 INFO kryten_webqueue.promos.director: Promo general inserted: type=event uid=20273 before content_uid=20269 (reset cadence counter from 1)
|
|
28
|
+
2026-06-17 08:06:03 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/917b7d9c/200.webp HTTP/1.1" 200
|
|
29
|
+
2026-06-17 08:06:03 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/5ff18426/200.webp HTTP/1.1" 200
|
|
30
|
+
2026-06-17 08:06:03 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/ab0b2723/200.webp HTTP/1.1" 200
|
|
31
|
+
2026-06-17 08:06:03 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/c16e912f/200.webp HTTP/1.1" 200
|
|
32
|
+
2026-06-17 08:06:04 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
33
|
+
2026-06-17 08:06:04 INFO uvicorn.access: 23.17.200.195:0 - "GET /images/917b7d9c/200.webp HTTP/1.1" 200
|
|
34
|
+
2026-06-17 08:06:04 INFO uvicorn.access: 23.17.200.195:0 - "GET /images/5ff18426/200.webp HTTP/1.1" 200
|
|
35
|
+
2026-06-17 08:06:04 INFO uvicorn.access: 23.17.200.195:0 - "GET /images/c16e912f/200.webp HTTP/1.1" 200
|
|
36
|
+
2026-06-17 08:06:04 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
37
|
+
2026-06-17 08:06:04 INFO uvicorn.access: 23.17.200.195:0 - "GET /images/ab0b2723/200.webp HTTP/1.1" 200
|
|
38
|
+
2026-06-17 08:06:05 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
39
|
+
2026-06-17 08:06:05 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
40
|
+
2026-06-17 08:06:06 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
41
|
+
2026-06-17 08:06:06 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
42
|
+
2026-06-17 08:06:06 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
43
|
+
2026-06-17 08:06:06 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
44
|
+
2026-06-17 08:06:06 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/b07442aa/200.webp HTTP/1.1" 200
|
|
45
|
+
2026-06-17 08:06:06 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/225c9eed/200.webp HTTP/1.1" 200
|
|
46
|
+
2026-06-17 08:06:06 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/b0ef3cd9/200.webp HTTP/1.1" 304
|
|
47
|
+
2026-06-17 08:06:06 INFO uvicorn.access: 172.92.128.89:0 - "GET /images/e94639e0/200.webp HTTP/1.1" 304
|
|
48
|
+
2026-06-17 08:06:07 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
49
|
+
2026-06-17 08:06:07 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
50
|
+
2026-06-17 08:06:07 INFO uvicorn.error: connection closed
|
|
51
|
+
2026-06-17 08:06:07 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
52
|
+
2026-06-17 08:06:07 INFO uvicorn.access: 23.17.200.195:0 - "GET /images/225c9eed/200.webp HTTP/1.1" 200
|
|
53
|
+
2026-06-17 08:06:07 INFO uvicorn.access: 23.17.200.195:0 - "GET /images/b07442aa/200.webp HTTP/1.1" 200
|
|
54
|
+
2026-06-17 08:06:07 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
55
|
+
2026-06-17 08:06:07 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/UGkmno18L.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
56
|
+
2026-06-17 08:06:08 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
57
|
+
2026-06-17 08:06:08 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/UGkmno18L.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
58
|
+
2026-06-17 08:06:09 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
59
|
+
2026-06-17 08:06:09 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
60
|
+
2026-06-17 08:06:09 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
61
|
+
2026-06-17 08:06:09 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
62
|
+
2026-06-17 08:06:10 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
63
|
+
2026-06-17 08:06:10 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
64
|
+
2026-06-17 08:06:11 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
65
|
+
2026-06-17 08:06:11 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
66
|
+
2026-06-17 08:06:11 INFO uvicorn.error: connection closed
|
|
67
|
+
2026-06-17 08:06:11 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
68
|
+
2026-06-17 08:06:11 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/3LQcZOfAk.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
69
|
+
2026-06-17 08:06:11 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
70
|
+
2026-06-17 08:06:12 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
71
|
+
2026-06-17 08:06:12 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
72
|
+
2026-06-17 08:06:13 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
73
|
+
2026-06-17 08:06:13 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
74
|
+
2026-06-17 08:06:13 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
75
|
+
2026-06-17 08:06:13 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/nNKbgmhLZ.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
76
|
+
2026-06-17 08:06:13 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
77
|
+
2026-06-17 08:06:14 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
78
|
+
2026-06-17 08:06:14 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
79
|
+
2026-06-17 08:06:15 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
80
|
+
2026-06-17 08:06:15 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
81
|
+
2026-06-17 08:06:15 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
82
|
+
2026-06-17 08:06:15 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
83
|
+
2026-06-17 08:06:15 INFO uvicorn.error: connection closed
|
|
84
|
+
2026-06-17 08:06:16 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
85
|
+
2026-06-17 08:06:16 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
86
|
+
2026-06-17 08:06:16 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
87
|
+
2026-06-17 08:06:16 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
88
|
+
2026-06-17 08:06:17 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
89
|
+
2026-06-17 08:06:17 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/7PblzWNCO.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
90
|
+
2026-06-17 08:06:17 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
91
|
+
2026-06-17 08:06:18 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
92
|
+
2026-06-17 08:06:18 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
93
|
+
2026-06-17 08:06:18 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/YOhlg3oLS.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
94
|
+
2026-06-17 08:06:19 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
95
|
+
2026-06-17 08:06:19 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
96
|
+
2026-06-17 08:06:19 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
97
|
+
2026-06-17 08:06:19 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
98
|
+
2026-06-17 08:06:19 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/YOhlg3oLS.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
99
|
+
2026-06-17 08:06:20 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
100
|
+
2026-06-17 08:06:20 WARNING kryten_webqueue.playlists.fire: Schedule fire: failed to add fallback https://www.dropsugar.co/api/v1/media/cytube/YOhlg3oLS.json?format=json: Client error '422 Unprocessable Entity' for url 'https://www.dropsugar.co:8443/api/v1/playlist/add'
|
|
101
|
+
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
|
102
|
+
2026-06-17 08:06:21 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
103
|
+
2026-06-17 08:06:21 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
104
|
+
2026-06-17 08:06:21 INFO uvicorn.error: connection closed
|
|
105
|
+
2026-06-17 08:06:21 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
106
|
+
2026-06-17 08:06:22 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
107
|
+
2026-06-17 08:06:22 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/ru9y7hgKw.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
108
|
+
2026-06-17 08:06:22 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
109
|
+
2026-06-17 08:06:22 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
110
|
+
2026-06-17 08:06:22 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
111
|
+
2026-06-17 08:06:22 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
112
|
+
2026-06-17 08:06:22 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/ru9y7hgKw.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
113
|
+
2026-06-17 08:06:23 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
114
|
+
2026-06-17 08:06:23 WARNING kryten_webqueue.playlists.fire: Schedule fire: failed to add fallback https://www.dropsugar.co/api/v1/media/cytube/ru9y7hgKw.json?format=json: Client error '422 Unprocessable Entity' for url 'https://www.dropsugar.co:8443/api/v1/playlist/add'
|
|
115
|
+
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
|
116
|
+
2026-06-17 08:06:24 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
117
|
+
2026-06-17 08:06:24 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/FJHG9gZaf.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
118
|
+
2026-06-17 08:06:25 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
119
|
+
2026-06-17 08:06:25 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/FJHG9gZaf.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
120
|
+
2026-06-17 08:06:25 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
121
|
+
2026-06-17 08:06:25 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
122
|
+
2026-06-17 08:06:25 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
123
|
+
2026-06-17 08:06:25 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
124
|
+
2026-06-17 08:06:25 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
125
|
+
2026-06-17 08:06:25 INFO uvicorn.error: connection closed
|
|
126
|
+
2026-06-17 08:06:26 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
127
|
+
2026-06-17 08:06:26 WARNING kryten_webqueue.playlists.fire: Schedule fire: failed to add fallback https://www.dropsugar.co/api/v1/media/cytube/FJHG9gZaf.json?format=json: Client error '422 Unprocessable Entity' for url 'https://www.dropsugar.co:8443/api/v1/playlist/add'
|
|
128
|
+
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
|
129
|
+
2026-06-17 08:06:26 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
130
|
+
2026-06-17 08:06:26 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/6Hf7ozMdm.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
131
|
+
2026-06-17 08:06:27 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
132
|
+
2026-06-17 08:06:27 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/6Hf7ozMdm.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
133
|
+
2026-06-17 08:06:28 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
134
|
+
2026-06-17 08:06:28 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
135
|
+
2026-06-17 08:06:28 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
136
|
+
2026-06-17 08:06:28 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
137
|
+
2026-06-17 08:06:28 WARNING kryten_webqueue.playlists.fire: Schedule fire: failed to add fallback https://www.dropsugar.co/api/v1/media/cytube/6Hf7ozMdm.json?format=json: Client error '422 Unprocessable Entity' for url 'https://www.dropsugar.co:8443/api/v1/playlist/add'
|
|
138
|
+
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
|
139
|
+
2026-06-17 08:06:29 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
140
|
+
2026-06-17 08:06:29 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
141
|
+
2026-06-17 08:06:30 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
142
|
+
2026-06-17 08:06:30 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/SAmW9PeQp.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
143
|
+
2026-06-17 08:06:30 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
144
|
+
2026-06-17 08:06:30 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/SAmW9PeQp.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
145
|
+
2026-06-17 08:06:31 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
146
|
+
2026-06-17 08:06:31 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
147
|
+
2026-06-17 08:06:31 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
148
|
+
2026-06-17 08:06:31 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
149
|
+
2026-06-17 08:06:31 WARNING kryten_webqueue.playlists.fire: Schedule fire: failed to add fallback https://www.dropsugar.co/api/v1/media/cytube/SAmW9PeQp.json?format=json: Client error '422 Unprocessable Entity' for url 'https://www.dropsugar.co:8443/api/v1/playlist/add'
|
|
150
|
+
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
|
151
|
+
2026-06-17 08:06:32 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
152
|
+
2026-06-17 08:06:32 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
153
|
+
2026-06-17 08:06:32 INFO uvicorn.error: connection closed
|
|
154
|
+
2026-06-17 08:06:32 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
155
|
+
2026-06-17 08:06:33 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
156
|
+
2026-06-17 08:06:33 INFO uvicorn.access: 172.92.128.89:0 - "GET /queue/next-schedule HTTP/1.1" 200
|
|
157
|
+
2026-06-17 08:06:34 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
158
|
+
2026-06-17 08:06:34 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
159
|
+
2026-06-17 08:06:34 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
160
|
+
2026-06-17 08:06:34 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
161
|
+
2026-06-17 08:06:34 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
162
|
+
2026-06-17 08:06:35 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
163
|
+
2026-06-17 08:06:35 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
164
|
+
2026-06-17 08:06:35 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/bakcYUoIu.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
165
|
+
2026-06-17 08:06:36 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
166
|
+
2026-06-17 08:06:36 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/bakcYUoIu.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
167
|
+
2026-06-17 08:06:37 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
168
|
+
2026-06-17 08:06:37 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
169
|
+
2026-06-17 08:06:37 INFO uvicorn.error: connection closed
|
|
170
|
+
2026-06-17 08:06:37 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
171
|
+
2026-06-17 08:06:37 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
172
|
+
2026-06-17 08:06:37 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
173
|
+
2026-06-17 08:06:37 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
174
|
+
2026-06-17 08:06:38 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
175
|
+
2026-06-17 08:06:38 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
176
|
+
2026-06-17 08:06:38 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/8i1YYvQgK.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
177
|
+
2026-06-17 08:06:39 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
178
|
+
2026-06-17 08:06:39 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/8i1YYvQgK.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
179
|
+
2026-06-17 08:06:40 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
180
|
+
2026-06-17 08:06:40 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
181
|
+
2026-06-17 08:06:40 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
182
|
+
2026-06-17 08:06:40 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
183
|
+
2026-06-17 08:06:41 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
184
|
+
2026-06-17 08:06:41 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/5xKNhLWM2.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
185
|
+
2026-06-17 08:06:41 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
186
|
+
2026-06-17 08:06:42 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
187
|
+
2026-06-17 08:06:42 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
188
|
+
2026-06-17 08:06:42 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
189
|
+
2026-06-17 08:06:42 INFO uvicorn.error: connection closed
|
|
190
|
+
2026-06-17 08:06:43 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
191
|
+
2026-06-17 08:06:43 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
192
|
+
2026-06-17 08:06:43 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
193
|
+
2026-06-17 08:06:43 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
194
|
+
2026-06-17 08:06:43 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
195
|
+
2026-06-17 08:06:44 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
196
|
+
2026-06-17 08:06:44 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
197
|
+
2026-06-17 08:06:44 INFO uvicorn.access: 98.97.86.89:0 - "GET /queue/next-schedule HTTP/1.1" 401
|
|
198
|
+
2026-06-17 08:06:45 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
199
|
+
2026-06-17 08:06:45 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
200
|
+
2026-06-17 08:06:46 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
201
|
+
2026-06-17 08:06:46 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/gDlFCp1ZQ.json?format=json (422 queueFail); retry 1/2 after 0.5s
|
|
202
|
+
2026-06-17 08:06:46 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
203
|
+
2026-06-17 08:06:46 INFO kryten_webqueue.playlists.bulk_add: CyTube rejected https://www.dropsugar.co/api/v1/media/cytube/gDlFCp1ZQ.json?format=json (422 queueFail); retry 2/2 after 1.0s
|
|
204
|
+
2026-06-17 08:06:46 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
205
|
+
2026-06-17 08:06:46 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
206
|
+
2026-06-17 08:06:46 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
207
|
+
2026-06-17 08:06:47 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 422 Unprocessable Entity"
|
|
208
|
+
2026-06-17 08:06:47 WARNING kryten_webqueue.playlists.fire: Schedule fire: failed to add fallback https://www.dropsugar.co/api/v1/media/cytube/gDlFCp1ZQ.json?format=json: Client error '422 Unprocessable Entity' for url 'https://www.dropsugar.co:8443/api/v1/playlist/add'
|
|
209
|
+
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
|
|
210
|
+
2026-06-17 08:06:48 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
211
|
+
2026-06-17 08:06:48 INFO uvicorn.error: 98.97.86.89:0 - "WebSocket /ws" 403
|
|
212
|
+
2026-06-17 08:06:48 INFO uvicorn.error: connection rejected (403 Forbidden)
|
|
213
|
+
2026-06-17 08:06:48 INFO uvicorn.error: connection closed
|
|
214
|
+
2026-06-17 08:06:49 INFO httpx: HTTP Request: POST https://www.dropsugar.co:8443/api/v1/playlist/add "HTTP/1.1 200 OK"
|
|
215
|
+
2026-06-17 08:06:49 INFO kryten_webqueue.playlists.fire: Schedule 10: appended 42 fallback item(s) from playlist 13
|
|
216
|
+
2026-06-17 08:06:49 INFO kryten_webqueue.playlists.fire: Schedule 10 fired: playlist 'Hollis Testing' (6 items)
|
|
217
|
+
2026-06-17 08:06:49 INFO apscheduler.executors.default: Job "PlaylistScheduler._fire (trigger: date[2026-06-17 08:06:00 UTC], next run at: 2026-06-17 08:06:00 UTC)" executed successfully
|
|
218
|
+
2026-06-17 08:06:49 INFO uvicorn.access: 172.92.128.89:0 - "GET /queue/next-schedule HTTP/1.1" 200
|
|
219
|
+
2026-06-17 08:06:49 INFO uvicorn.access: 23.17.200.195:0 - "GET /queue/next-schedule HTTP/1.1" 200
|
|
220
|
+
2026-06-17 08:06:50 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
221
|
+
2026-06-17 08:06:50 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
222
|
+
2026-06-17 08:06:50 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
223
|
+
2026-06-17 08:06:50 DEBUG kryten_webqueue.promos.director: Promo on_poll skipped: immutable event lock active
|
|
224
|
+
2026-06-17 08:06:53 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
225
|
+
2026-06-17 08:06:53 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
226
|
+
2026-06-17 08:06:53 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
227
|
+
2026-06-17 08:06:53 DEBUG kryten_webqueue.promos.director: Promo on_poll skipped: immutable event lock active
|
|
228
|
+
2026-06-17 08:06:56 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/playlist "HTTP/1.1 200 OK"
|
|
229
|
+
2026-06-17 08:06:56 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
230
|
+
2026-06-17 08:06:56 INFO httpx: HTTP Request: GET https://www.dropsugar.co:8443/api/v1/state/now-playing "HTTP/1.1 200 OK"
|
|
231
|
+
2026-06-17 08:06:56 DEBUG kryten_webqueue.promos.director: Promo on_poll skipped: immutable event lock active
|
|
232
|
+
|
|
233
|
+
|
|
@@ -145,3 +145,73 @@ class ApiGateClient:
|
|
|
145
145
|
"request_id": request_id,
|
|
146
146
|
"reason": reason,
|
|
147
147
|
})
|
|
148
|
+
|
|
149
|
+
# --- Moderator service status ---
|
|
150
|
+
|
|
151
|
+
async def moderator_ping(self) -> dict:
|
|
152
|
+
return await self.get("/moderator/ping")
|
|
153
|
+
|
|
154
|
+
async def moderator_health(self) -> dict:
|
|
155
|
+
return await self.get("/moderator/health")
|
|
156
|
+
|
|
157
|
+
async def moderator_stats(self) -> dict:
|
|
158
|
+
return await self.get("/moderator/stats")
|
|
159
|
+
|
|
160
|
+
# --- Moderation entries ---
|
|
161
|
+
|
|
162
|
+
async def mod_list_entries(self, channel: str, action_filter: str | None = None) -> dict:
|
|
163
|
+
params: dict = {}
|
|
164
|
+
if action_filter:
|
|
165
|
+
params["filter"] = action_filter
|
|
166
|
+
return await self.get(f"/channels/{channel}/moderation", **params)
|
|
167
|
+
|
|
168
|
+
async def mod_add_entry(
|
|
169
|
+
self,
|
|
170
|
+
channel: str,
|
|
171
|
+
username: str,
|
|
172
|
+
action: str,
|
|
173
|
+
reason: str | None = None,
|
|
174
|
+
moderator: str | None = None,
|
|
175
|
+
) -> dict:
|
|
176
|
+
body: dict = {"username": username, "action": action}
|
|
177
|
+
if reason:
|
|
178
|
+
body["reason"] = reason
|
|
179
|
+
if moderator:
|
|
180
|
+
body["moderator"] = moderator
|
|
181
|
+
return await self.post(f"/channels/{channel}/moderation", json=body)
|
|
182
|
+
|
|
183
|
+
async def mod_remove_entry(self, channel: str, username: str) -> dict:
|
|
184
|
+
return await self.delete(f"/channels/{channel}/moderation/{username}")
|
|
185
|
+
|
|
186
|
+
# --- Patterns ---
|
|
187
|
+
|
|
188
|
+
async def mod_list_patterns(self, channel: str) -> dict:
|
|
189
|
+
return await self.get(f"/channels/{channel}/patterns")
|
|
190
|
+
|
|
191
|
+
async def mod_add_pattern(
|
|
192
|
+
self,
|
|
193
|
+
channel: str,
|
|
194
|
+
pattern: str,
|
|
195
|
+
is_regex: bool = False,
|
|
196
|
+
action: str = "ban",
|
|
197
|
+
description: str | None = None,
|
|
198
|
+
added_by: str | None = None,
|
|
199
|
+
) -> dict:
|
|
200
|
+
body: dict = {"pattern": pattern, "is_regex": is_regex, "action": action}
|
|
201
|
+
if description:
|
|
202
|
+
body["description"] = description
|
|
203
|
+
if added_by:
|
|
204
|
+
body["added_by"] = added_by
|
|
205
|
+
return await self.post(f"/channels/{channel}/patterns", json=body)
|
|
206
|
+
|
|
207
|
+
async def mod_remove_pattern(self, channel: str, pattern: str) -> dict:
|
|
208
|
+
from urllib.parse import quote
|
|
209
|
+
encoded = quote(pattern, safe="")
|
|
210
|
+
return await self.delete(f"/channels/{channel}/patterns/{encoded}")
|
|
211
|
+
|
|
212
|
+
# --- Recent users ---
|
|
213
|
+
|
|
214
|
+
async def mod_recent_users(self, channel: str, window_minutes: float = 60.0) -> dict:
|
|
215
|
+
return await self.get(
|
|
216
|
+
f"/channels/{channel}/users/recent", window_minutes=window_minutes
|
|
217
|
+
)
|
|
@@ -33,6 +33,7 @@ from .routes.admin_queue import router as admin_queue_router
|
|
|
33
33
|
from .routes.admin_jobs import router as admin_jobs_router
|
|
34
34
|
from .routes.admin_catalog import router as admin_catalog_router
|
|
35
35
|
from .routes.admin_promos import router as admin_promos_router
|
|
36
|
+
from .routes.admin_moderation import router as admin_moderation_router
|
|
36
37
|
from .routes.feedback import router as feedback_router
|
|
37
38
|
from .routes.admin_feedback import router as admin_feedback_router
|
|
38
39
|
from .routes.pages import router as pages_router
|
|
@@ -260,6 +261,7 @@ def create_app(config: Config) -> FastAPI:
|
|
|
260
261
|
app.include_router(admin_jobs_router)
|
|
261
262
|
app.include_router(admin_catalog_router)
|
|
262
263
|
app.include_router(admin_promos_router)
|
|
264
|
+
app.include_router(admin_moderation_router)
|
|
263
265
|
app.include_router(feedback_router)
|
|
264
266
|
app.include_router(admin_feedback_router)
|
|
265
267
|
app.include_router(ws_router)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from ._connection import _DBBase
|
|
2
|
+
from ._catalog import _CatalogMixin, HIDDEN_ITEM_TAG, HIDDEN_CATEGORY_NAMES, HIDDEN_TAG_NAMES
|
|
3
|
+
from ._playlists import _PlaylistsMixin
|
|
4
|
+
from ._queue import _QueueMixin
|
|
5
|
+
from ._feedback import _FeedbackMixin
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Database(_CatalogMixin, _PlaylistsMixin, _QueueMixin, _FeedbackMixin, _DBBase):
|
|
9
|
+
"""Async SQLite database wrapper."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Database",
|
|
14
|
+
"HIDDEN_ITEM_TAG",
|
|
15
|
+
"HIDDEN_CATEGORY_NAMES",
|
|
16
|
+
"HIDDEN_TAG_NAMES",
|
|
17
|
+
]
|