kryten-webqueue 0.5.0__tar.gz → 0.5.2__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.5.0 → kryten_webqueue-0.5.2}/CHANGELOG.md +20 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/PKG-INFO +1 -1
- kryten_webqueue-0.5.2/kryten_webqueue/__init__.py +6 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/app.py +3 -2
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/queue/ordering.py +77 -44
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/pyproject.toml +1 -1
- kryten_webqueue-0.5.0/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/.gitignore +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/README.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/config.example.json +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.5.0/kryten_webqueue → kryten_webqueue-0.5.2/kryten_webqueue/api_gate}/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.5.0/kryten_webqueue/api_gate → kryten_webqueue-0.5.2/kryten_webqueue/auth}/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.5.0/kryten_webqueue/auth → kryten_webqueue-0.5.2/kryten_webqueue/catalog}/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/catalog/db.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.5.0/kryten_webqueue/catalog → kryten_webqueue-0.5.2/kryten_webqueue/playlists}/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.5.0/kryten_webqueue/playlists → kryten_webqueue-0.5.2/kryten_webqueue/queue}/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.5.0/kryten_webqueue/queue → kryten_webqueue-0.5.2/kryten_webqueue/routes}/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.5.0/kryten_webqueue/routes → kryten_webqueue-0.5.2/kryten_webqueue/ws}/__init__.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/ws/manager.py +0 -0
|
@@ -5,6 +5,26 @@ 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.5.2] - 2026-06-05
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Queue items now land in the correct playlist position** — Positioning is computed relative to the currently-playing item and the persistent pay-queue list (`queue_shadow` rows with `is_pay = 1`):
|
|
13
|
+
- **Play Next** — moved to immediately *after* the currently-playing item (previously `prepend`, which placed it before the active item). Existing pay items shift down one position.
|
|
14
|
+
- **Queue** — moved to immediately after the *last* item in the persistent pay-queue list, or after the currently-playing item when no pay items exist (previously left at the end of the playlist when the pay list was empty).
|
|
15
|
+
- **Queue as Admin** — same target as Queue, but the item is *not* added to the persistent pay list (`is_pay = 0`).
|
|
16
|
+
- All paths now add to CyTube with `position="end"` and then issue a single `move` to the resolved target UID, refunding (where applicable) and removing the orphaned item if the move fails.
|
|
17
|
+
|
|
18
|
+
[0.5.2]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.5.2
|
|
19
|
+
|
|
20
|
+
## [0.5.1] - 2026-06-05
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **Version logged at startup** — Service version is now read from package metadata (`importlib.metadata`) and logged when the lifespan starts: `kryten-webqueue v<version> started on <host>:<port>`. The same version is exposed in the FastAPI OpenAPI schema (replacing the stale hard-coded `"0.1.0"`)
|
|
25
|
+
|
|
26
|
+
[0.5.1]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.5.1
|
|
27
|
+
|
|
8
28
|
## [0.5.0] - 2026-06-04
|
|
9
29
|
|
|
10
30
|
### Added
|
|
@@ -7,6 +7,7 @@ from fastapi.staticfiles import StaticFiles
|
|
|
7
7
|
from fastapi.templating import Jinja2Templates
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
|
+
from . import __version__
|
|
10
11
|
from .config import Config
|
|
11
12
|
from .catalog.db import Database
|
|
12
13
|
from .api_gate.client import ApiGateClient
|
|
@@ -137,7 +138,7 @@ async def lifespan(app: FastAPI):
|
|
|
137
138
|
asyncio.create_task(_immutability_expiry_loop()),
|
|
138
139
|
]
|
|
139
140
|
|
|
140
|
-
logger.info(f"kryten-webqueue started on {config.host}:{config.port}")
|
|
141
|
+
logger.info(f"kryten-webqueue v{__version__} started on {config.host}:{config.port}")
|
|
141
142
|
|
|
142
143
|
yield
|
|
143
144
|
|
|
@@ -156,7 +157,7 @@ async def lifespan(app: FastAPI):
|
|
|
156
157
|
def create_app(config: Config) -> FastAPI:
|
|
157
158
|
app = FastAPI(
|
|
158
159
|
title="kryten-webqueue",
|
|
159
|
-
version=
|
|
160
|
+
version=__version__,
|
|
160
161
|
lifespan=lifespan,
|
|
161
162
|
)
|
|
162
163
|
app.state.config = config
|
|
@@ -50,6 +50,39 @@ async def _announce_queued(api_gate, shadow, *, uid: int, title: str, username:
|
|
|
50
50
|
logger.warning("Failed to send queue announcement", exc_info=True)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
async def _now_playing_uid(api_gate, shadow) -> int | None:
|
|
54
|
+
"""UID of the currently-playing item, preferring fresh state over the cache."""
|
|
55
|
+
np = None
|
|
56
|
+
try:
|
|
57
|
+
np = await api_gate.get_now_playing()
|
|
58
|
+
except Exception:
|
|
59
|
+
np = None
|
|
60
|
+
if not np:
|
|
61
|
+
np = shadow.now_playing
|
|
62
|
+
if not np:
|
|
63
|
+
return None
|
|
64
|
+
uid = np.get("uid")
|
|
65
|
+
try:
|
|
66
|
+
return int(uid) if uid is not None else None
|
|
67
|
+
except (TypeError, ValueError):
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _shadow_index_after_uid(shadow, target_uid: int | None) -> int:
|
|
72
|
+
"""Shadow list index immediately after target_uid (end of list if not found)."""
|
|
73
|
+
if target_uid is not None:
|
|
74
|
+
for idx, it in enumerate(shadow.items):
|
|
75
|
+
if it.get("uid") == target_uid:
|
|
76
|
+
return idx + 1
|
|
77
|
+
return len(shadow.items)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def _move_after(api_gate, *, uid: int, target_uid: int | None) -> None:
|
|
81
|
+
"""Move uid to immediately after target_uid. No-op when target is None."""
|
|
82
|
+
if target_uid is not None:
|
|
83
|
+
await api_gate.playlist_move(uid, target_uid)
|
|
84
|
+
|
|
85
|
+
|
|
53
86
|
async def insert_pay_queue(
|
|
54
87
|
*,
|
|
55
88
|
api_gate,
|
|
@@ -80,16 +113,17 @@ async def insert_pay_queue(
|
|
|
80
113
|
except httpx.HTTPStatusError as exc:
|
|
81
114
|
return {"success": False, "error": f"Spend failed: {exc.response.status_code}"}
|
|
82
115
|
|
|
83
|
-
#
|
|
116
|
+
# Target position: immediately after the LAST item in the persistent
|
|
117
|
+
# pay-queue list, or after the currently-playing item when none exist.
|
|
84
118
|
last_pay_uid = await db.get_last_pay_uid()
|
|
85
|
-
|
|
119
|
+
target_uid = last_pay_uid if last_pay_uid else await _now_playing_uid(api_gate, shadow)
|
|
86
120
|
|
|
87
|
-
# Add to CyTube playlist
|
|
121
|
+
# Add to CyTube playlist (always appended; repositioned below)
|
|
88
122
|
try:
|
|
89
123
|
add_result = await api_gate.playlist_add(
|
|
90
124
|
media_type=media_type,
|
|
91
125
|
media_id=media_id,
|
|
92
|
-
position=
|
|
126
|
+
position="end",
|
|
93
127
|
)
|
|
94
128
|
except httpx.HTTPStatusError as exc:
|
|
95
129
|
try:
|
|
@@ -107,20 +141,19 @@ async def insert_pay_queue(
|
|
|
107
141
|
|
|
108
142
|
uid = add_result["uid"]
|
|
109
143
|
|
|
110
|
-
# Move after
|
|
111
|
-
|
|
144
|
+
# Move after the target UID; refund + remove if positioning fails
|
|
145
|
+
try:
|
|
146
|
+
await _move_after(api_gate, uid=uid, target_uid=target_uid)
|
|
147
|
+
except httpx.HTTPStatusError:
|
|
112
148
|
try:
|
|
113
|
-
await api_gate.
|
|
114
|
-
except
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
except Exception:
|
|
122
|
-
pass
|
|
123
|
-
return {"success": False, "error": "Failed to position item in queue"}
|
|
149
|
+
await api_gate.queue_refund(username=username, request_id=request_id, reason="move_failed")
|
|
150
|
+
except Exception:
|
|
151
|
+
pass
|
|
152
|
+
try:
|
|
153
|
+
await api_gate.playlist_delete(uid)
|
|
154
|
+
except Exception:
|
|
155
|
+
pass
|
|
156
|
+
return {"success": False, "error": "Failed to position item in queue"}
|
|
124
157
|
|
|
125
158
|
# Record spend
|
|
126
159
|
_ft = friendly_token if friendly_token is not None else (media_id if media_type == "cm" else None)
|
|
@@ -143,11 +176,8 @@ async def insert_pay_queue(
|
|
|
143
176
|
"z_cost": z_cost,
|
|
144
177
|
"schedule_id": None,
|
|
145
178
|
}
|
|
146
|
-
# Position after
|
|
147
|
-
|
|
148
|
-
pos = await db.get_shadow_position_after(last_pay_uid)
|
|
149
|
-
else:
|
|
150
|
-
pos = len(shadow.items)
|
|
179
|
+
# Position immediately after the target UID
|
|
180
|
+
pos = _shadow_index_after_uid(shadow, target_uid)
|
|
151
181
|
await shadow.insert_at(item, pos)
|
|
152
182
|
|
|
153
183
|
# Queue history
|
|
@@ -192,7 +222,10 @@ async def insert_pay_playnext(
|
|
|
192
222
|
except httpx.HTTPStatusError as exc:
|
|
193
223
|
return {"success": False, "error": f"Spend failed: {exc.response.status_code}"}
|
|
194
224
|
|
|
195
|
-
#
|
|
225
|
+
# Target position: immediately after the currently-playing item.
|
|
226
|
+
target_uid = await _now_playing_uid(api_gate, shadow)
|
|
227
|
+
|
|
228
|
+
# Add to CyTube playlist (always appended; repositioned below)
|
|
196
229
|
try:
|
|
197
230
|
add_result = await api_gate.playlist_add(
|
|
198
231
|
media_type=media_type,
|
|
@@ -214,9 +247,9 @@ async def insert_pay_playnext(
|
|
|
214
247
|
|
|
215
248
|
uid = add_result["uid"]
|
|
216
249
|
|
|
217
|
-
# Move to
|
|
250
|
+
# Move to immediately after the now-playing item; refund + remove on failure
|
|
218
251
|
try:
|
|
219
|
-
await api_gate
|
|
252
|
+
await _move_after(api_gate, uid=uid, target_uid=target_uid)
|
|
220
253
|
except httpx.HTTPStatusError:
|
|
221
254
|
try:
|
|
222
255
|
await api_gate.queue_refund(username=username, request_id=request_id, reason="move_failed")
|
|
@@ -236,7 +269,8 @@ async def insert_pay_playnext(
|
|
|
236
269
|
tier=tier, z_cost=z_cost,
|
|
237
270
|
)
|
|
238
271
|
|
|
239
|
-
# Update local shadow
|
|
272
|
+
# Update local shadow immediately after now-playing. Existing pay items
|
|
273
|
+
# shift down one position as insert_at re-indexes the list.
|
|
240
274
|
item = {
|
|
241
275
|
"uid": uid,
|
|
242
276
|
"title": title,
|
|
@@ -249,7 +283,8 @@ async def insert_pay_playnext(
|
|
|
249
283
|
"z_cost": z_cost,
|
|
250
284
|
"schedule_id": None,
|
|
251
285
|
}
|
|
252
|
-
|
|
286
|
+
pos = _shadow_index_after_uid(shadow, target_uid)
|
|
287
|
+
await shadow.insert_at(item, pos)
|
|
253
288
|
|
|
254
289
|
await db.add_queue_history(
|
|
255
290
|
username=username, friendly_token=_ft,
|
|
@@ -280,16 +315,18 @@ async def insert_admin_queue(
|
|
|
280
315
|
immediately after the last paid item, i.e. at the top of the free section.
|
|
281
316
|
"""
|
|
282
317
|
async with _queue_lock:
|
|
283
|
-
#
|
|
318
|
+
# Target position: immediately after the LAST item in the persistent
|
|
319
|
+
# pay-queue list, or after the currently-playing item when none exist.
|
|
320
|
+
# The admin item itself is NOT added to the persistent pay list.
|
|
284
321
|
last_pay_uid = await db.get_last_pay_uid()
|
|
285
|
-
|
|
322
|
+
target_uid = last_pay_uid if last_pay_uid else await _now_playing_uid(api_gate, shadow)
|
|
286
323
|
|
|
287
|
-
# Add to CyTube playlist
|
|
324
|
+
# Add to CyTube playlist (always appended; repositioned below)
|
|
288
325
|
try:
|
|
289
326
|
add_result = await api_gate.playlist_add(
|
|
290
327
|
media_type=media_type,
|
|
291
328
|
media_id=media_id,
|
|
292
|
-
position=
|
|
329
|
+
position="end",
|
|
293
330
|
)
|
|
294
331
|
except httpx.HTTPStatusError as exc:
|
|
295
332
|
return {"success": False, "error": _add_failure_reason(None, exc)}
|
|
@@ -298,16 +335,15 @@ async def insert_admin_queue(
|
|
|
298
335
|
|
|
299
336
|
uid = add_result["uid"]
|
|
300
337
|
|
|
301
|
-
# Move
|
|
302
|
-
|
|
338
|
+
# Move after the target UID; remove the orphan if positioning fails
|
|
339
|
+
try:
|
|
340
|
+
await _move_after(api_gate, uid=uid, target_uid=target_uid)
|
|
341
|
+
except httpx.HTTPStatusError:
|
|
303
342
|
try:
|
|
304
|
-
await api_gate.
|
|
305
|
-
except
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
except Exception:
|
|
309
|
-
pass
|
|
310
|
-
return {"success": False, "error": "Failed to position item in queue"}
|
|
343
|
+
await api_gate.playlist_delete(uid)
|
|
344
|
+
except Exception:
|
|
345
|
+
pass
|
|
346
|
+
return {"success": False, "error": "Failed to position item in queue"}
|
|
311
347
|
|
|
312
348
|
_ft = friendly_token if friendly_token is not None else (media_id if media_type == "cm" else None)
|
|
313
349
|
|
|
@@ -324,10 +360,7 @@ async def insert_admin_queue(
|
|
|
324
360
|
"z_cost": None,
|
|
325
361
|
"schedule_id": None,
|
|
326
362
|
}
|
|
327
|
-
|
|
328
|
-
pos = await db.get_shadow_position_after(last_pay_uid)
|
|
329
|
-
else:
|
|
330
|
-
pos = len(shadow.items)
|
|
363
|
+
pos = _shadow_index_after_uid(shadow, target_uid)
|
|
331
364
|
await shadow.insert_at(item, pos)
|
|
332
365
|
|
|
333
366
|
# Queue history (zero cost, admin tier)
|
|
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.5.0/kryten_webqueue → kryten_webqueue-0.5.2/kryten_webqueue/api_gate}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.5.0 → kryten_webqueue-0.5.2}/kryten_webqueue/templates/user/dashboard.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|