kryten-webqueue 0.2.8__tar.gz → 0.3.0__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 (58) hide show
  1. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/PKG-INFO +1 -1
  2. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/app.py +10 -9
  3. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/catalog/db.py +2 -2
  4. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/catalog/sync.py +9 -1
  5. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/templates/catalog/browse.html +3 -0
  6. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/pyproject.toml +1 -1
  7. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/.github/workflows/python-publish.yml +0 -0
  8. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/.github/workflows/release.yml +0 -0
  9. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/.gitignore +0 -0
  10. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/README.md +0 -0
  11. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/config.example.json +0 -0
  12. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/deploy/kryten-webqueue.service +0 -0
  13. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/deploy/nginx-queue.conf +0 -0
  14. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/docs/IMPLEMENTATION_SPEC.md +0 -0
  15. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/docs/IMPL_API_GATE.md +0 -0
  16. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/docs/IMPL_ECONOMY.md +0 -0
  17. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/docs/IMPL_KRYTEN_PY.md +0 -0
  18. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/docs/IMPL_ROBOT.md +0 -0
  19. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/docs/PRE_PLAN_GAPS.md +0 -0
  20. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/docs/PRODUCT_PLAN.md +0 -0
  21. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/__init__.py +0 -0
  22. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/__main__.py +0 -0
  23. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/api_gate/__init__.py +0 -0
  24. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/api_gate/client.py +0 -0
  25. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/auth/__init__.py +0 -0
  26. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/auth/otp.py +0 -0
  27. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/auth/rate_limit.py +0 -0
  28. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/auth/session.py +0 -0
  29. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/catalog/__init__.py +0 -0
  30. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/catalog/images.py +0 -0
  31. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/config.py +0 -0
  32. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/playlists/__init__.py +0 -0
  33. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/playlists/fire.py +0 -0
  34. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/playlists/importer.py +0 -0
  35. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/playlists/scheduler.py +0 -0
  36. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/queue/__init__.py +0 -0
  37. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/queue/ordering.py +0 -0
  38. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/queue/poller.py +0 -0
  39. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/queue/shadow.py +0 -0
  40. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/__init__.py +0 -0
  41. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/admin_playlists.py +0 -0
  42. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/admin_queue.py +0 -0
  43. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/admin_schedules.py +0 -0
  44. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/auth.py +0 -0
  45. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/catalog.py +0 -0
  46. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/pages.py +0 -0
  47. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/queue.py +0 -0
  48. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/routes/user.py +0 -0
  49. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/static/css/main.css +0 -0
  50. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/static/js/main.js +0 -0
  51. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/templates/admin/index.html +0 -0
  52. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/templates/auth/login.html +0 -0
  53. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/templates/base.html +0 -0
  54. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/templates/queue/index.html +0 -0
  55. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/templates/user/dashboard.html +0 -0
  56. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/ws/__init__.py +0 -0
  57. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/kryten_webqueue/ws/handler.py +0 -0
  58. {kryten_webqueue-0.2.8 → kryten_webqueue-0.3.0}/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.2.8
3
+ Version: 0.3.0
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
@@ -45,15 +45,7 @@ async def lifespan(app: FastAPI):
45
45
  api_gate = ApiGateClient(config.api_gate_url, config.api_gate_token)
46
46
  app.state.api_gate = api_gate
47
47
 
48
- # Catalog sync
49
- catalog_sync = CatalogSync(
50
- mediacms_url=config.mediacms_url,
51
- mediacms_token=config.mediacms_token,
52
- db=db,
53
- )
54
- app.state.catalog_sync = catalog_sync
55
-
56
- # Cover art resolver
48
+ # Cover art resolver (created before sync so it can be passed in)
57
49
  cover_art = CoverArtResolver(
58
50
  image_dir=config.image_dir,
59
51
  placeholder_dir=config.placeholder_dir,
@@ -62,6 +54,15 @@ async def lifespan(app: FastAPI):
62
54
  )
63
55
  app.state.cover_art = cover_art
64
56
 
57
+ # Catalog sync
58
+ catalog_sync = CatalogSync(
59
+ mediacms_url=config.mediacms_url,
60
+ mediacms_token=config.mediacms_token,
61
+ db=db,
62
+ cover_art=cover_art,
63
+ )
64
+ app.state.catalog_sync = catalog_sync
65
+
65
66
  # WebSocket manager
66
67
  ws_manager = WebSocketManager()
67
68
  app.state.ws_manager = ws_manager
@@ -220,7 +220,7 @@ class Database:
220
220
 
221
221
  async def browse(self, *, category: str | None = None, page: int = 1, per_page: int = 24) -> list[dict]:
222
222
  query = """
223
- SELECT c.friendly_token, c.title, c.duration_sec, c.cover_art_path, c.manifest_url
223
+ SELECT c.friendly_token, c.title, c.duration_sec, c.cover_art_path, c.thumbnail_url, c.manifest_url
224
224
  FROM catalog c
225
225
  WHERE c.friendly_token NOT IN (
226
226
  SELECT spi.media_id FROM saved_playlist_items spi
@@ -244,7 +244,7 @@ class Database:
244
244
 
245
245
  async def search(self, query_text: str, *, page: int = 1, per_page: int = 24) -> list[dict]:
246
246
  sql = """
247
- SELECT c.friendly_token, c.title, c.duration_sec, c.cover_art_path, c.manifest_url,
247
+ SELECT c.friendly_token, c.title, c.duration_sec, c.cover_art_path, c.thumbnail_url, c.manifest_url,
248
248
  rank AS relevance
249
249
  FROM catalog_fts fts
250
250
  JOIN catalog c ON c.rowid = fts.rowid
@@ -28,7 +28,7 @@ def _describe_httpx_error(exc: Exception, url: str) -> str:
28
28
  class CatalogSync:
29
29
  """Synchronizes catalog data from MediaCMS."""
30
30
 
31
- def __init__(self, *, mediacms_url: str, mediacms_token: str, db):
31
+ def __init__(self, *, mediacms_url: str, mediacms_token: str, db, cover_art=None):
32
32
  # Strip any accidental /api/v1 suffix — the sync code appends it itself
33
33
  url = mediacms_url.rstrip("/")
34
34
  for suffix in ("/api/v1", "/api"):
@@ -38,6 +38,7 @@ class CatalogSync:
38
38
  self._url = url
39
39
  self._token = mediacms_token
40
40
  self._db = db
41
+ self._cover_art = cover_art
41
42
  self._client = httpx.AsyncClient(
42
43
  headers={"Authorization": f"Token {mediacms_token}"},
43
44
  timeout=30.0,
@@ -127,6 +128,13 @@ class CatalogSync:
127
128
  await self._db.insert_catalog(row)
128
129
  stats["new"] += 1
129
130
 
131
+ # Fetch TMDB/OMDB cover art if not already cached
132
+ if self._cover_art and not (existing and existing.get("cover_art_path")):
133
+ try:
134
+ await self._cover_art.resolve(token, row["title"], self._db)
135
+ except Exception as e:
136
+ logger.debug(f"Cover art resolve failed for {token}: {e}")
137
+
130
138
  def _build_manifest_url(self, media: dict) -> str:
131
139
  # Use the MediaCMS watch page URL (e.g. https://www.dropsugar.co/view?m=TOKEN)
132
140
  url = media.get("url", "")
@@ -32,6 +32,9 @@
32
32
  /static/images/{{ item.cover_art_path }}/800.webp 800w"
33
33
  sizes="(max-width: 600px) 200px, 400px"
34
34
  alt="{{ item.title }}" loading="lazy">
35
+ {% elif item.thumbnail_url %}
36
+ <img src="{{ item.thumbnail_url }}"
37
+ alt="{{ item.title }}" loading="lazy">
35
38
  {% else %}
36
39
  <div class="card-poster-placeholder">
37
40
  <span>{{ item.title[:1] }}</span>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.2.8"
3
+ version = "0.3.0"
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"