kryten-webqueue 0.46.0__tar.gz → 0.46.1__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 (209) hide show
  1. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/CHANGELOG.md +14 -0
  2. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/PKG-INFO +1 -1
  3. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/motd/builder.py +27 -23
  4. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/pyproject.toml +1 -1
  5. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_motd_publish.py +13 -10
  6. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/.github/workflows/gitleaks.yml +0 -0
  7. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/.github/workflows/python-publish.yml +0 -0
  8. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/.github/workflows/release.yml +0 -0
  9. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/.gitignore +0 -0
  10. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/.gitleaks.toml +0 -0
  11. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/.pre-commit-config.yaml +0 -0
  12. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/AGENT-WORKFLOW-GUIDE.md +0 -0
  13. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/AGENTS.md +0 -0
  14. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/README.md +0 -0
  15. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/config.example.json +0 -0
  16. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/deploy/kryten-webqueue.service +0 -0
  17. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/deploy/nginx-queue.conf +0 -0
  18. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/IMPLEMENTATION_SPEC.md +0 -0
  19. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/IMPL_API_GATE.md +0 -0
  20. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/IMPL_ECONOMY.md +0 -0
  21. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/IMPL_KRYTEN_PY.md +0 -0
  22. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/IMPL_ROBOT.md +0 -0
  23. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
  24. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
  25. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/Log for incorrect promo insertion problem.txt +0 -0
  26. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
  27. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/PRD_TMDB_LOCAL_INDEX.md +0 -0
  28. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/PRE_PLAN_GAPS.md +0 -0
  29. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/PRODUCT_PLAN.md +0 -0
  30. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/PUBLIC_API.md +0 -0
  31. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/SPEC-Visual-UX-Sprint-August-2026.md +0 -0
  32. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/SPEC_ADMIN_ITEM_EDITOR.md +0 -0
  33. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
  34. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/SPEC_EMOTE_REHOST.md +0 -0
  35. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
  36. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/SPEC_TMDB_LOCAL_INDEX.md +0 -0
  37. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/docs/UX_POLISH_PLAN.md +0 -0
  38. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/__init__.py +0 -0
  39. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/__main__.py +0 -0
  40. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/api_gate/__init__.py +0 -0
  41. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/api_gate/client.py +0 -0
  42. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/app.py +0 -0
  43. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/auth/__init__.py +0 -0
  44. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/auth/device_keys.py +0 -0
  45. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/auth/otp.py +0 -0
  46. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/auth/rate_limit.py +0 -0
  47. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/auth/session.py +0 -0
  48. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/__init__.py +0 -0
  49. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/__init__.py +0 -0
  50. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_blackouts.py +0 -0
  51. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_catalog.py +0 -0
  52. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_connection.py +0 -0
  53. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_devices.py +0 -0
  54. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
  55. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_feedback.py +0 -0
  56. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_fetch_queue.py +0 -0
  57. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_motd.py +0 -0
  58. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_people.py +0 -0
  59. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_playlists.py +0 -0
  60. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_queue.py +0 -0
  61. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
  62. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
  63. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/classify.py +0 -0
  64. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/normalise.py +0 -0
  65. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
  66. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
  67. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/report.py +0 -0
  68. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
  69. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
  70. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
  71. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/identify.py +0 -0
  72. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
  73. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
  74. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/tags.py +0 -0
  75. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/enrichment/steps/title.py +0 -0
  76. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/images.py +0 -0
  77. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/mediacms.py +0 -0
  78. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/sync.py +0 -0
  79. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/tmdb_index/__init__.py +0 -0
  80. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/tmdb_index/_schema.py +0 -0
  81. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/tmdb_index/_textmatch.py +0 -0
  82. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/tmdb_index/_ttscrape.py +0 -0
  83. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/tmdb_index/builder.py +0 -0
  84. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/tmdb_index/coverage.py +0 -0
  85. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/catalog/tmdb_index/index.py +0 -0
  86. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/config.py +0 -0
  87. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/__init__.py +0 -0
  88. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
  89. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
  90. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/blackout_scan.py +0 -0
  91. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
  92. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
  93. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
  94. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
  95. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
  96. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
  97. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
  98. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
  99. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/__init__.py +0 -0
  100. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
  101. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/job_scheduler.py +0 -0
  102. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/log_capture.py +0 -0
  103. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/manager.py +0 -0
  104. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/motd_publish.py +0 -0
  105. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
  106. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/jobs/tasks.py +0 -0
  107. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/logging_config.py +0 -0
  108. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/motd/__init__.py +0 -0
  109. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/motd/render.py +0 -0
  110. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/playlists/__init__.py +0 -0
  111. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/playlists/bulk_add.py +0 -0
  112. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/playlists/fire.py +0 -0
  113. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/playlists/importer.py +0 -0
  114. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/playlists/ordering.py +0 -0
  115. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/playlists/scheduler.py +0 -0
  116. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/promos/__init__.py +0 -0
  117. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/promos/director.py +0 -0
  118. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/queue/__init__.py +0 -0
  119. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/queue/completion.py +0 -0
  120. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/queue/ordering.py +0 -0
  121. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/queue/poller.py +0 -0
  122. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/queue/presence.py +0 -0
  123. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/queue/race_poller.py +0 -0
  124. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/queue/shadow.py +0 -0
  125. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/__init__.py +0 -0
  126. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_catalog.py +0 -0
  127. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_feedback.py +0 -0
  128. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
  129. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_jobs.py +0 -0
  130. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_moderation.py +0 -0
  131. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_motd.py +0 -0
  132. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_playlists.py +0 -0
  133. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_promos.py +0 -0
  134. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_queue.py +0 -0
  135. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/admin_schedules.py +0 -0
  136. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/auth.py +0 -0
  137. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/catalog.py +0 -0
  138. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/devices.py +0 -0
  139. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/feedback.py +0 -0
  140. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/pages.py +0 -0
  141. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/public_api.py +0 -0
  142. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/queue.py +0 -0
  143. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/routes/user.py +0 -0
  144. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/css/main.css +0 -0
  145. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/admin-motd.js +0 -0
  146. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/admin-playlists.js +0 -0
  147. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/admin-promos.js +0 -0
  148. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
  149. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/admin-schedules.js +0 -0
  150. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/admin.js +0 -0
  151. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/keybindings.js +0 -0
  152. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/static/js/main.js +0 -0
  153. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/admin/index.html +0 -0
  154. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/admin/motd.html +0 -0
  155. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/admin/playlists.html +0 -0
  156. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/admin/promos.html +0 -0
  157. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
  158. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/admin/schedules.html +0 -0
  159. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/auth/login.html +0 -0
  160. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/base.html +0 -0
  161. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/catalog/browse.html +0 -0
  162. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/catalog/edit_item_modal.html +0 -0
  163. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
  164. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
  165. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/feedback/index.html +0 -0
  166. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/motd/channel_z.html +0 -0
  167. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/queue/index.html +0 -0
  168. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/race.html +0 -0
  169. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/user/dashboard.html +0 -0
  170. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/user/link_device.html +0 -0
  171. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/templates/user/my_list.html +0 -0
  172. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/ws/__init__.py +0 -0
  173. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/ws/handler.py +0 -0
  174. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/kryten_webqueue/ws/manager.py +0 -0
  175. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/scripts/AGENTS.md +0 -0
  176. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/scripts/patch_mediacms_bulk_facets.py +0 -0
  177. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/scripts/patch_mediacms_tags.py +0 -0
  178. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/scripts/trigger_enrich.py +0 -0
  179. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/__init__.py +0 -0
  180. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_catalog_blackout.py +0 -0
  181. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_catalog_delete.py +0 -0
  182. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_classify.py +0 -0
  183. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_config_persistence.py +0 -0
  184. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_device_linking.py +0 -0
  185. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_feedback.py +0 -0
  186. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_fetch_queue_pacing.py +0 -0
  187. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_fetchurls_sharepoint.py +0 -0
  188. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_identify_step.py +0 -0
  189. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_job_logs_and_queue_visibility.py +0 -0
  190. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_people_facets.py +0 -0
  191. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_phase1.py +0 -0
  192. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_phase2_jobs.py +0 -0
  193. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_phase3_jobs.py +0 -0
  194. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_phase4_live_fixes.py +0 -0
  195. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_playlist_import.py +0 -0
  196. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_presence_refund.py +0 -0
  197. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_promo_director.py +0 -0
  198. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_promo_pool_exclusion.py +0 -0
  199. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_providers.py +0 -0
  200. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_queue_announce.py +0 -0
  201. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_queue_preview_fallback.py +0 -0
  202. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_race_poller.py +0 -0
  203. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_recently_played_hide.py +0 -0
  204. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_save_results_to_playlist.py +0 -0
  205. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_schedule_lock.py +0 -0
  206. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_search_facets.py +0 -0
  207. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_title_normalization.py +0 -0
  208. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_tmdb_index.py +0 -0
  209. {kryten_webqueue-0.46.0 → kryten_webqueue-0.46.1}/tests/test_watchlist.py +0 -0
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.46.1] - 2026-09-10
4
+
5
+ ### Changed
6
+
7
+ - **The grid is an even split again** (6/6 for a 12-slot Friday+Saturday
8
+ weekend). The 5/7 shape 0.46.0 inferred from the live MOTD was an artifact of
9
+ a markup bug: with the `<a>` tags left unclosed, the blank line after the
10
+ fifth poster looks like a night break but isn't, and most screen widths hide
11
+ the resulting reflow. The generated snippet closes its tags, so it renders the
12
+ intended 6/6.
13
+ - A night with more scheduled titles than grid positions now records a warning
14
+ (job result, admin panel, and log) instead of silently dropping the extras.
15
+
3
16
  ## [0.46.0] - 2026-09-10
4
17
 
5
18
  ### Changed
@@ -15,6 +28,7 @@
15
28
  night is never truncated, so an over-full weekend shows every scheduled movie;
16
29
  `motd.slots` is a target the grid is only padded *up* to when the weekend is
17
30
  still thin.
31
+ (Reverted in 0.46.1 — the 5/7 reading was a markup artifact.)
18
32
  - **Sunday is excluded.** New `motd.nights` (default `[1, 2]`) controls which
19
33
  nights the grid covers; Sunday has no schedule yet, so its workbook titles are
20
34
  parsed but ignored. Add `3` once the scheduled-playlists feature fills that
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: kryten-webqueue
3
- Version: 0.46.0
3
+ Version: 0.46.1
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
@@ -127,22 +127,13 @@ def _grid_nights(motd_cfg) -> tuple[int, ...]:
127
127
  return nights or _NIGHTS
128
128
 
129
129
 
130
- def _slot_counts(
131
- titles_by_night: dict[int, list[str]], nights: tuple[int, ...], slots: int
132
- ) -> dict[int, int]:
133
- """Decide how many positions each night gets.
134
-
135
- The schedule drives the shape — a 5/7 weekend renders 5/7, not an even 6/6 —
136
- and a night is never truncated, so a scheduled movie can't be hidden. Only
137
- when the weekend is still thin is the grid padded up to ``slots``, handing
138
- each extra to whichever night is furthest below its even share.
139
- """
140
- counts = {n: len(titles_by_night.get(n, [])) for n in nights}
141
- share = slots / len(nights)
142
- while sum(counts.values()) < slots:
143
- night = min(nights, key=lambda n: (counts[n] - share, nights.index(n)))
144
- counts[night] += 1
145
- return counts
130
+ def _slot_counts(nights: tuple[int, ...], slots: int) -> dict[int, int]:
131
+ """Split ``slots`` evenly across ``nights``; extras go to the earlier nights."""
132
+ per_night, remainder = divmod(slots, len(nights))
133
+ return {
134
+ night: per_night + (1 if index <= remainder else 0)
135
+ for index, night in enumerate(nights, 1)
136
+ }
146
137
 
147
138
 
148
139
  def _blank_grid(
@@ -258,13 +249,26 @@ def build_slots(
258
249
  logger.warning("motd: workbook unavailable for %s: %s", week_key, exc)
259
250
 
260
251
  nights = _grid_nights(motd_cfg)
261
- week.slots = _blank_grid(
262
- friday,
263
- saturday,
264
- sunday,
265
- _slot_counts(titles_by_night, nights, slot_count),
266
- nights,
267
- )
252
+ counts = _slot_counts(nights, slot_count)
253
+ week.slots = _blank_grid(friday, saturday, sunday, counts, nights)
254
+
255
+ # The grid is a fixed size, so a night with more titles than positions would
256
+ # silently drop the extras. Surface it instead.
257
+ for night in nights:
258
+ extra = len(titles_by_night.get(night, [])) - counts[night]
259
+ if extra > 0:
260
+ label = _NIGHT_LABELS.get(night, f"Night {night}")
261
+ week.warnings.append(
262
+ f"{label}: {extra} scheduled title(s) beyond the "
263
+ f"{counts[night]}-slot grid are not shown"
264
+ )
265
+ logger.warning(
266
+ "motd: %s has %d title(s) beyond its %d slots on %s",
267
+ label,
268
+ extra,
269
+ counts[night],
270
+ week_key,
271
+ )
268
272
 
269
273
  if not dry_run:
270
274
  poster_dir.mkdir(parents=True, exist_ok=True)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.46.0"
3
+ version = "0.46.1"
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"
@@ -85,32 +85,35 @@ def test_grid_pads_a_thin_weekend_to_the_target_size(
85
85
  assert len([s for s in week.slots if s.night == 2]) == 6
86
86
 
87
87
 
88
- def test_grid_shape_follows_the_schedule(tmp_path, monkeypatch, stub_lookup):
89
- # A real 5/7 weekend renders 5/7, not an even 6/6.
88
+ def test_grid_shape_is_an_even_split(tmp_path, monkeypatch, stub_lookup):
90
89
  _stub_workbook(
91
90
  monkeypatch,
92
91
  {
93
- 1: [f"Friday {n} (1980)" for n in range(5)],
94
- 2: [f"Saturday {n} (1980)" for n in range(7)],
92
+ 1: [f"Friday {n} (1980)" for n in range(6)],
93
+ 2: [f"Saturday {n} (1980)" for n in range(6)],
95
94
  },
96
95
  )
97
96
  week = builder.build_slots(_config(tmp_path), today=datetime.date(2026, 3, 4))
98
- assert len([s for s in week.slots if s.night == 1]) == 5
99
- assert len([s for s in week.slots if s.night == 2]) == 7
97
+ assert len([s for s in week.slots if s.night == 1]) == 6
98
+ assert len([s for s in week.slots if s.night == 2]) == 6
100
99
  assert all(s.resolved for s in week.slots)
100
+ assert not week.warnings
101
101
 
102
102
 
103
- def test_grid_never_truncates_an_overfull_weekend(tmp_path, monkeypatch, stub_lookup):
103
+ def test_overfull_night_warns_instead_of_silently_dropping(
104
+ tmp_path, monkeypatch, stub_lookup
105
+ ):
104
106
  _stub_workbook(
105
107
  monkeypatch,
106
108
  {
107
109
  1: [f"Friday {n} (1980)" for n in range(8)],
108
- 2: [f"Saturday {n} (1980)" for n in range(7)],
110
+ 2: [f"Saturday {n} (1980)" for n in range(6)],
109
111
  },
110
112
  )
111
113
  week = builder.build_slots(_config(tmp_path), today=datetime.date(2026, 3, 4))
112
- assert len(week.slots) == 15
113
- assert "Friday 7 (1980)" in [s.title for s in week.slots]
114
+ assert len(week.slots) == 12
115
+ assert any("2 scheduled title(s)" in w for w in week.warnings)
116
+ assert "Friday 6 (1980)" not in [s.title for s in week.slots]
114
117
 
115
118
 
116
119
  def test_sunday_titles_are_ignored(tmp_path, monkeypatch, stub_lookup):