kryten-webqueue 0.4.1__tar.gz → 0.4.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/PKG-INFO +1 -1
  2. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/app.py +6 -0
  3. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/ordering.py +24 -18
  4. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/queue.py +10 -10
  5. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/pyproject.toml +1 -1
  6. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/.github/workflows/python-publish.yml +0 -0
  7. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/.github/workflows/release.yml +0 -0
  8. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/.gitignore +0 -0
  9. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/README.md +0 -0
  10. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/config.example.json +0 -0
  11. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/deploy/kryten-webqueue.service +0 -0
  12. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/deploy/nginx-queue.conf +0 -0
  13. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPLEMENTATION_SPEC.md +0 -0
  14. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_API_GATE.md +0 -0
  15. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_ECONOMY.md +0 -0
  16. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_KRYTEN_PY.md +0 -0
  17. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_ROBOT.md +0 -0
  18. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/PRE_PLAN_GAPS.md +0 -0
  19. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/PRODUCT_PLAN.md +0 -0
  20. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/__init__.py +0 -0
  21. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/__main__.py +0 -0
  22. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/api_gate/__init__.py +0 -0
  23. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/api_gate/client.py +0 -0
  24. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/__init__.py +0 -0
  25. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/otp.py +0 -0
  26. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/rate_limit.py +0 -0
  27. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/session.py +0 -0
  28. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/__init__.py +0 -0
  29. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/db.py +0 -0
  30. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/images.py +0 -0
  31. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/sync.py +0 -0
  32. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/config.py +0 -0
  33. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/__init__.py +0 -0
  34. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/fire.py +0 -0
  35. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/importer.py +0 -0
  36. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/scheduler.py +0 -0
  37. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/__init__.py +0 -0
  38. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/poller.py +0 -0
  39. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/shadow.py +0 -0
  40. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/__init__.py +0 -0
  41. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/admin_playlists.py +0 -0
  42. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/admin_queue.py +0 -0
  43. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/admin_schedules.py +0 -0
  44. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/auth.py +0 -0
  45. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/catalog.py +0 -0
  46. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/pages.py +0 -0
  47. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/user.py +0 -0
  48. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/static/css/main.css +0 -0
  49. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/static/js/main.js +0 -0
  50. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/index.html +0 -0
  51. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/playlists.html +0 -0
  52. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
  53. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/schedules.html +0 -0
  54. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/auth/login.html +0 -0
  55. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/base.html +0 -0
  56. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/catalog/browse.html +0 -0
  57. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/queue/index.html +0 -0
  58. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/user/dashboard.html +0 -0
  59. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/ws/__init__.py +0 -0
  60. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/ws/handler.py +0 -0
  61. {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/ws/manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryten-webqueue
3
- Version: 0.4.1
3
+ Version: 0.4.3
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
@@ -182,4 +182,10 @@ def create_app(config: Config) -> FastAPI:
182
182
  if static_dir.exists():
183
183
  app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
184
184
 
185
+ # Cover art images (nginx serves this in production, but also mount here
186
+ # so uvicorn handles it directly when nginx isn't in front)
187
+ image_dir = Path(config.image_dir)
188
+ image_dir.mkdir(parents=True, exist_ok=True)
189
+ app.mount("/images", StaticFiles(directory=str(image_dir)), name="images")
190
+
185
191
  return app
@@ -2,6 +2,8 @@ import asyncio
2
2
  import uuid
3
3
  import logging
4
4
 
5
+ import httpx
6
+
5
7
  logger = logging.getLogger(__name__)
6
8
 
7
9
  # Module-level lock for queue ordering
@@ -25,15 +27,17 @@ async def insert_pay_queue(
25
27
  async with _queue_lock:
26
28
  request_id = str(uuid.uuid4())
27
29
 
28
- # Spend currency
29
- spend_result = await api_gate.queue_spend(
30
- username=username,
31
- duration_sec=duration_sec,
32
- tier=tier,
33
- request_id=request_id,
34
- )
35
- if not spend_result.get("success"):
36
- return {"success": False, "error": spend_result.get("error", "Spend failed")}
30
+ # Spend currency (api-gate _unwrap strips the success envelope;
31
+ # raise_for_status propagates failures as httpx.HTTPStatusError)
32
+ try:
33
+ await api_gate.queue_spend(
34
+ username=username,
35
+ duration_sec=duration_sec,
36
+ tier=tier,
37
+ request_id=request_id,
38
+ )
39
+ except httpx.HTTPStatusError as exc:
40
+ return {"success": False, "error": f"Spend failed: {exc.response.status_code}"}
37
41
 
38
42
  # Find position: after last pay item, or prepend if none
39
43
  last_pay_uid = await db.get_last_pay_uid()
@@ -109,15 +113,17 @@ async def insert_pay_playnext(
109
113
  async with _queue_lock:
110
114
  request_id = str(uuid.uuid4())
111
115
 
112
- # Spend currency
113
- spend_result = await api_gate.queue_spend(
114
- username=username,
115
- duration_sec=duration_sec,
116
- tier=tier,
117
- request_id=request_id,
118
- )
119
- if not spend_result.get("success"):
120
- return {"success": False, "error": spend_result.get("error", "Spend failed")}
116
+ # Spend currency (api-gate _unwrap strips the success envelope;
117
+ # raise_for_status propagates failures as httpx.HTTPStatusError)
118
+ try:
119
+ await api_gate.queue_spend(
120
+ username=username,
121
+ duration_sec=duration_sec,
122
+ tier=tier,
123
+ request_id=request_id,
124
+ )
125
+ except httpx.HTTPStatusError as exc:
126
+ return {"success": False, "error": f"Spend failed: {exc.response.status_code}"}
121
127
 
122
128
  # Add to CyTube playlist at prepend position
123
129
  add_result = await api_gate.playlist_add(
@@ -36,16 +36,16 @@ async def add_to_queue(request: Request, user: dict = Depends(get_current_user))
36
36
  if not item:
37
37
  raise HTTPException(404, "Item not found in catalog")
38
38
 
39
- # Preview cost
39
+ # Preview cost (api-gate _unwrap strips the success envelope; raise_for_status
40
+ # handles non-2xx, so no success check needed here)
40
41
  preview = await api_gate.queue_preview(
41
42
  username=user["username"],
42
43
  duration_sec=item["duration_sec"],
43
44
  tier=tier,
44
45
  )
45
- if not preview.get("success"):
46
- raise HTTPException(400, preview.get("error", "Cost preview failed"))
47
-
48
- z_cost = preview["cost"]
46
+ z_cost = preview.get("cost") or preview.get("z_cost")
47
+ if z_cost is None:
48
+ raise HTTPException(502, "Cost preview returned no cost value")
49
49
 
50
50
  result = await insert_pay_queue(
51
51
  api_gate=api_gate,
@@ -88,16 +88,16 @@ async def play_next(request: Request, user: dict = Depends(get_current_user)):
88
88
  if not item:
89
89
  raise HTTPException(404, "Item not found in catalog")
90
90
 
91
- # Preview cost
91
+ # Preview cost (api-gate _unwrap strips the success envelope; raise_for_status
92
+ # handles non-2xx, so no success check needed here)
92
93
  preview = await api_gate.queue_preview(
93
94
  username=user["username"],
94
95
  duration_sec=item["duration_sec"],
95
96
  tier=tier,
96
97
  )
97
- if not preview.get("success"):
98
- raise HTTPException(400, preview.get("error", "Cost preview failed"))
99
-
100
- z_cost = preview["cost"]
98
+ z_cost = preview.get("cost") or preview.get("z_cost")
99
+ if z_cost is None:
100
+ raise HTTPException(502, "Cost preview returned no cost value")
101
101
 
102
102
  result = await insert_pay_playnext(
103
103
  api_gate=api_gate,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.4.1"
3
+ version = "0.4.3"
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"