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.
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/PKG-INFO +1 -1
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/app.py +6 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/ordering.py +24 -18
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/queue.py +10 -10
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/pyproject.toml +1 -1
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/.gitignore +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/README.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/config.example.json +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/db.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/ws/manager.py +0 -0
|
@@ -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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.4.1 → kryten_webqueue-0.4.3}/kryten_webqueue/templates/user/dashboard.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|