kryten-webqueue 0.35.4__tar.gz → 0.35.5__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.4 → kryten_webqueue-0.35.5}/CHANGELOG.md +11 -0
  2. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/PKG-INFO +1 -1
  3. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_catalog.py +47 -18
  4. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/pyproject.toml +1 -1
  5. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/.github/workflows/gitleaks.yml +0 -0
  6. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/.github/workflows/python-publish.yml +0 -0
  7. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/.github/workflows/release.yml +0 -0
  8. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/.gitignore +0 -0
  9. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/.gitleaks.toml +0 -0
  10. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/.pre-commit-config.yaml +0 -0
  11. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/AGENT-WORKFLOW-GUIDE.md +0 -0
  12. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/README.md +0 -0
  13. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/config.example.json +0 -0
  14. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/deploy/kryten-webqueue.service +0 -0
  15. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/deploy/nginx-queue.conf +0 -0
  16. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/IMPLEMENTATION_SPEC.md +0 -0
  17. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/IMPL_API_GATE.md +0 -0
  18. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/IMPL_ECONOMY.md +0 -0
  19. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/IMPL_KRYTEN_PY.md +0 -0
  20. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/IMPL_ROBOT.md +0 -0
  21. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
  22. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
  23. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/Log for incorrect promo insertion problem.txt +0 -0
  24. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
  25. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/PRE_PLAN_GAPS.md +0 -0
  26. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/PRODUCT_PLAN.md +0 -0
  27. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
  28. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/SPEC_EMOTE_REHOST.md +0 -0
  29. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
  30. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/docs/UX_POLISH_PLAN.md +0 -0
  31. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/__init__.py +0 -0
  32. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/__main__.py +0 -0
  33. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/api_gate/__init__.py +0 -0
  34. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/api_gate/client.py +0 -0
  35. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/app.py +0 -0
  36. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/auth/__init__.py +0 -0
  37. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/auth/otp.py +0 -0
  38. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/auth/rate_limit.py +0 -0
  39. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/auth/session.py +0 -0
  40. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/__init__.py +0 -0
  41. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/__init__.py +0 -0
  42. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_connection.py +0 -0
  43. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
  44. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_feedback.py +0 -0
  45. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_people.py +0 -0
  46. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_playlists.py +0 -0
  47. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_queue.py +0 -0
  48. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
  49. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
  50. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/classify.py +0 -0
  51. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/normalise.py +0 -0
  52. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
  53. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
  54. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/report.py +0 -0
  55. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
  56. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
  57. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
  58. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
  59. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
  60. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/steps/tags.py +0 -0
  61. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/enrichment/steps/title.py +0 -0
  62. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/images.py +0 -0
  63. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/mediacms.py +0 -0
  64. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/catalog/sync.py +0 -0
  65. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/config.py +0 -0
  66. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/__init__.py +0 -0
  67. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
  68. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
  69. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
  70. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
  71. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
  72. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
  73. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
  74. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
  75. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
  76. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
  77. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/jobs/__init__.py +0 -0
  78. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
  79. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/jobs/job_scheduler.py +0 -0
  80. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/jobs/manager.py +0 -0
  81. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
  82. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/jobs/tasks.py +0 -0
  83. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/logging_config.py +0 -0
  84. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/playlists/__init__.py +0 -0
  85. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/playlists/bulk_add.py +0 -0
  86. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/playlists/fire.py +0 -0
  87. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/playlists/importer.py +0 -0
  88. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/playlists/ordering.py +0 -0
  89. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/playlists/scheduler.py +0 -0
  90. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/promos/__init__.py +0 -0
  91. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/promos/director.py +0 -0
  92. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/queue/__init__.py +0 -0
  93. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/queue/completion.py +0 -0
  94. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/queue/ordering.py +0 -0
  95. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/queue/poller.py +0 -0
  96. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/queue/presence.py +0 -0
  97. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/queue/race_poller.py +0 -0
  98. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/queue/shadow.py +0 -0
  99. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/__init__.py +0 -0
  100. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_catalog.py +0 -0
  101. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_feedback.py +0 -0
  102. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
  103. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_jobs.py +0 -0
  104. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_moderation.py +0 -0
  105. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_playlists.py +0 -0
  106. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_promos.py +0 -0
  107. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_queue.py +0 -0
  108. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/admin_schedules.py +0 -0
  109. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/auth.py +0 -0
  110. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/catalog.py +0 -0
  111. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/feedback.py +0 -0
  112. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/pages.py +0 -0
  113. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/queue.py +0 -0
  114. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/routes/user.py +0 -0
  115. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/static/css/main.css +0 -0
  116. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/static/js/admin-playlists.js +0 -0
  117. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/static/js/admin-promos.js +0 -0
  118. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
  119. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/static/js/admin-schedules.js +0 -0
  120. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/static/js/admin.js +0 -0
  121. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/static/js/main.js +0 -0
  122. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/admin/index.html +0 -0
  123. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/admin/playlists.html +0 -0
  124. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/admin/promos.html +0 -0
  125. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
  126. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/admin/schedules.html +0 -0
  127. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/auth/login.html +0 -0
  128. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/base.html +0 -0
  129. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/catalog/browse.html +0 -0
  130. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
  131. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
  132. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/feedback/index.html +0 -0
  133. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/queue/index.html +0 -0
  134. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/race.html +0 -0
  135. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/user/dashboard.html +0 -0
  136. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/templates/user/my_list.html +0 -0
  137. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/ws/__init__.py +0 -0
  138. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/ws/handler.py +0 -0
  139. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/kryten_webqueue/ws/manager.py +0 -0
  140. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/__init__.py +0 -0
  141. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_classify.py +0 -0
  142. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_config_persistence.py +0 -0
  143. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_feedback.py +0 -0
  144. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_fetchurls_sharepoint.py +0 -0
  145. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_people_facets.py +0 -0
  146. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_phase1.py +0 -0
  147. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_phase2_jobs.py +0 -0
  148. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_phase3_jobs.py +0 -0
  149. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_phase4_live_fixes.py +0 -0
  150. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_playlist_import.py +0 -0
  151. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_presence_refund.py +0 -0
  152. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_promo_director.py +0 -0
  153. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_promo_pool_exclusion.py +0 -0
  154. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_providers.py +0 -0
  155. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_queue_announce.py +0 -0
  156. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_race_poller.py +0 -0
  157. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_recently_played_hide.py +0 -0
  158. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_save_results_to_playlist.py +0 -0
  159. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_schedule_lock.py +0 -0
  160. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_search_facets.py +0 -0
  161. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_title_normalization.py +0 -0
  162. {kryten_webqueue-0.35.4 → kryten_webqueue-0.35.5}/tests/test_watchlist.py +0 -0
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.35.5] - 2026-08-14
6
+
7
+ ### Fixed
8
+
9
+ - **SQL binding parameter error in enrichment pipeline.** Fixed `update_catalog`
10
+ method to support partial updates (updating only specific fields like title
11
+ or description without requiring all fields). Previously, the method required
12
+ all catalog fields to be present, causing "You did not supply a value for
13
+ binding parameter" errors when the meta and title enrichment steps attempted
14
+ partial updates. Now builds SQL dynamically based on provided fields.
15
+
5
16
  ## [0.35.4] - 2026-08-13
6
17
 
7
18
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: kryten-webqueue
3
- Version: 0.35.4
3
+ Version: 0.35.5
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
@@ -792,25 +792,54 @@ class _CatalogMixin:
792
792
  await self._db.commit()
793
793
 
794
794
  async def update_catalog(self, friendly_token: str, row: dict):
795
- sql = """
796
- UPDATE catalog SET title=:title, description=:description,
797
- duration_sec=:duration_sec, manifest_url=:manifest_url,
798
- thumbnail_url=:thumbnail_url,
799
- added_at=COALESCE(:added_at, added_at),
800
- synced_at=:synced_at, updated_at=:synced_at
801
- WHERE friendly_token=:friendly_token
795
+ """Update catalog item with partial or full field set.
796
+
797
+ Only fields present in *row* are updated. Always adds friendly_token
798
+ and updates updated_at to current time when any field changes.
802
799
  """
803
- row = {"added_at": None, **row}
804
- await self._db.execute(sql, row)
805
- # Rebuild FTS for this row
806
- await self._db.execute(
807
- "DELETE FROM catalog_fts WHERE friendly_token = ?", [friendly_token]
808
- )
809
- await self._db.execute(
810
- "INSERT INTO catalog_fts(rowid, friendly_token, title, description) "
811
- "SELECT rowid, friendly_token, title, description FROM catalog WHERE friendly_token = ?",
812
- [friendly_token],
813
- )
800
+ if not row:
801
+ return
802
+
803
+ # Build SET clause from provided fields
804
+ allowed = {
805
+ "title", "description", "duration_sec", "manifest_url",
806
+ "thumbnail_url", "added_at", "synced_at"
807
+ }
808
+ updates = []
809
+ params = {"friendly_token": friendly_token}
810
+
811
+ for key in row:
812
+ if key in allowed:
813
+ # added_at uses COALESCE to preserve existing if None
814
+ if key == "added_at":
815
+ updates.append("added_at=COALESCE(:added_at, added_at)")
816
+ else:
817
+ updates.append(f"{key}=:{key}")
818
+ params[key] = row[key]
819
+
820
+ # Always update updated_at when modifying
821
+ updates.append("updated_at=:updated_at")
822
+ params["updated_at"] = row.get("synced_at") or params.get("synced_at")
823
+ if not params["updated_at"]:
824
+ from datetime import datetime, UTC
825
+ params["updated_at"] = datetime.now(UTC).isoformat()
826
+
827
+ if not updates:
828
+ return
829
+
830
+ sql = f"UPDATE catalog SET {', '.join(updates)} WHERE friendly_token=:friendly_token"
831
+ await self._db.execute(sql, params)
832
+
833
+ # Rebuild FTS for this row if title or description changed
834
+ if "title" in row or "description" in row:
835
+ await self._db.execute(
836
+ "DELETE FROM catalog_fts WHERE friendly_token = ?", [friendly_token]
837
+ )
838
+ await self._db.execute(
839
+ "INSERT INTO catalog_fts(rowid, friendly_token, title, description) "
840
+ "SELECT rowid, friendly_token, title, description FROM catalog WHERE friendly_token = ?",
841
+ [friendly_token],
842
+ )
814
843
  await self._db.commit()
815
844
 
816
845
  async def update_cover_art(self, friendly_token: str, path: str, source: str):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryten-webqueue"
3
- version = "0.35.4"
3
+ version = "0.35.5"
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"