kryten-webqueue 0.4.4__tar.gz → 0.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. kryten_webqueue-0.4.6/CHANGELOG.md +221 -0
  2. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/PKG-INFO +1 -1
  3. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/queue/ordering.py +40 -16
  4. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/queue.py +4 -2
  5. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/pyproject.toml +1 -1
  6. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/.github/workflows/python-publish.yml +0 -0
  7. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/.github/workflows/release.yml +0 -0
  8. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/.gitignore +0 -0
  9. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/README.md +0 -0
  10. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/config.example.json +0 -0
  11. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/deploy/kryten-webqueue.service +0 -0
  12. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/deploy/nginx-queue.conf +0 -0
  13. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/docs/IMPLEMENTATION_SPEC.md +0 -0
  14. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/docs/IMPL_API_GATE.md +0 -0
  15. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/docs/IMPL_ECONOMY.md +0 -0
  16. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/docs/IMPL_KRYTEN_PY.md +0 -0
  17. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/docs/IMPL_ROBOT.md +0 -0
  18. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/docs/PRE_PLAN_GAPS.md +0 -0
  19. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/docs/PRODUCT_PLAN.md +0 -0
  20. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/__init__.py +0 -0
  21. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/__main__.py +0 -0
  22. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/api_gate/__init__.py +0 -0
  23. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/api_gate/client.py +0 -0
  24. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/app.py +0 -0
  25. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/auth/__init__.py +0 -0
  26. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/auth/otp.py +0 -0
  27. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/auth/rate_limit.py +0 -0
  28. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/auth/session.py +0 -0
  29. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/catalog/__init__.py +0 -0
  30. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/catalog/db.py +0 -0
  31. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/catalog/images.py +0 -0
  32. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/catalog/sync.py +0 -0
  33. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/config.py +0 -0
  34. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/playlists/__init__.py +0 -0
  35. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/playlists/fire.py +0 -0
  36. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/playlists/importer.py +0 -0
  37. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/playlists/scheduler.py +0 -0
  38. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/queue/__init__.py +0 -0
  39. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/queue/poller.py +0 -0
  40. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/queue/shadow.py +0 -0
  41. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/__init__.py +0 -0
  42. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/admin_playlists.py +0 -0
  43. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/admin_queue.py +0 -0
  44. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/admin_schedules.py +0 -0
  45. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/auth.py +0 -0
  46. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/catalog.py +0 -0
  47. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/pages.py +0 -0
  48. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/routes/user.py +0 -0
  49. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/static/css/main.css +0 -0
  50. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/static/js/main.js +0 -0
  51. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/admin/index.html +0 -0
  52. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/admin/playlists.html +0 -0
  53. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
  54. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/admin/schedules.html +0 -0
  55. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/auth/login.html +0 -0
  56. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/base.html +0 -0
  57. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/catalog/browse.html +0 -0
  58. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/queue/index.html +0 -0
  59. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/templates/user/dashboard.html +0 -0
  60. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/ws/__init__.py +0 -0
  61. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/ws/handler.py +0 -0
  62. {kryten_webqueue-0.4.4 → kryten_webqueue-0.4.6}/kryten_webqueue/ws/manager.py +0 -0
@@ -0,0 +1,221 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.4.6] - 2026-06-04
9
+
10
+ ### Fixed
11
+
12
+ - **Wrong media ID sent to CyTube** — `/queue/add` and `/queue/playnext` were passing `friendly_token` (the MediaCMS slug, e.g. `"my-movie-2024"`) as the `id` field for CyTube custom media type `"cm"`. CyTube requires the manifest URL as the ID; the slug was silently rejected, the `queue` confirmation event never fired, the Robot waited 8 seconds, and kryten-py's matching 8-second timeout fired first giving a 504. Fixed by passing `item["manifest_url"]` as `media_id` to CyTube and threading `friendly_token` separately through `insert_pay_queue` / `insert_pay_playnext` for spend/history records
13
+
14
+ [0.4.6]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.4.6
15
+
16
+ ## [0.4.5] - 2026-06-04
17
+
18
+ ### Fixed
19
+
20
+ - **Unhandled `HTTPStatusError` from `playlist_add`** — Both `insert_pay_queue` and `insert_pay_playnext` now catch `httpx.HTTPStatusError` thrown when the api-gate returns a non-2xx response for `/playlist/add`. On failure the spend is refunded (best-effort) and a `{success: False, error: "Failed to add to playlist"}` dict is returned so the route can surface a 400 to the browser instead of crashing with an unhandled 500
21
+
22
+ [0.4.5]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.4.5
23
+
24
+ ## [0.4.4] - 2026-06-04
25
+
26
+ ### Fixed
27
+
28
+ - **`cost_z` field name** — Economy preview response uses `cost_z` not `cost`/`z_cost`; the wrong key names caused every queue/add and queue/playnext call to raise HTTP 502 "Cost preview returned no cost value"
29
+ - **Eligibility not surfaced** — Queue routes now check `preview.get("available")` before attempting the spend; when `False`, the `error_code` from the preview (`cooldown_active`, `daily_limit_reached`, `insufficient_balance`, `blackout_active`) is returned as HTTP 400 so the UI can display a meaningful message
30
+
31
+ [0.4.4]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.4.4
32
+
33
+ ## [0.4.3] - 2026-06-04
34
+
35
+ ### Fixed
36
+
37
+ - **Phantom `success` checks on economy responses** — `api-gate` economy routes use `_unwrap()` which strips the outer `{success, data}` envelope before returning. Both `/queue/add` and `/queue/playnext` were checking `preview.get("success")` on the already-stripped data dict, which was always `None` → always HTTP 400 "Cost preview failed". Removed the checks entirely; `raise_for_status()` in the httpx client already propagates non-2xx responses as `HTTPStatusError`
38
+ - **Spend error handling** — Replaced `spend_result.get("success")` check with `try/except httpx.HTTPStatusError` around the `queue_spend` call for a clean error dict on failure
39
+
40
+ [0.4.3]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.4.3
41
+
42
+ ## [0.4.2] - 2026-06-03
43
+
44
+ ### Fixed
45
+
46
+ - **`/images/` 404s in development** — Mounted `StaticFiles` at `/images` pointing to `config.image_dir` in `app.py`; previously only the nginx alias served these in production, but uvicorn had no route so any direct or dev request returned 404
47
+
48
+ [0.4.2]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.4.2
49
+
50
+ ## [0.4.1] - 2026-06-03
51
+
52
+ ### Added
53
+
54
+ - **Parallel TMDB movie + TV search** — Replaced `search/multi` (which mixed in person results) with concurrent `search/movie` + `search/tv` calls; picks highest-popularity result across both
55
+ - **Title cleaning** — `_clean_title()` strips year suffixes, episode tags (`S01E02`), and resolution noise (`1080p`, `BluRay`, `x264`, etc.) before retrying a failed lookup
56
+ - **Year extraction** — Parsed year passed to TMDB as the `year` filter when available
57
+ - **`w780` poster size** — Upgraded from `w500` for higher-quality cover art
58
+ - **Thumbnail fallback** — If no external art is found, falls back to the MediaCMS thumbnail URL already stored in the catalog
59
+ - **DB migration 2** — Clears all cached `cover_art_path` / `cover_art_source` rows on first startup to force a full repoll with the improved resolver
60
+
61
+ ### Changed
62
+
63
+ - **OMDB lookup** — Now passes extracted year to improve match accuracy
64
+
65
+ [0.4.1]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.4.1
66
+
67
+ ## [0.4.0] - 2026-06-02
68
+
69
+ ### Added
70
+
71
+ - **Pagination** — Browse and search pages use page-number controls; `browse_count` / `search_count` added to db for total page calculation
72
+ - **Admin stub routes** — `/admin/playlists`, `/admin/schedules`, `/admin/queue-mgmt` page routes with stub templates
73
+ - **Queue page improvements** — `qi-info`/`qi-right` layout, total duration, remaining time, initial HTTP load, "queued by" metadata
74
+ - **CSS additions** — `.user-header`, `.user-rank`, `.user-online`, `.balance-meta`, `.tx-time`, `.qi-info`, `.qi-meta`, `.qi-right`, `.queue-summary`, `.np-meta`, `.np-times`
75
+
76
+ ### Fixed
77
+
78
+ - **Cover art paths** — Static image references updated from `/static/images` to `/images` (nginx alias)
79
+ - **TMDB search** — Filter to prefer `poster_path` results over person `profile_path` matches
80
+ - **Queue button error handling** — JS now surfaces API error messages inline rather than failing silently
81
+
82
+ ### Changed
83
+
84
+ - **User dashboard** — Shows rank name, Z balance with comma formatting (`toLocaleString`), `describeTx` helper for transaction labels, transaction dates
85
+
86
+ [0.4.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.4.0
87
+
88
+ ## [0.3.2] - 2026-06-02
89
+
90
+ ### Added
91
+
92
+ - **Cover art resolver logging** — INFO/WARNING log entries to diagnose silent failures in TMDB/OMDB lookups
93
+
94
+ [0.3.2]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.3.2
95
+
96
+ ## [0.3.1] - 2026-06-02
97
+
98
+ ### Fixed
99
+
100
+ - **Cover art rate limiting** — Added 250ms delay between API calls to avoid hitting TMDB/OMDB rate limits
101
+
102
+ [0.3.1]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.3.1
103
+
104
+ ## [0.3.0] - 2026-06-02
105
+
106
+ ### Added
107
+
108
+ - **TMDB/OMDB cover art** — Fetches and caches box art from TMDB (primary) and OMDB (fallback) during catalog sync; MediaCMS thumbnail is last resort
109
+
110
+ [0.3.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.3.0
111
+
112
+ ## [0.2.9] - 2026-06-02
113
+
114
+ ### Added
115
+
116
+ - **MediaCMS thumbnail fallback** — Shows MediaCMS-sourced thumbnail as cover art when no external art is available
117
+
118
+ [0.2.9]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.9
119
+
120
+ ## [0.2.8] - 2026-06-02
121
+
122
+ ### Fixed
123
+
124
+ - **`manifest_url`** — Now uses the watch page URL instead of the raw media URL
125
+ - **Catalog search route** — Added missing template handler for `/catalog/search`
126
+
127
+ [0.2.8]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.8
128
+
129
+ ## [0.2.7] - 2026-06-02
130
+
131
+ ### Fixed
132
+
133
+ - **Full catalog sync** — Switched from `/media` (capped at 1000 items) to `/manage_media` endpoint for complete pagination
134
+
135
+ [0.2.7]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.7
136
+
137
+ ## [0.2.6] - 2026-06-02
138
+
139
+ ### Fixed
140
+
141
+ - **Catalog pagination** — Follow `next` URL from MediaCMS API response instead of constructing page numbers manually
142
+
143
+ [0.2.6]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.6
144
+
145
+ ## [0.2.5] - 2026-06-02
146
+
147
+ ### Fixed
148
+
149
+ - **Network error messages** — Clear per-host error messages; distinguish DNS failure, connection refused, and timeout
150
+
151
+ [0.2.5]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.5
152
+
153
+ ## [0.2.4] - 2026-06-02
154
+
155
+ ### Fixed
156
+
157
+ - **MediaCMS URL misconfiguration** — Strip trailing `/api/v1` from `mediacms_url` if accidentally included; log 4xx response URL and body for debugging
158
+
159
+ [0.2.4]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.4
160
+
161
+ ## [0.2.3] - 2026-06-01
162
+
163
+ ### Fixed
164
+
165
+ - **Catalog sync error logging** — Log full traceback on sync failure instead of message-only
166
+
167
+ [0.2.3]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.3
168
+
169
+ ## [0.2.2] - 2026-06-01
170
+
171
+ ### Fixed
172
+
173
+ - **Catalog sync startup timing** — Run catalog sync immediately on startup; add `INFO`-level progress logging
174
+
175
+ [0.2.2]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.2
176
+
177
+ ## [0.2.1] - 2026-06-01
178
+
179
+ ### Fixed
180
+
181
+ - **Starlette compatibility** — Updated `TemplateResponse` calls to Starlette ≥0.36 signature `(request, name, context)`
182
+
183
+ [0.2.1]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.1
184
+
185
+ ## [0.2.0] - 2026-05-31
186
+
187
+ ### Added
188
+
189
+ - **`main()` entrypoint** — Allows `kryten-webqueue` console script invocation
190
+ - **Deploy files in wheel** — nginx config and systemd unit included as `shared-data` for `pipx`-based installs
191
+
192
+ ### Fixed
193
+
194
+ - **nginx TLS hardening** — `http2` directive syntax fixed; proxy keep-alive headers added
195
+
196
+ [0.2.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.2.0
197
+
198
+ ## [0.1.6] - 2026-05-31
199
+
200
+ ### Changed
201
+
202
+ - **CI** — Restored original two-workflow pattern (release.yml + python-publish.yml)
203
+
204
+ [0.1.6]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.1.6
205
+
206
+ ## [0.1.3] - 2026-05-31
207
+
208
+ ### Fixed
209
+
210
+ - **Duration parsing** — Parse `HH:MM:SS` strings to seconds
211
+ - **Duration/currentTime coercion** — Coerce API string values to `float`
212
+
213
+ [0.1.3]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.1.3
214
+
215
+ ## [0.1.0] - 2026-05-31
216
+
217
+ ### Added
218
+
219
+ - Initial release: FastAPI app with catalog sync from MediaCMS, SQLite catalog DB, queue/playnext routes, session auth, Jinja2 templates for browse/search/queue/user pages, nginx + systemd deploy configs
220
+
221
+ [0.1.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryten-webqueue
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: Netflix/Tubi-style catalog browser and pay-to-play queue management for CyTube
5
5
  Author: grobertson
6
6
  License-Expression: MIT
@@ -18,6 +18,7 @@ async def insert_pay_queue(
18
18
  username: str,
19
19
  media_type: str,
20
20
  media_id: str,
21
+ friendly_token: str | None = None,
21
22
  title: str,
22
23
  duration_sec: int,
23
24
  tier: str,
@@ -44,14 +45,24 @@ async def insert_pay_queue(
44
45
  position = "end" if not last_pay_uid else str(last_pay_uid)
45
46
 
46
47
  # Add to CyTube playlist
47
- add_result = await api_gate.playlist_add(
48
- media_type=media_type,
49
- media_id=media_id,
50
- position=position,
51
- )
48
+ try:
49
+ add_result = await api_gate.playlist_add(
50
+ media_type=media_type,
51
+ media_id=media_id,
52
+ position=position,
53
+ )
54
+ except httpx.HTTPStatusError:
55
+ try:
56
+ await api_gate.queue_refund(username=username, request_id=request_id, reason="playlist_add_failed")
57
+ except Exception:
58
+ pass
59
+ return {"success": False, "error": "Failed to add to playlist"}
52
60
  if not add_result.get("success"):
53
61
  # Refund on failure
54
- await api_gate.queue_refund(username=username, request_id=request_id, reason="add_failed")
62
+ try:
63
+ await api_gate.queue_refund(username=username, request_id=request_id, reason="add_failed")
64
+ except Exception:
65
+ pass
55
66
  return {"success": False, "error": "Failed to add to playlist"}
56
67
 
57
68
  uid = add_result["uid"]
@@ -61,9 +72,10 @@ async def insert_pay_queue(
61
72
  await api_gate.playlist_move(uid, last_pay_uid)
62
73
 
63
74
  # Record spend
75
+ _ft = friendly_token if friendly_token is not None else (media_id if media_type == "cm" else None)
64
76
  await db.save_spend_request(
65
77
  request_id, username=username, uid=uid,
66
- friendly_token=media_id if media_type == "cm" else None,
78
+ friendly_token=_ft,
67
79
  tier=tier, z_cost=z_cost,
68
80
  )
69
81
 
@@ -89,7 +101,7 @@ async def insert_pay_queue(
89
101
 
90
102
  # Queue history
91
103
  await db.add_queue_history(
92
- username=username, friendly_token=media_id if media_type == "cm" else None,
104
+ username=username, friendly_token=_ft,
93
105
  title=title, tier=tier, z_cost=z_cost,
94
106
  )
95
107
 
@@ -104,6 +116,7 @@ async def insert_pay_playnext(
104
116
  username: str,
105
117
  media_type: str,
106
118
  media_id: str,
119
+ friendly_token: str | None = None,
107
120
  title: str,
108
121
  duration_sec: int,
109
122
  tier: str,
@@ -126,13 +139,23 @@ async def insert_pay_playnext(
126
139
  return {"success": False, "error": f"Spend failed: {exc.response.status_code}"}
127
140
 
128
141
  # Add to CyTube playlist at prepend position
129
- add_result = await api_gate.playlist_add(
130
- media_type=media_type,
131
- media_id=media_id,
132
- position="end",
133
- )
142
+ try:
143
+ add_result = await api_gate.playlist_add(
144
+ media_type=media_type,
145
+ media_id=media_id,
146
+ position="end",
147
+ )
148
+ except httpx.HTTPStatusError:
149
+ try:
150
+ await api_gate.queue_refund(username=username, request_id=request_id, reason="playlist_add_failed")
151
+ except Exception:
152
+ pass
153
+ return {"success": False, "error": "Failed to add to playlist"}
134
154
  if not add_result.get("success"):
135
- await api_gate.queue_refund(username=username, request_id=request_id, reason="add_failed")
155
+ try:
156
+ await api_gate.queue_refund(username=username, request_id=request_id, reason="add_failed")
157
+ except Exception:
158
+ pass
136
159
  return {"success": False, "error": "Failed to add to playlist"}
137
160
 
138
161
  uid = add_result["uid"]
@@ -141,9 +164,10 @@ async def insert_pay_playnext(
141
164
  await api_gate.playlist_move(uid, "prepend")
142
165
 
143
166
  # Record spend
167
+ _ft = friendly_token if friendly_token is not None else (media_id if media_type == "cm" else None)
144
168
  await db.save_spend_request(
145
169
  request_id, username=username, uid=uid,
146
- friendly_token=media_id if media_type == "cm" else None,
170
+ friendly_token=_ft,
147
171
  tier=tier, z_cost=z_cost,
148
172
  )
149
173
 
@@ -163,7 +187,7 @@ async def insert_pay_playnext(
163
187
  await shadow.insert_at(item, 0)
164
188
 
165
189
  await db.add_queue_history(
166
- username=username, friendly_token=media_id if media_type == "cm" else None,
190
+ username=username, friendly_token=_ft,
167
191
  title=title, tier=tier, z_cost=z_cost,
168
192
  )
169
193
 
@@ -56,7 +56,8 @@ async def add_to_queue(request: Request, user: dict = Depends(get_current_user))
56
56
  db=db,
57
57
  username=user["username"],
58
58
  media_type="cm",
59
- media_id=friendly_token,
59
+ media_id=item["manifest_url"],
60
+ friendly_token=friendly_token,
60
61
  title=item["title"],
61
62
  duration_sec=item["duration_sec"],
62
63
  tier=tier,
@@ -111,7 +112,8 @@ async def play_next(request: Request, user: dict = Depends(get_current_user)):
111
112
  db=db,
112
113
  username=user["username"],
113
114
  media_type="cm",
114
- media_id=friendly_token,
115
+ media_id=item["manifest_url"],
116
+ friendly_token=friendly_token,
115
117
  title=item["title"],
116
118
  duration_sec=item["duration_sec"],
117
119
  tier=tier,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.4.4"
3
+ version = "0.4.6"
4
4
  description = "Netflix/Tubi-style catalog browser and pay-to-play queue management for CyTube"
5
5
  readme = "README.md"
6
6
  license = "MIT"