kryten-webqueue 0.36.1__tar.gz → 0.36.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.36.1 → kryten_webqueue-0.36.2}/CHANGELOG.md +13 -0
  2. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/PKG-INFO +1 -1
  3. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/steps/tags.py +34 -3
  4. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/pyproject.toml +1 -1
  5. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/.github/workflows/gitleaks.yml +0 -0
  6. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/.github/workflows/python-publish.yml +0 -0
  7. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/.github/workflows/release.yml +0 -0
  8. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/.gitignore +0 -0
  9. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/.gitleaks.toml +0 -0
  10. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/.pre-commit-config.yaml +0 -0
  11. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/AGENT-WORKFLOW-GUIDE.md +0 -0
  12. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/README.md +0 -0
  13. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/config.example.json +0 -0
  14. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/deploy/kryten-webqueue.service +0 -0
  15. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/deploy/nginx-queue.conf +0 -0
  16. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/IMPLEMENTATION_SPEC.md +0 -0
  17. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/IMPL_API_GATE.md +0 -0
  18. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/IMPL_ECONOMY.md +0 -0
  19. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/IMPL_KRYTEN_PY.md +0 -0
  20. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/IMPL_ROBOT.md +0 -0
  21. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
  22. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
  23. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/Log for incorrect promo insertion problem.txt +0 -0
  24. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
  25. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/PRE_PLAN_GAPS.md +0 -0
  26. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/PRODUCT_PLAN.md +0 -0
  27. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
  28. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/SPEC_EMOTE_REHOST.md +0 -0
  29. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
  30. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/docs/UX_POLISH_PLAN.md +0 -0
  31. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/__init__.py +0 -0
  32. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/__main__.py +0 -0
  33. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/api_gate/__init__.py +0 -0
  34. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/api_gate/client.py +0 -0
  35. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/app.py +0 -0
  36. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/auth/__init__.py +0 -0
  37. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/auth/otp.py +0 -0
  38. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/auth/rate_limit.py +0 -0
  39. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/auth/session.py +0 -0
  40. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/__init__.py +0 -0
  41. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/__init__.py +0 -0
  42. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_catalog.py +0 -0
  43. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_connection.py +0 -0
  44. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
  45. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_feedback.py +0 -0
  46. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_people.py +0 -0
  47. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_playlists.py +0 -0
  48. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_queue.py +0 -0
  49. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
  50. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
  51. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/classify.py +0 -0
  52. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/normalise.py +0 -0
  53. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
  54. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
  55. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/report.py +0 -0
  56. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
  57. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
  58. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
  59. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
  60. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
  61. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/enrichment/steps/title.py +0 -0
  62. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/images.py +0 -0
  63. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/mediacms.py +0 -0
  64. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/catalog/sync.py +0 -0
  65. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/config.py +0 -0
  66. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/__init__.py +0 -0
  67. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
  68. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
  69. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
  70. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
  71. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
  72. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
  73. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
  74. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
  75. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
  76. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
  77. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/jobs/__init__.py +0 -0
  78. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
  79. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/jobs/job_scheduler.py +0 -0
  80. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/jobs/manager.py +0 -0
  81. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
  82. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/jobs/tasks.py +0 -0
  83. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/logging_config.py +0 -0
  84. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/playlists/__init__.py +0 -0
  85. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/playlists/bulk_add.py +0 -0
  86. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/playlists/fire.py +0 -0
  87. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/playlists/importer.py +0 -0
  88. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/playlists/ordering.py +0 -0
  89. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/playlists/scheduler.py +0 -0
  90. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/promos/__init__.py +0 -0
  91. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/promos/director.py +0 -0
  92. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/queue/__init__.py +0 -0
  93. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/queue/completion.py +0 -0
  94. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/queue/ordering.py +0 -0
  95. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/queue/poller.py +0 -0
  96. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/queue/presence.py +0 -0
  97. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/queue/race_poller.py +0 -0
  98. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/queue/shadow.py +0 -0
  99. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/__init__.py +0 -0
  100. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_catalog.py +0 -0
  101. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_feedback.py +0 -0
  102. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
  103. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_jobs.py +0 -0
  104. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_moderation.py +0 -0
  105. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_playlists.py +0 -0
  106. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_promos.py +0 -0
  107. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_queue.py +0 -0
  108. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/admin_schedules.py +0 -0
  109. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/auth.py +0 -0
  110. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/catalog.py +0 -0
  111. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/feedback.py +0 -0
  112. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/pages.py +0 -0
  113. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/queue.py +0 -0
  114. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/routes/user.py +0 -0
  115. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/static/css/main.css +0 -0
  116. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/static/js/admin-playlists.js +0 -0
  117. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/static/js/admin-promos.js +0 -0
  118. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
  119. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/static/js/admin-schedules.js +0 -0
  120. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/static/js/admin.js +0 -0
  121. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/static/js/main.js +0 -0
  122. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/admin/index.html +0 -0
  123. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/admin/playlists.html +0 -0
  124. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/admin/promos.html +0 -0
  125. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
  126. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/admin/schedules.html +0 -0
  127. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/auth/login.html +0 -0
  128. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/base.html +0 -0
  129. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/catalog/browse.html +0 -0
  130. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
  131. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
  132. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/feedback/index.html +0 -0
  133. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/queue/index.html +0 -0
  134. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/race.html +0 -0
  135. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/user/dashboard.html +0 -0
  136. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/templates/user/my_list.html +0 -0
  137. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/ws/__init__.py +0 -0
  138. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/ws/handler.py +0 -0
  139. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/kryten_webqueue/ws/manager.py +0 -0
  140. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/__init__.py +0 -0
  141. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_classify.py +0 -0
  142. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_config_persistence.py +0 -0
  143. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_feedback.py +0 -0
  144. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_fetchurls_sharepoint.py +0 -0
  145. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_people_facets.py +0 -0
  146. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_phase1.py +0 -0
  147. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_phase2_jobs.py +0 -0
  148. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_phase3_jobs.py +0 -0
  149. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_phase4_live_fixes.py +0 -0
  150. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_playlist_import.py +0 -0
  151. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_presence_refund.py +0 -0
  152. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_promo_director.py +0 -0
  153. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_promo_pool_exclusion.py +0 -0
  154. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_providers.py +0 -0
  155. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_queue_announce.py +0 -0
  156. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_race_poller.py +0 -0
  157. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_recently_played_hide.py +0 -0
  158. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_save_results_to_playlist.py +0 -0
  159. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_schedule_lock.py +0 -0
  160. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_search_facets.py +0 -0
  161. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_title_normalization.py +0 -0
  162. {kryten_webqueue-0.36.1 → kryten_webqueue-0.36.2}/tests/test_watchlist.py +0 -0
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.36.2] - 2026-08-14
6
+
7
+ ### Fixed
8
+
9
+ - **Stop flooding MediaCMS with futile tag-push 400s.** The tags step attempted
10
+ `POST /media/user/bulk_actions` (`add_tags`) for every enriched item, but that
11
+ endpoint filters `Media.objects.filter(user=request.user, ...)` with no manager
12
+ or superuser bypass, so pushes against media owned by other users always return
13
+ 400 "No matching media found". The step now looks up the API token's username
14
+ once via `/whoami` and only attempts the CMS push for media that user actually
15
+ owns. Local `catalog_tags` remains the source of truth for derived
16
+ genre/MPAA/hosted tags, so tags still display regardless.
17
+
5
18
  ## [0.36.1] - 2026-08-14
6
19
 
7
20
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: kryten-webqueue
3
- Version: 0.36.1
3
+ Version: 0.36.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
@@ -42,6 +42,22 @@ class TagsStep:
42
42
  )
43
43
  self._headers = {"Authorization": f"Token {config.mediacms_token}"}
44
44
 
45
+ async def _get_api_username(self, client: httpx.AsyncClient) -> str | None:
46
+ """Return the API token's username, or None.
47
+
48
+ MediaCMS ``/media/user/bulk_actions`` filters ``Media.objects.filter(
49
+ user=request.user, ...)`` with no manager/superuser bypass, so tag
50
+ pushes only succeed on media this user owns. We use this to skip futile
51
+ pushes against media owned by others instead of flooding CMS with 400s.
52
+ """
53
+ try:
54
+ resp = await client.get(f"{self._base}/api/v1/whoami")
55
+ if resp.status_code != 200:
56
+ return None
57
+ return resp.json().get("username")
58
+ except (httpx.HTTPError, json.JSONDecodeError, ValueError):
59
+ return None
60
+
45
61
  async def run(
46
62
  self,
47
63
  *,
@@ -54,6 +70,7 @@ class TagsStep:
54
70
  now = datetime.now(UTC).isoformat()
55
71
 
56
72
  async with httpx.AsyncClient(headers=self._headers, timeout=_TIMEOUT) as client:
73
+ api_user = None if dry_run else await self._get_api_username(client)
57
74
  for cls in classifications:
58
75
  result.processed += 1
59
76
  try:
@@ -88,7 +105,10 @@ class TagsStep:
88
105
  for tag in desired:
89
106
  await self._db.add_catalog_tag(cls.friendly_token, tag)
90
107
  changed = await self._push_tags(
91
- client, cls.friendly_token, desired
108
+ client,
109
+ cls.friendly_token,
110
+ desired,
111
+ api_user=api_user,
92
112
  )
93
113
  # Reverse sync: pull any CMS-only tags back to local DB
94
114
  await self._reverse_sync(client, cls.friendly_token)
@@ -110,13 +130,21 @@ class TagsStep:
110
130
  return result
111
131
 
112
132
  async def _push_tags(
113
- self, client: httpx.AsyncClient, token: str, new_tags: list[str]
133
+ self,
134
+ client: httpx.AsyncClient,
135
+ token: str,
136
+ new_tags: list[str],
137
+ *,
138
+ api_user: str | None = None,
114
139
  ) -> bool:
115
140
  """Add tags to a CMS media item via the bulk_actions ``add_tags`` action.
116
141
 
117
142
  The ``PUT /media/{token}`` endpoint only accepts title/description/media_file
118
143
  and silently ignores a ``tags`` field, so tag changes must go through
119
- ``POST /media/user/bulk_actions`` with ``action: add_tags``.
144
+ ``POST /media/user/bulk_actions`` with ``action: add_tags``. That endpoint
145
+ filters ``Media.objects.filter(user=request.user, ...)`` with no manager or
146
+ superuser bypass, so we skip the push unless the API user owns the media
147
+ rather than flooding CMS with 400s.
120
148
  """
121
149
  detail_url = f"{self._base}/api/v1/media/{token}"
122
150
  try:
@@ -124,6 +152,9 @@ class TagsStep:
124
152
  if resp.status_code != 200:
125
153
  return False
126
154
  data = resp.json()
155
+ owner = data.get("user")
156
+ if api_user is None or owner != api_user:
157
+ return False
127
158
  current = {
128
159
  (t.get("title") if isinstance(t, dict) else t)
129
160
  for t in (data.get("tags_info") or [])
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.36.1"
3
+ version = "0.36.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"