kryten-webqueue 0.36.0__tar.gz → 0.36.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 (162) hide show
  1. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/CHANGELOG.md +17 -0
  2. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/PKG-INFO +1 -1
  3. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/steps/tags.py +21 -11
  4. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/catalog/browse.html +7 -0
  5. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/pyproject.toml +1 -1
  6. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/.github/workflows/gitleaks.yml +0 -0
  7. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/.github/workflows/python-publish.yml +0 -0
  8. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/.github/workflows/release.yml +0 -0
  9. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/.gitignore +0 -0
  10. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/.gitleaks.toml +0 -0
  11. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/.pre-commit-config.yaml +0 -0
  12. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/AGENT-WORKFLOW-GUIDE.md +0 -0
  13. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/README.md +0 -0
  14. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/config.example.json +0 -0
  15. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/deploy/kryten-webqueue.service +0 -0
  16. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/deploy/nginx-queue.conf +0 -0
  17. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/IMPLEMENTATION_SPEC.md +0 -0
  18. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/IMPL_API_GATE.md +0 -0
  19. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/IMPL_ECONOMY.md +0 -0
  20. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/IMPL_KRYTEN_PY.md +0 -0
  21. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/IMPL_ROBOT.md +0 -0
  22. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
  23. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
  24. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/Log for incorrect promo insertion problem.txt +0 -0
  25. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
  26. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/PRE_PLAN_GAPS.md +0 -0
  27. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/PRODUCT_PLAN.md +0 -0
  28. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
  29. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/SPEC_EMOTE_REHOST.md +0 -0
  30. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
  31. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/docs/UX_POLISH_PLAN.md +0 -0
  32. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/__init__.py +0 -0
  33. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/__main__.py +0 -0
  34. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/api_gate/__init__.py +0 -0
  35. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/api_gate/client.py +0 -0
  36. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/app.py +0 -0
  37. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/auth/__init__.py +0 -0
  38. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/auth/otp.py +0 -0
  39. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/auth/rate_limit.py +0 -0
  40. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/auth/session.py +0 -0
  41. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/__init__.py +0 -0
  42. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/__init__.py +0 -0
  43. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_catalog.py +0 -0
  44. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_connection.py +0 -0
  45. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
  46. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_feedback.py +0 -0
  47. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_people.py +0 -0
  48. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_playlists.py +0 -0
  49. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_queue.py +0 -0
  50. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
  51. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
  52. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/classify.py +0 -0
  53. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/normalise.py +0 -0
  54. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
  55. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
  56. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/report.py +0 -0
  57. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
  58. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
  59. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
  60. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
  61. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
  62. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/enrichment/steps/title.py +0 -0
  63. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/images.py +0 -0
  64. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/mediacms.py +0 -0
  65. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/catalog/sync.py +0 -0
  66. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/config.py +0 -0
  67. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/__init__.py +0 -0
  68. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
  69. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
  70. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
  71. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
  72. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
  73. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
  74. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
  75. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
  76. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
  77. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
  78. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/jobs/__init__.py +0 -0
  79. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
  80. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/jobs/job_scheduler.py +0 -0
  81. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/jobs/manager.py +0 -0
  82. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
  83. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/jobs/tasks.py +0 -0
  84. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/logging_config.py +0 -0
  85. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/playlists/__init__.py +0 -0
  86. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/playlists/bulk_add.py +0 -0
  87. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/playlists/fire.py +0 -0
  88. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/playlists/importer.py +0 -0
  89. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/playlists/ordering.py +0 -0
  90. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/playlists/scheduler.py +0 -0
  91. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/promos/__init__.py +0 -0
  92. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/promos/director.py +0 -0
  93. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/queue/__init__.py +0 -0
  94. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/queue/completion.py +0 -0
  95. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/queue/ordering.py +0 -0
  96. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/queue/poller.py +0 -0
  97. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/queue/presence.py +0 -0
  98. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/queue/race_poller.py +0 -0
  99. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/queue/shadow.py +0 -0
  100. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/__init__.py +0 -0
  101. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_catalog.py +0 -0
  102. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_feedback.py +0 -0
  103. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
  104. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_jobs.py +0 -0
  105. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_moderation.py +0 -0
  106. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_playlists.py +0 -0
  107. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_promos.py +0 -0
  108. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_queue.py +0 -0
  109. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/admin_schedules.py +0 -0
  110. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/auth.py +0 -0
  111. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/catalog.py +0 -0
  112. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/feedback.py +0 -0
  113. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/pages.py +0 -0
  114. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/queue.py +0 -0
  115. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/routes/user.py +0 -0
  116. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/static/css/main.css +0 -0
  117. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/static/js/admin-playlists.js +0 -0
  118. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/static/js/admin-promos.js +0 -0
  119. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
  120. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/static/js/admin-schedules.js +0 -0
  121. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/static/js/admin.js +0 -0
  122. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/static/js/main.js +0 -0
  123. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/admin/index.html +0 -0
  124. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/admin/playlists.html +0 -0
  125. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/admin/promos.html +0 -0
  126. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
  127. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/admin/schedules.html +0 -0
  128. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/auth/login.html +0 -0
  129. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/base.html +0 -0
  130. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
  131. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
  132. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/feedback/index.html +0 -0
  133. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/queue/index.html +0 -0
  134. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/race.html +0 -0
  135. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/user/dashboard.html +0 -0
  136. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/templates/user/my_list.html +0 -0
  137. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/ws/__init__.py +0 -0
  138. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/ws/handler.py +0 -0
  139. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/kryten_webqueue/ws/manager.py +0 -0
  140. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/__init__.py +0 -0
  141. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_classify.py +0 -0
  142. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_config_persistence.py +0 -0
  143. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_feedback.py +0 -0
  144. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_fetchurls_sharepoint.py +0 -0
  145. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_people_facets.py +0 -0
  146. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_phase1.py +0 -0
  147. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_phase2_jobs.py +0 -0
  148. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_phase3_jobs.py +0 -0
  149. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_phase4_live_fixes.py +0 -0
  150. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_playlist_import.py +0 -0
  151. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_presence_refund.py +0 -0
  152. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_promo_director.py +0 -0
  153. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_promo_pool_exclusion.py +0 -0
  154. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_providers.py +0 -0
  155. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_queue_announce.py +0 -0
  156. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_race_poller.py +0 -0
  157. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_recently_played_hide.py +0 -0
  158. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_save_results_to_playlist.py +0 -0
  159. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_schedule_lock.py +0 -0
  160. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_search_facets.py +0 -0
  161. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_title_normalization.py +0 -0
  162. {kryten_webqueue-0.36.0 → kryten_webqueue-0.36.1}/tests/test_watchlist.py +0 -0
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.36.1] - 2026-08-14
6
+
7
+ ### Fixed
8
+
9
+ - **Tags now appear on item detail pages.** The tags enrichment step pushed tags to
10
+ MediaCMS via `PUT /media/{token}`, but that endpoint only accepts title/description
11
+ and silently ignores a `tags` field — so tags never landed in CMS and thus never
12
+ reverse-synced into the local catalog. Tag pushes now use the correct
13
+ `POST /media/user/bulk_actions` (`action: add_tags`) endpoint, and computed
14
+ genre/MPAA/hosted-show tags are also written directly to the local `catalog_tags`
15
+ table so they display regardless of CMS write success.
16
+ - **Person and Studio facet links now work.** Clicking a person or studio chip on an
17
+ item detail page dropped the filter and landed on the unfiltered browse page whenever
18
+ the user had a saved sort or hide-short preference: the `applyFacets()` redirect
19
+ rebuilt the URL from the dropdowns only and discarded the `person`/`studio` query
20
+ params. It now preserves them.
21
+
5
22
  ## [0.36.0] - 2026-08-14
6
23
 
7
24
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: kryten-webqueue
3
- Version: 0.36.0
3
+ Version: 0.36.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
@@ -83,10 +83,14 @@ class TagsStep:
83
83
  continue
84
84
 
85
85
  if not dry_run:
86
+ # Persist computed tags locally first so item detail shows
87
+ # them even if the CMS write below fails (owner/permission).
88
+ for tag in desired:
89
+ await self._db.add_catalog_tag(cls.friendly_token, tag)
86
90
  changed = await self._push_tags(
87
91
  client, cls.friendly_token, desired
88
92
  )
89
- # Reverse sync: pull CMS tags back to local DB
93
+ # Reverse sync: pull any CMS-only tags back to local DB
90
94
  await self._reverse_sync(client, cls.friendly_token)
91
95
  if changed:
92
96
  result.changed += 1
@@ -108,13 +112,18 @@ class TagsStep:
108
112
  async def _push_tags(
109
113
  self, client: httpx.AsyncClient, token: str, new_tags: list[str]
110
114
  ) -> bool:
111
- url = f"{self._base}/api/v1/media/{token}"
115
+ """Add tags to a CMS media item via the bulk_actions ``add_tags`` action.
116
+
117
+ The ``PUT /media/{token}`` endpoint only accepts title/description/media_file
118
+ and silently ignores a ``tags`` field, so tag changes must go through
119
+ ``POST /media/user/bulk_actions`` with ``action: add_tags``.
120
+ """
121
+ detail_url = f"{self._base}/api/v1/media/{token}"
112
122
  try:
113
- resp = await client.get(url)
123
+ resp = await client.get(detail_url)
114
124
  if resp.status_code != 200:
115
125
  return False
116
126
  data = resp.json()
117
- owner = data.get("user")
118
127
  current = {
119
128
  (t.get("title") if isinstance(t, dict) else t)
120
129
  for t in (data.get("tags_info") or [])
@@ -122,13 +131,14 @@ class TagsStep:
122
131
  to_add = [t for t in new_tags if t not in current]
123
132
  if not to_add:
124
133
  return False
125
- merged = list(current | set(to_add))
126
- put = await client.put(url, data={"tags": ",".join(merged)})
127
- if put.status_code in (200, 201) and owner:
128
- await client.post(
129
- f"{self._base}/api/v1/media/user/bulk_actions",
130
- json={"action": "change_owner", "media_ids": [token], "owner": owner},
131
- )
134
+ put = await client.post(
135
+ f"{self._base}/api/v1/media/user/bulk_actions",
136
+ json={
137
+ "action": "add_tags",
138
+ "media_ids": [token],
139
+ "tag_titles": to_add,
140
+ },
141
+ )
132
142
  return put.status_code in (200, 201)
133
143
  except (httpx.HTTPError, json.JSONDecodeError, ValueError):
134
144
  return False
@@ -203,6 +203,13 @@ function applyFacets() {
203
203
  }
204
204
  if (cat) params.set('category', cat);
205
205
  if (tag) params.set('tag', tag);
206
+ // Preserve person/studio facets — these come from the URL, not the dropdowns,
207
+ // and must survive a sort/hide_short change or the filter is silently dropped.
208
+ const current = new URLSearchParams(window.location.search);
209
+ const person = current.get('person');
210
+ const studio = current.get('studio');
211
+ if (person) params.set('person', person);
212
+ if (studio) params.set('studio', studio);
206
213
  if (sort && sort !== 'default') params.set('sort', sort);
207
214
  // Only include hide_short in URL when non-default (default = 1, hide).
208
215
  if (!hideShort) params.set('hide_short', '0');
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.36.0"
3
+ version = "0.36.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"