kryten-webqueue 0.7.3__tar.gz → 0.7.4__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.7.3 → kryten_webqueue-0.7.4}/CHANGELOG.md +6 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/PKG-INFO +1 -1
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/catalog/db.py +2 -2
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/queue/shadow.py +1 -1
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/pages.py +3 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/static/css/main.css +70 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/catalog/item_detail.html +28 -6
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/pyproject.toml +1 -1
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/.gitignore +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/README.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/config.example.json +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/app.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/ws/manager.py +0 -0
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
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
|
+
## [0.7.4] - 2026-06-08
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Catalog item detail page redesigned** to a two-column layout: the poster and action buttons (Queue / Play Next / Queue as Admin) stack in a sticky left column, while the right column shows the title, a divider, the formatted description, and **Category** / **Tags** facet rows. Category and tag chips link back into a filtered catalog browse. Categories/tags are sourced from the catalog join tables (populated by sync since 0.7.0).
|
|
12
|
+
|
|
7
13
|
## [0.7.3] - 2026-06-08
|
|
8
14
|
|
|
9
15
|
### Fixed
|
|
@@ -409,7 +409,7 @@ class Database:
|
|
|
409
409
|
"SELECT description FROM catalog WHERE friendly_token = ?", [friendly_token]
|
|
410
410
|
)
|
|
411
411
|
cats = await self._fetch_all(
|
|
412
|
-
"SELECT cat.name FROM categories cat "
|
|
412
|
+
"SELECT cat.name, cat.slug FROM categories cat "
|
|
413
413
|
"JOIN catalog_categories cc ON cc.category_id = cat.id "
|
|
414
414
|
"WHERE cc.friendly_token = ? ORDER BY cat.name",
|
|
415
415
|
[friendly_token],
|
|
@@ -422,7 +422,7 @@ class Database:
|
|
|
422
422
|
)
|
|
423
423
|
return {
|
|
424
424
|
"description": (row or {}).get("description"),
|
|
425
|
-
"categories": [c["name"] for c in cats],
|
|
425
|
+
"categories": [{"name": c["name"], "slug": c["slug"]} for c in cats],
|
|
426
426
|
"tags": [t["name"] for t in tags],
|
|
427
427
|
}
|
|
428
428
|
|
|
@@ -222,7 +222,7 @@ class QueueShadow:
|
|
|
222
222
|
try:
|
|
223
223
|
facets = await db.get_item_facets(np["friendly_token"])
|
|
224
224
|
np.setdefault("description", facets.get("description"))
|
|
225
|
-
np["categories"] = facets.get("categories") or []
|
|
225
|
+
np["categories"] = [c["name"] for c in (facets.get("categories") or [])]
|
|
226
226
|
np["tags"] = facets.get("tags") or []
|
|
227
227
|
except Exception:
|
|
228
228
|
logger.debug("Failed to enrich now-playing facets", exc_info=True)
|
|
@@ -109,9 +109,12 @@ async def catalog_item_page(request: Request, friendly_token: str):
|
|
|
109
109
|
return templates.TemplateResponse(
|
|
110
110
|
request, "catalog/item_not_found.html", {"user": user}, status_code=404
|
|
111
111
|
)
|
|
112
|
+
facets = await db.get_item_facets(friendly_token)
|
|
112
113
|
return templates.TemplateResponse(request, "catalog/item_detail.html", {
|
|
113
114
|
"user": user,
|
|
114
115
|
"item": item,
|
|
116
|
+
"categories": facets.get("categories") or [],
|
|
117
|
+
"tags": facets.get("tags") or [],
|
|
115
118
|
})
|
|
116
119
|
|
|
117
120
|
|
|
@@ -507,12 +507,82 @@ a:hover {
|
|
|
507
507
|
white-space: pre-line;
|
|
508
508
|
overflow-wrap: anywhere;
|
|
509
509
|
}
|
|
510
|
+
/* Item detail: poster + buttons on the left, title/description/facets on right. */
|
|
511
|
+
.item-detail-layout {
|
|
512
|
+
display: grid;
|
|
513
|
+
grid-template-columns: 300px minmax(0, 1fr);
|
|
514
|
+
gap: 2rem;
|
|
515
|
+
align-items: start;
|
|
516
|
+
}
|
|
517
|
+
@media (max-width: 700px) {
|
|
518
|
+
.item-detail-layout {
|
|
519
|
+
grid-template-columns: 1fr;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
.item-detail-poster {
|
|
523
|
+
display: flex;
|
|
524
|
+
flex-direction: column;
|
|
525
|
+
gap: 1rem;
|
|
526
|
+
position: sticky;
|
|
527
|
+
top: calc(var(--nav-height) + 1rem);
|
|
528
|
+
}
|
|
529
|
+
.item-detail-poster img,
|
|
530
|
+
.item-detail-poster .card-poster-placeholder {
|
|
531
|
+
width: 100%;
|
|
532
|
+
aspect-ratio: 2 / 3;
|
|
533
|
+
object-fit: cover;
|
|
534
|
+
border-radius: var(--radius);
|
|
535
|
+
display: block;
|
|
536
|
+
}
|
|
537
|
+
.item-detail-actions {
|
|
538
|
+
display: flex;
|
|
539
|
+
flex-direction: column;
|
|
540
|
+
gap: 0.5rem;
|
|
541
|
+
}
|
|
542
|
+
.item-detail-actions .btn {
|
|
543
|
+
width: 100%;
|
|
544
|
+
}
|
|
545
|
+
.item-detail-info {
|
|
546
|
+
min-width: 0;
|
|
547
|
+
}
|
|
548
|
+
.item-detail-title {
|
|
549
|
+
margin: 0;
|
|
550
|
+
line-height: 1.2;
|
|
551
|
+
overflow-wrap: anywhere;
|
|
552
|
+
}
|
|
553
|
+
.item-detail-meta {
|
|
554
|
+
margin-top: 0.35rem;
|
|
555
|
+
font-size: 0.85rem;
|
|
556
|
+
color: var(--text-secondary);
|
|
557
|
+
}
|
|
510
558
|
/* MediaCMS descriptions are newline-delimited plain text; preserve the line
|
|
511
559
|
breaks (Synopsis / Tagline / Cast & Crew sections) instead of collapsing them. */
|
|
512
560
|
.item-detail-description {
|
|
513
561
|
white-space: pre-line;
|
|
514
562
|
overflow-wrap: anywhere;
|
|
515
563
|
line-height: 1.6;
|
|
564
|
+
margin-top: 0.75rem;
|
|
565
|
+
padding-top: 0.75rem;
|
|
566
|
+
border-top: 1px solid var(--border);
|
|
567
|
+
}
|
|
568
|
+
.item-detail-facet {
|
|
569
|
+
margin-top: 1rem;
|
|
570
|
+
}
|
|
571
|
+
.item-detail-facet-label {
|
|
572
|
+
display: block;
|
|
573
|
+
font-size: 0.75rem;
|
|
574
|
+
text-transform: uppercase;
|
|
575
|
+
letter-spacing: 0.05em;
|
|
576
|
+
color: var(--text-secondary);
|
|
577
|
+
margin-bottom: 0.4rem;
|
|
578
|
+
}
|
|
579
|
+
.item-detail-chips {
|
|
580
|
+
display: flex;
|
|
581
|
+
flex-wrap: wrap;
|
|
582
|
+
gap: 0.4rem;
|
|
583
|
+
}
|
|
584
|
+
a.np-chip {
|
|
585
|
+
text-decoration: none;
|
|
516
586
|
}
|
|
517
587
|
.np-chips {
|
|
518
588
|
display: flex;
|
{kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/catalog/item_detail.html
RENAMED
|
@@ -19,6 +19,14 @@
|
|
|
19
19
|
{% else %}
|
|
20
20
|
<div class="card-poster-placeholder"><span>{{ item.title[:1] }}</span></div>
|
|
21
21
|
{% endif %}
|
|
22
|
+
|
|
23
|
+
<div class="card-actions item-detail-actions">
|
|
24
|
+
<button class="btn btn-queue" onclick="queueItem('{{ item.friendly_token }}')">Queue</button>
|
|
25
|
+
<button class="btn btn-playnext" onclick="playNext('{{ item.friendly_token }}')">Play Next</button>
|
|
26
|
+
{% if user.rank >= 3 %}
|
|
27
|
+
<button class="btn btn-admin" onclick="queueAsAdmin('{{ item.friendly_token }}')">Queue as Admin</button>
|
|
28
|
+
{% endif %}
|
|
29
|
+
</div>
|
|
22
30
|
</div>
|
|
23
31
|
|
|
24
32
|
<div class="item-detail-info">
|
|
@@ -35,13 +43,27 @@
|
|
|
35
43
|
<p class="item-detail-description empty-state">No description available.</p>
|
|
36
44
|
{% endif %}
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
{% if categories %}
|
|
47
|
+
<div class="item-detail-facet">
|
|
48
|
+
<span class="item-detail-facet-label">Category</span>
|
|
49
|
+
<div class="item-detail-chips">
|
|
50
|
+
{% for cat in categories %}
|
|
51
|
+
<a class="np-chip np-chip-cat" href="/catalog/browse?category={{ cat.slug }}">{{ cat.name }}</a>
|
|
52
|
+
{% endfor %}
|
|
53
|
+
</div>
|
|
44
54
|
</div>
|
|
55
|
+
{% endif %}
|
|
56
|
+
|
|
57
|
+
{% if tags %}
|
|
58
|
+
<div class="item-detail-facet">
|
|
59
|
+
<span class="item-detail-facet-label">Tags</span>
|
|
60
|
+
<div class="item-detail-chips">
|
|
61
|
+
{% for tag in tags %}
|
|
62
|
+
<a class="np-chip np-chip-tag" href="/catalog/browse?tag={{ tag | urlencode }}">{{ tag }}</a>
|
|
63
|
+
{% endfor %}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
{% endif %}
|
|
45
67
|
</div>
|
|
46
68
|
</div>
|
|
47
69
|
</div>
|
|
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
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.7.3 → kryten_webqueue-0.7.4}/kryten_webqueue/templates/user/dashboard.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|