kryten-webqueue 0.35.0__tar.gz → 0.35.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.
Files changed (162) hide show
  1. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/CHANGELOG.md +37 -0
  2. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/PKG-INFO +1 -1
  3. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/jobs/tasks.py +58 -12
  4. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/static/css/main.css +20 -1
  5. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/static/js/admin.js +9 -5
  6. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/pyproject.toml +1 -1
  7. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/.github/workflows/gitleaks.yml +0 -0
  8. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/.github/workflows/python-publish.yml +0 -0
  9. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/.github/workflows/release.yml +0 -0
  10. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/.gitignore +0 -0
  11. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/.gitleaks.toml +0 -0
  12. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/.pre-commit-config.yaml +0 -0
  13. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/AGENT-WORKFLOW-GUIDE.md +0 -0
  14. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/README.md +0 -0
  15. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/config.example.json +0 -0
  16. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/deploy/kryten-webqueue.service +0 -0
  17. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/deploy/nginx-queue.conf +0 -0
  18. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/IMPLEMENTATION_SPEC.md +0 -0
  19. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/IMPL_API_GATE.md +0 -0
  20. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/IMPL_ECONOMY.md +0 -0
  21. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/IMPL_KRYTEN_PY.md +0 -0
  22. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/IMPL_ROBOT.md +0 -0
  23. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
  24. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
  25. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/Log for incorrect promo insertion problem.txt +0 -0
  26. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
  27. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/PRE_PLAN_GAPS.md +0 -0
  28. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/PRODUCT_PLAN.md +0 -0
  29. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
  30. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/SPEC_EMOTE_REHOST.md +0 -0
  31. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
  32. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/docs/UX_POLISH_PLAN.md +0 -0
  33. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/__init__.py +0 -0
  34. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/__main__.py +0 -0
  35. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/api_gate/__init__.py +0 -0
  36. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/api_gate/client.py +0 -0
  37. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/app.py +0 -0
  38. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/auth/__init__.py +0 -0
  39. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/auth/otp.py +0 -0
  40. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/auth/rate_limit.py +0 -0
  41. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/auth/session.py +0 -0
  42. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/__init__.py +0 -0
  43. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/__init__.py +0 -0
  44. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_catalog.py +0 -0
  45. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_connection.py +0 -0
  46. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
  47. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_feedback.py +0 -0
  48. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_people.py +0 -0
  49. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_playlists.py +0 -0
  50. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_queue.py +0 -0
  51. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
  52. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
  53. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/classify.py +0 -0
  54. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/normalise.py +0 -0
  55. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
  56. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
  57. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/report.py +0 -0
  58. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
  59. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
  60. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
  61. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
  62. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
  63. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/steps/tags.py +0 -0
  64. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/enrichment/steps/title.py +0 -0
  65. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/images.py +0 -0
  66. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/mediacms.py +0 -0
  67. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/catalog/sync.py +0 -0
  68. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/config.py +0 -0
  69. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/__init__.py +0 -0
  70. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
  71. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
  72. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
  73. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
  74. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
  75. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
  76. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
  77. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
  78. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
  79. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
  80. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/jobs/__init__.py +0 -0
  81. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
  82. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/jobs/job_scheduler.py +0 -0
  83. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/jobs/manager.py +0 -0
  84. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
  85. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/logging_config.py +0 -0
  86. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/playlists/__init__.py +0 -0
  87. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/playlists/bulk_add.py +0 -0
  88. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/playlists/fire.py +0 -0
  89. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/playlists/importer.py +0 -0
  90. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/playlists/ordering.py +0 -0
  91. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/playlists/scheduler.py +0 -0
  92. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/promos/__init__.py +0 -0
  93. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/promos/director.py +0 -0
  94. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/queue/__init__.py +0 -0
  95. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/queue/completion.py +0 -0
  96. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/queue/ordering.py +0 -0
  97. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/queue/poller.py +0 -0
  98. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/queue/presence.py +0 -0
  99. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/queue/race_poller.py +0 -0
  100. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/queue/shadow.py +0 -0
  101. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/__init__.py +0 -0
  102. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_catalog.py +0 -0
  103. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_feedback.py +0 -0
  104. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
  105. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_jobs.py +0 -0
  106. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_moderation.py +0 -0
  107. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_playlists.py +0 -0
  108. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_promos.py +0 -0
  109. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_queue.py +0 -0
  110. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/admin_schedules.py +0 -0
  111. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/auth.py +0 -0
  112. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/catalog.py +0 -0
  113. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/feedback.py +0 -0
  114. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/pages.py +0 -0
  115. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/queue.py +0 -0
  116. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/routes/user.py +0 -0
  117. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/static/js/admin-playlists.js +0 -0
  118. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/static/js/admin-promos.js +0 -0
  119. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
  120. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/static/js/admin-schedules.js +0 -0
  121. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/static/js/main.js +0 -0
  122. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/admin/index.html +0 -0
  123. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/admin/playlists.html +0 -0
  124. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/admin/promos.html +0 -0
  125. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
  126. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/admin/schedules.html +0 -0
  127. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/auth/login.html +0 -0
  128. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/base.html +0 -0
  129. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/catalog/browse.html +0 -0
  130. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
  131. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
  132. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/feedback/index.html +0 -0
  133. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/queue/index.html +0 -0
  134. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/race.html +0 -0
  135. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/user/dashboard.html +0 -0
  136. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/templates/user/my_list.html +0 -0
  137. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/ws/__init__.py +0 -0
  138. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/ws/handler.py +0 -0
  139. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/kryten_webqueue/ws/manager.py +0 -0
  140. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/__init__.py +0 -0
  141. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_classify.py +0 -0
  142. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_config_persistence.py +0 -0
  143. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_feedback.py +0 -0
  144. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_fetchurls_sharepoint.py +0 -0
  145. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_people_facets.py +0 -0
  146. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_phase1.py +0 -0
  147. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_phase2_jobs.py +0 -0
  148. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_phase3_jobs.py +0 -0
  149. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_phase4_live_fixes.py +0 -0
  150. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_playlist_import.py +0 -0
  151. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_presence_refund.py +0 -0
  152. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_promo_director.py +0 -0
  153. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_promo_pool_exclusion.py +0 -0
  154. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_providers.py +0 -0
  155. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_queue_announce.py +0 -0
  156. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_race_poller.py +0 -0
  157. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_recently_played_hide.py +0 -0
  158. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_save_results_to_playlist.py +0 -0
  159. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_schedule_lock.py +0 -0
  160. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_search_facets.py +0 -0
  161. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_title_normalization.py +0 -0
  162. {kryten_webqueue-0.35.0 → kryten_webqueue-0.35.2}/tests/test_watchlist.py +0 -0
@@ -2,6 +2,43 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.35.2] - 2026-08-13
6
+
7
+ ### Fixed
8
+
9
+ - **Job / schedule modals no longer clip off-screen.** The job-runner and
10
+ schedule parameter dialogs could grow taller than the viewport, pushing
11
+ the Cancel/Save/Run buttons below the fold where they were unreachable.
12
+
13
+ - `.modal-box` is now a flex column capped at `90vh` with `max-width`
14
+ widened from 420 px to 540 px.
15
+ - All scrollable content is wrapped in a `.modal-body` div that absorbs
16
+ the overflow; the action buttons are pinned at the bottom with a
17
+ separator line and `flex-shrink: 0`, so they are always reachable
18
+ regardless of how many parameters a job defines.
19
+
20
+ ## [0.35.1] - 2026-08-13
21
+
22
+ ### Fixed
23
+
24
+ - **Catalog Enrich job UX.** The `catalog_enrich` admin job modal was
25
+ unusable — raw comma-separated step names, unexplained "tokens" field.
26
+
27
+ - **Steps** is now a dropdown of labelled presets (e.g. *"Full pipeline",
28
+ "Enrichment only", "Art only", "Title cleanup only"*) rather than a
29
+ free-text field requiring knowledge of internal step names.
30
+ - **Specific items** replaces the opaque "Tokens" label. Placeholder text
31
+ and help text explain that these are the short MediaCMS media IDs visible
32
+ in item URLs, and that leaving it blank processes the whole catalog.
33
+ - **All fields** now show help text beneath them (Force re-run, Dry run,
34
+ Item limit, Description quality threshold are all explained).
35
+ - Help text rendering added to `_jobFieldHtml` in `admin.js` — all jobs
36
+ that define a `help` key on their schema fields now display it. A
37
+ `placeholder` key is also honoured on text/number inputs.
38
+ - `.field-help` CSS class added for the muted hint lines.
39
+ - Token parsing accepts comma- or space-separated IDs and ignores `"all"`
40
+ for the default blank case.
41
+
5
42
  ## [0.35.0] - 2026-08-13
6
43
 
7
44
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: kryten-webqueue
3
- Version: 0.35.0
3
+ Version: 0.35.2
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
@@ -104,9 +104,11 @@ async def catalog_enrich_job(params: dict, ctx) -> dict:
104
104
  )
105
105
  step_param = params.get("steps", "all")
106
106
  steps = None if step_param == "all" else [s.strip() for s in step_param.split(",")]
107
- token_param = params.get("tokens", "all")
107
+ token_param = (params.get("tokens") or "").strip()
108
108
  tokens = (
109
- None if token_param == "all" else [t.strip() for t in token_param.split(",")]
109
+ None
110
+ if not token_param or token_param == "all"
111
+ else [t.strip() for t in token_param.replace(",", " ").split() if t.strip()]
110
112
  )
111
113
 
112
114
  report = await pipeline.run(
@@ -124,19 +126,55 @@ async def catalog_enrich_job(params: dict, ctx) -> dict:
124
126
  CATALOG_ENRICH_SCHEMA = [
125
127
  {
126
128
  "name": "steps",
127
- "label": "Steps",
128
- "type": "string",
129
+ "label": "Pipeline steps",
130
+ "type": "enum",
129
131
  "default": "all",
130
132
  "required": False,
131
- "help": "Comma-separated: sync,classify,title,meta,art,tags,categories — or 'all'",
133
+ "options": [
134
+ {
135
+ "value": "all",
136
+ "label": "Full pipeline — sync → classify → title → meta → art → tags",
137
+ },
138
+ {
139
+ "value": "classify,meta,art,tags",
140
+ "label": "Enrichment only — classify, fetch metadata, art, and tags (no sync)",
141
+ },
142
+ {"value": "art", "label": "Art only — (re-)fetch poster art"},
143
+ {
144
+ "value": "classify,art",
145
+ "label": "Classify + art — re-detect content type then fetch art",
146
+ },
147
+ {
148
+ "value": "classify,meta",
149
+ "label": "Classify + metadata — descriptions and credits only",
150
+ },
151
+ {
152
+ "value": "tags",
153
+ "label": "Tags only — push genres/MPAA/hosted-show tags to CMS",
154
+ },
155
+ {
156
+ "value": "title",
157
+ "label": "Title cleanup only — normalise and push corrected titles",
158
+ },
159
+ {"value": "sync", "label": "Sync only — pull latest items from MediaCMS"},
160
+ {
161
+ "value": "classify",
162
+ "label": "Classify only — detect hosted shows, TV episodes, etc.",
163
+ },
164
+ ],
165
+ "help": "Choose which parts of the pipeline to run. 'Full pipeline' is the normal nightly run.",
132
166
  },
133
167
  {
134
168
  "name": "tokens",
135
- "label": "Tokens",
169
+ "label": "Specific items (optional)",
136
170
  "type": "string",
137
- "default": "all",
171
+ "default": None,
138
172
  "required": False,
139
- "help": "Comma-separated friendly_tokens to process, or 'all'",
173
+ "placeholder": "Leave blank for entire catalog",
174
+ "help": (
175
+ "Paste one or more MediaCMS media IDs (the short alphanumeric codes in item URLs, "
176
+ "e.g. AbCd1234) separated by commas or spaces. Leave blank to process the entire catalog."
177
+ ),
140
178
  },
141
179
  {
142
180
  "name": "force",
@@ -144,28 +182,36 @@ CATALOG_ENRICH_SCHEMA = [
144
182
  "type": "bool",
145
183
  "default": False,
146
184
  "required": False,
185
+ "help": "Re-process items even if they were enriched before. Useful after fixing a bug or adding a new hosted-show pattern.",
147
186
  },
148
187
  {
149
188
  "name": "dry_run",
150
- "label": "Dry run",
189
+ "label": "Dry run (preview only)",
151
190
  "type": "bool",
152
191
  "default": False,
153
192
  "required": False,
193
+ "help": "Walk through the pipeline and log what would change, but write nothing to the database or CMS.",
154
194
  },
155
195
  {
156
196
  "name": "limit",
157
- "label": "Limit",
197
+ "label": "Item limit per step",
158
198
  "type": "int",
159
199
  "default": None,
160
200
  "required": False,
161
- "help": "Max items per step (leave blank for all)",
201
+ "placeholder": "blank = no limit",
202
+ "help": "Stop after processing this many items per step. Handy for a quick test run before committing to the full catalog.",
162
203
  },
163
204
  {
164
205
  "name": "min_score",
165
- "label": "Min description score",
206
+ "label": "Description quality threshold",
166
207
  "type": "int",
167
208
  "default": 50,
168
209
  "required": False,
210
+ "help": (
211
+ "Skip the metadata step for items whose description already scores at or above this value "
212
+ "(0–130 scale; a fully-enriched description scores ~100). Lower this or use Force re-run "
213
+ "to refresh already-enriched items."
214
+ ),
169
215
  },
170
216
  ]
171
217
 
@@ -1357,10 +1357,20 @@ a.np-chip {
1357
1357
  box-shadow: var(--shadow);
1358
1358
  padding: 1.5rem;
1359
1359
  width: 100%;
1360
- max-width: 420px;
1360
+ max-width: 540px;
1361
+ max-height: 90vh;
1362
+ display: flex;
1363
+ flex-direction: column;
1364
+ }
1365
+ .modal-body {
1366
+ overflow-y: auto;
1367
+ flex: 1;
1368
+ min-height: 0;
1369
+ padding-right: 0.25rem; /* breathing room beside scrollbar */
1361
1370
  }
1362
1371
  .modal-box h3 {
1363
1372
  margin-bottom: 1rem;
1373
+ flex-shrink: 0;
1364
1374
  }
1365
1375
  .modal-actions {
1366
1376
  display: flex;
@@ -1368,6 +1378,9 @@ a.np-chip {
1368
1378
  justify-content: flex-end;
1369
1379
  flex-wrap: wrap;
1370
1380
  margin-top: 1.25rem;
1381
+ padding-top: 0.75rem;
1382
+ border-top: 1px solid var(--border);
1383
+ flex-shrink: 0;
1371
1384
  }
1372
1385
  .btn-secondary {
1373
1386
  background: var(--bg-card);
@@ -1506,6 +1519,12 @@ a.np-chip {
1506
1519
  font: inherit;
1507
1520
  width: 100%;
1508
1521
  }
1522
+ .field-help {
1523
+ font-size: 0.74rem;
1524
+ color: var(--text-secondary);
1525
+ opacity: 0.8;
1526
+ line-height: 1.4;
1527
+ }
1509
1528
  .check {
1510
1529
  display: flex;
1511
1530
  align-items: center;
@@ -49,11 +49,12 @@ function _jobFieldHtml(f, prefix = 'jobparam-') {
49
49
  const id = `${prefix}${f.name}`;
50
50
  const label = escapeHtml(f.label || f.name);
51
51
  const def = f.default;
52
+ const helpHtml = f.help ? `<span class="field-help">${escapeHtml(f.help)}</span>` : '';
52
53
  if (f.type === 'bool') {
53
54
  return `<label class="field field-inline">
54
55
  <input type="checkbox" id="${id}" ${def ? 'checked' : ''}>
55
56
  <span>${label}</span>
56
- </label>`;
57
+ </label>${helpHtml ? `<div style="margin:-0.5rem 0 0.85rem 0">${helpHtml}</div>` : ''}`;
57
58
  }
58
59
  if (f.type === 'enum') {
59
60
  const opts = (f.options || []).map(o => {
@@ -63,17 +64,18 @@ function _jobFieldHtml(f, prefix = 'jobparam-') {
63
64
  return `<option value="${escapeHtml(String(val))}" ${sel}>${escapeHtml(String(txt))}</option>`;
64
65
  }).join('');
65
66
  return `<label class="field"><span>${label}</span>
66
- <select id="${id}">${opts}</select></label>`;
67
+ <select id="${id}">${opts}</select>${helpHtml}</label>`;
67
68
  }
68
69
  if (f.type === 'playlist') {
69
70
  return `<label class="field"><span>${label}</span>
70
- <select id="${id}" data-playlist-picker="1"><option value="">—</option></select></label>`;
71
+ <select id="${id}" data-playlist-picker="1"><option value="">—</option></select>${helpHtml}</label>`;
71
72
  }
72
73
  const inputType = (f.type === 'int' || f.type === 'float') ? 'number' : 'text';
73
74
  const step = f.type === 'float' ? ' step="any"' : '';
74
75
  const val = (def === null || def === undefined) ? '' : escapeHtml(String(def));
76
+ const placeholder = f.placeholder ? ` placeholder="${escapeHtml(f.placeholder)}"` : '';
75
77
  return `<label class="field"><span>${label}${f.required ? ' *' : ''}</span>
76
- <input type="${inputType}"${step} id="${id}" value="${val}"></label>`;
78
+ <input type="${inputType}"${step}${placeholder} id="${id}" value="${val}">${helpHtml}</label>`;
77
79
  }
78
80
 
79
81
  async function openJobRunnerModal(job) {
@@ -84,7 +86,7 @@ async function openJobRunnerModal(job) {
84
86
  overlay.innerHTML = `
85
87
  <div class="modal-box" role="dialog" aria-modal="true">
86
88
  <h3>Run: ${escapeHtml(job.label)}</h3>
87
- <div class="job-params">${job.schema.map(f => _jobFieldHtml(f)).join('')}</div>
89
+ <div class="modal-body"><div class="job-params">${job.schema.map(f => _jobFieldHtml(f)).join('')}</div></div>
88
90
  <div class="modal-actions">
89
91
  <button class="btn btn-secondary" data-action="cancel">Cancel</button>
90
92
  <button class="btn btn-primary" data-action="run">Run</button>
@@ -157,6 +159,7 @@ async function openScheduleModal(jobName) {
157
159
  overlay.innerHTML = `
158
160
  <div class="modal-box" role="dialog" aria-modal="true">
159
161
  <h3>${existing ? 'Edit' : 'Create'} Schedule: ${escapeHtml(job.label)}</h3>
162
+ <div class="modal-body">
160
163
  <label class="field">
161
164
  <span>Cron expression <small style="font-weight:normal;opacity:.7">(min hour dom mon dow, UTC)</small></span>
162
165
  <input type="text" id="sched-cron" placeholder="0 21 * * 4" value="${cronVal}" autocomplete="off">
@@ -166,6 +169,7 @@ async function openScheduleModal(jobName) {
166
169
  <span>Active</span>
167
170
  </label>
168
171
  ${paramFields ? `<details ${existing ? 'open' : ''}><summary style="cursor:pointer;margin:.5rem 0">Job parameters</summary><div class="job-params">${paramFields}</div></details>` : ''}
172
+ </div>
169
173
  <div class="modal-actions">
170
174
  <button class="btn btn-secondary" data-action="cancel">Cancel</button>
171
175
  <button class="btn btn-primary" data-action="save">Save</button>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.35.0"
3
+ version = "0.35.2"
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"