kryten-webqueue 0.36.3__tar.gz → 0.36.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.
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/CHANGELOG.md +23 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/PKG-INFO +1 -1
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/tags.py +22 -37
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/sync.py +69 -19
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/pyproject.toml +1 -1
- kryten_webqueue-0.36.5/scripts/patch_mediacms_bulk_facets.py +105 -0
- kryten_webqueue-0.36.5/scripts/trigger_enrich.py +45 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/.github/workflows/gitleaks.yml +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/.github/workflows/python-publish.yml +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/.github/workflows/release.yml +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/.gitignore +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/.gitleaks.toml +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/.pre-commit-config.yaml +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/AGENT-WORKFLOW-GUIDE.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/README.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/config.example.json +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/Kryten-Economy Service Quality Survey Report at v0.13.0.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/Kryten-Webqueue Service Quality Survey Report at v0.27.1.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/Log for incorrect promo insertion problem.txt +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/SPEC_CATALOG_ENRICHMENT_PIPELINE.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/SPEC_EMOTE_REHOST.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/app.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_catalog.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_connection.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_enrichment.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_feedback.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_people.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_playlists.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_queue.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/db/_watchlist.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/classify.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/normalise.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/pipeline.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/providers.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/report.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/art.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/categories.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/meta.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/sync.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/title.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/motdposters.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/playedmovies.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/jobs/job_scheduler.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/jobs/rehost_emotes.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/queue/completion.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/queue/race_poller.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_feedback.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_job_schedules.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_moderation.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/feedback.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/css/main.css +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin-playlists.js +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin-promos.js +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin-queue-mgmt.js +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin-schedules.js +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin.js +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/feedback/index.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/race.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/user/dashboard.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/user/my_list.html +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/scripts/patch_mediacms_tags.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/__init__.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_classify.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_feedback.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_people_facets.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_providers.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_race_poller.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_recently_played_hide.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_search_facets.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_title_normalization.py +0 -0
- {kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/tests/test_watchlist.py +0 -0
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.36.5] - 2026-08-14
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Tags step no longer reads every item.** With `mediacms_manage_all_media` set,
|
|
10
|
+
the tags step now issues a single idempotent `add_tags` POST per item (the
|
|
11
|
+
server skips tags already present) instead of a per-item `GET` to read current
|
|
12
|
+
tags/owner. The redundant per-item reverse-sync `GET` was also removed — catalog
|
|
13
|
+
sync already mirrors CMS tags in bulk via `/api/v1/media_facets`. Stock-CMS
|
|
14
|
+
(owner-scoped) deployments still do the ownership `GET` on the fallback path.
|
|
15
|
+
|
|
16
|
+
## [0.36.4] - 2026-08-14
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Catalog sync pulls tags/categories in bulk.** Instead of one
|
|
21
|
+
`GET /api/v1/media/{token}` per item (10k+ requests, ~30+ min), sync now
|
|
22
|
+
prefetches every item's facets from a new `GET /api/v1/media_facets` endpoint
|
|
23
|
+
in a handful of paginated calls (~35s for the full catalog). Falls back to the
|
|
24
|
+
per-item detail endpoint automatically when the bulk endpoint is unavailable
|
|
25
|
+
(stock MediaCMS). See `scripts/patch_mediacms_bulk_facets.py` for the CMS-side
|
|
26
|
+
endpoint.
|
|
27
|
+
|
|
5
28
|
## [0.36.3] - 2026-08-14
|
|
6
29
|
|
|
7
30
|
### Added
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/tags.py
RENAMED
|
@@ -117,8 +117,6 @@ class TagsStep:
|
|
|
117
117
|
desired,
|
|
118
118
|
api_user=api_user,
|
|
119
119
|
)
|
|
120
|
-
# Reverse sync: pull any CMS-only tags back to local DB
|
|
121
|
-
await self._reverse_sync(client, cls.friendly_token)
|
|
122
120
|
if changed:
|
|
123
121
|
result.changed += 1
|
|
124
122
|
else:
|
|
@@ -148,50 +146,37 @@ class TagsStep:
|
|
|
148
146
|
|
|
149
147
|
The ``PUT /media/{token}`` endpoint only accepts title/description/media_file
|
|
150
148
|
and silently ignores a ``tags`` field, so tag changes must go through
|
|
151
|
-
``POST /media/user/bulk_actions`` with ``action: add_tags``.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
``POST /media/user/bulk_actions`` with ``action: add_tags``.
|
|
150
|
+
|
|
151
|
+
When ``manage_all`` is set the server-side ``add_tags`` is idempotent
|
|
152
|
+
(it skips tags already present), so we POST directly with no per-item GET.
|
|
153
|
+
On stock MediaCMS the endpoint is owner-scoped, so we must first GET the
|
|
154
|
+
item to check ownership and skip the push for media we do not own.
|
|
155
155
|
"""
|
|
156
|
-
detail_url = f"{self._base}/api/v1/media/{token}"
|
|
157
156
|
try:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
if not self._manage_all:
|
|
158
|
+
resp = await client.get(f"{self._base}/api/v1/media/{token}")
|
|
159
|
+
if resp.status_code != 200:
|
|
160
|
+
return False
|
|
161
|
+
data = resp.json()
|
|
162
|
+
owner = data.get("user")
|
|
163
|
+
if api_user is None or owner != api_user:
|
|
164
|
+
return False
|
|
165
|
+
current = {
|
|
166
|
+
(t.get("title") if isinstance(t, dict) else t)
|
|
167
|
+
for t in (data.get("tags_info") or [])
|
|
168
|
+
}
|
|
169
|
+
new_tags = [t for t in new_tags if t not in current]
|
|
170
|
+
if not new_tags:
|
|
171
|
+
return False
|
|
172
172
|
put = await client.post(
|
|
173
173
|
f"{self._base}/api/v1/media/user/bulk_actions",
|
|
174
174
|
json={
|
|
175
175
|
"action": "add_tags",
|
|
176
176
|
"media_ids": [token],
|
|
177
|
-
"tag_titles":
|
|
177
|
+
"tag_titles": new_tags,
|
|
178
178
|
},
|
|
179
179
|
)
|
|
180
180
|
return put.status_code in (200, 201)
|
|
181
181
|
except (httpx.HTTPError, json.JSONDecodeError, ValueError):
|
|
182
182
|
return False
|
|
183
|
-
|
|
184
|
-
async def _reverse_sync(self, client: httpx.AsyncClient, token: str) -> None:
|
|
185
|
-
"""Insert any CMS-only tags into the local catalog_tags table."""
|
|
186
|
-
url = f"{self._base}/api/v1/media/{token}"
|
|
187
|
-
try:
|
|
188
|
-
resp = await client.get(url)
|
|
189
|
-
if resp.status_code != 200:
|
|
190
|
-
return
|
|
191
|
-
data = resp.json()
|
|
192
|
-
for t in data.get("tags_info") or []:
|
|
193
|
-
name = t.get("title") if isinstance(t, dict) else t
|
|
194
|
-
if name:
|
|
195
|
-
await self._db.add_catalog_tag(token, str(name))
|
|
196
|
-
except (httpx.HTTPError, json.JSONDecodeError, ValueError) as exc:
|
|
197
|
-
logger.warning("[tags] Failed reverse-sync for %s: %s", token, exc)
|
|
@@ -46,6 +46,9 @@ class CatalogSync:
|
|
|
46
46
|
self._token = mediacms_token
|
|
47
47
|
self._db = db
|
|
48
48
|
self._cover_art = cover_art
|
|
49
|
+
# Populated once per sync by _prefetch_all_facets; None means the bulk
|
|
50
|
+
# endpoint is unavailable and we fall back to per-item detail fetches.
|
|
51
|
+
self._facets_map: dict[str, dict] | None = None
|
|
49
52
|
self._client = httpx.AsyncClient(
|
|
50
53
|
headers={"Authorization": f"Token {mediacms_token}"},
|
|
51
54
|
timeout=30.0,
|
|
@@ -60,6 +63,10 @@ class CatalogSync:
|
|
|
60
63
|
stats = {"seen": 0, "new": 0, "updated": 0, "errors": 0, "deleted": 0}
|
|
61
64
|
sync_started_at = datetime.now(UTC).isoformat()
|
|
62
65
|
|
|
66
|
+
# Pull every item's tags/categories in a few paginated calls up front so
|
|
67
|
+
# per-item facet sync needs no HTTP (falls back to detail fetch on miss).
|
|
68
|
+
await self._prefetch_all_facets()
|
|
69
|
+
|
|
63
70
|
try:
|
|
64
71
|
# /manage_media returns all items (9k+); /media is capped at 1000
|
|
65
72
|
next_url: str | None = f"{self._url}/api/v1/manage_media"
|
|
@@ -191,33 +198,76 @@ class CatalogSync:
|
|
|
191
198
|
await asyncio.sleep(0.25)
|
|
192
199
|
|
|
193
200
|
async def _sync_item_facets(self, token: str):
|
|
194
|
-
"""Populate category/tag memberships
|
|
201
|
+
"""Populate category/tag memberships for an item.
|
|
195
202
|
|
|
196
|
-
|
|
197
|
-
the per-item detail (``/api/v1/media/{token}``) which
|
|
198
|
-
``categories_info`` and ``tags_info
|
|
203
|
+
Prefers the bulk facets pulled by :meth:`_prefetch_all_facets`; falls
|
|
204
|
+
back to the per-item detail endpoint (``/api/v1/media/{token}``) which
|
|
205
|
+
exposes ``categories_info`` and ``tags_info`` when the bulk map is
|
|
206
|
+
unavailable or missing this token.
|
|
199
207
|
"""
|
|
200
|
-
|
|
201
|
-
if
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
cached = self._facets_map.get(token) if self._facets_map is not None else None
|
|
209
|
+
if cached is not None:
|
|
210
|
+
cat_names = cached.get("categories") or []
|
|
211
|
+
tag_names = cached.get("tags") or []
|
|
212
|
+
else:
|
|
213
|
+
resp = await self._client.get(f"{self._url}/api/v1/media/{token}")
|
|
214
|
+
if resp.status_code != 200:
|
|
215
|
+
return
|
|
216
|
+
data = resp.json()
|
|
217
|
+
cat_names = [
|
|
218
|
+
c.get("title")
|
|
219
|
+
for c in (data.get("categories_info") or [])
|
|
220
|
+
if isinstance(c, dict) and c.get("title")
|
|
221
|
+
]
|
|
222
|
+
tag_names = []
|
|
223
|
+
for t in data.get("tags_info") or []:
|
|
224
|
+
name = t.get("title") if isinstance(t, dict) else t
|
|
225
|
+
if name:
|
|
226
|
+
tag_names.append(name)
|
|
215
227
|
|
|
216
228
|
cat_ids = [await self._db.upsert_category(n) for n in cat_names]
|
|
217
229
|
tag_ids = [await self._db.upsert_tag(n) for n in tag_names]
|
|
218
230
|
await self._db.set_catalog_categories(token, cat_ids)
|
|
219
231
|
await self._db.set_catalog_tags(token, tag_ids)
|
|
220
232
|
|
|
233
|
+
async def _prefetch_all_facets(self) -> None:
|
|
234
|
+
"""Bulk-load every item's tags/categories from ``/api/v1/media_facets``.
|
|
235
|
+
|
|
236
|
+
One call per page (page_size=500) instead of one GET per item. Sets
|
|
237
|
+
``self._facets_map`` to None on any failure so callers fall back to the
|
|
238
|
+
per-item detail endpoint.
|
|
239
|
+
"""
|
|
240
|
+
facets: dict[str, dict] = {}
|
|
241
|
+
page = 1
|
|
242
|
+
try:
|
|
243
|
+
while True:
|
|
244
|
+
resp = await self._client.get(
|
|
245
|
+
f"{self._url}/api/v1/media_facets",
|
|
246
|
+
params={"page": page, "page_size": 500},
|
|
247
|
+
)
|
|
248
|
+
if resp.status_code != 200:
|
|
249
|
+
# Endpoint absent (stock CMS) — fall back to per-item.
|
|
250
|
+
self._facets_map = None if page == 1 else facets
|
|
251
|
+
return
|
|
252
|
+
data = resp.json()
|
|
253
|
+
for row in data.get("results") or []:
|
|
254
|
+
tok = row.get("friendly_token")
|
|
255
|
+
if tok:
|
|
256
|
+
facets[tok] = {
|
|
257
|
+
"tags": row.get("tags") or [],
|
|
258
|
+
"categories": row.get("categories") or [],
|
|
259
|
+
}
|
|
260
|
+
if not data.get("has_next"):
|
|
261
|
+
break
|
|
262
|
+
page += 1
|
|
263
|
+
self._facets_map = facets
|
|
264
|
+
logger.info(
|
|
265
|
+
"Prefetched facets for %d items in %d page(s)", len(facets), page
|
|
266
|
+
)
|
|
267
|
+
except (httpx.HTTPError, ValueError) as exc:
|
|
268
|
+
logger.debug("Facet prefetch failed (%s); using per-item fallback", exc)
|
|
269
|
+
self._facets_map = None
|
|
270
|
+
|
|
221
271
|
def _build_manifest_url(self, media: dict) -> str:
|
|
222
272
|
# CyTube custom media ("cm") requires the manifest JSON URL, NOT the
|
|
223
273
|
# human watch page (/view?m=TOKEN). MediaCMS exposes a CyTube manifest at
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Add a read-only bulk facets endpoint to MediaCMS so integrations can pull
|
|
3
|
+
every item's friendly_token + tags + categories in a few paginated calls
|
|
4
|
+
instead of one GET per media. Editor/superuser only. Idempotent."""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
BASE = "/home/mediacms.io/mediacms/files"
|
|
9
|
+
MEDIA = f"{BASE}/views/media.py"
|
|
10
|
+
INIT = f"{BASE}/views/__init__.py"
|
|
11
|
+
URLS = f"{BASE}/urls.py"
|
|
12
|
+
|
|
13
|
+
VIEW_MARKER = "class MediaFacetsList(APIView):"
|
|
14
|
+
VIEW_CODE = '''
|
|
15
|
+
|
|
16
|
+
class MediaFacetsList(APIView):
|
|
17
|
+
"""Bulk read-only listing of media friendly_token + tags + categories.
|
|
18
|
+
|
|
19
|
+
Lets trusted integrations (MediaCMS editors) pull every item's facets in a
|
|
20
|
+
few paginated calls instead of one GET per media. Slice pagination by id.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
permission_classes = (permissions.IsAuthenticated,)
|
|
24
|
+
|
|
25
|
+
def get(self, request, format=None):
|
|
26
|
+
if not is_mediacms_editor(request.user):
|
|
27
|
+
return Response({"detail": "not allowed"}, status=status.HTTP_403_FORBIDDEN)
|
|
28
|
+
try:
|
|
29
|
+
page = max(1, int(request.query_params.get("page", 1)))
|
|
30
|
+
except (TypeError, ValueError):
|
|
31
|
+
page = 1
|
|
32
|
+
try:
|
|
33
|
+
page_size = int(request.query_params.get("page_size", 500))
|
|
34
|
+
except (TypeError, ValueError):
|
|
35
|
+
page_size = 500
|
|
36
|
+
page_size = max(1, min(2000, page_size))
|
|
37
|
+
start = (page - 1) * page_size
|
|
38
|
+
rows = Media.objects.order_by("id").prefetch_related("tags", "category")[
|
|
39
|
+
start : start + page_size
|
|
40
|
+
]
|
|
41
|
+
results = [
|
|
42
|
+
{
|
|
43
|
+
"friendly_token": m.friendly_token,
|
|
44
|
+
"tags": [t.title for t in m.tags.all()],
|
|
45
|
+
"categories": [c.title for c in m.category.all()],
|
|
46
|
+
}
|
|
47
|
+
for m in rows
|
|
48
|
+
]
|
|
49
|
+
return Response(
|
|
50
|
+
{
|
|
51
|
+
"page": page,
|
|
52
|
+
"page_size": page_size,
|
|
53
|
+
"has_next": len(results) == page_size,
|
|
54
|
+
"results": results,
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
'''
|
|
58
|
+
|
|
59
|
+
INIT_ANCHOR = "from .media import MediaBulkUserActions # noqa: F401\n"
|
|
60
|
+
INIT_ADD = "from .media import MediaFacetsList # noqa: F401\n"
|
|
61
|
+
|
|
62
|
+
URL_ANCHOR = (
|
|
63
|
+
' re_path(r"^api/v1/media/user/bulk_actions/$", views.MediaBulkUserActions.as_view()),\n'
|
|
64
|
+
)
|
|
65
|
+
URL_ADD = ' re_path(r"^api/v1/media_facets$", views.MediaFacetsList.as_view()),\n'
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def patch(path, check, apply_fn):
|
|
69
|
+
with open(path, encoding="utf-8") as f:
|
|
70
|
+
src = f.read()
|
|
71
|
+
if check in src:
|
|
72
|
+
print(f"{path}: already patched")
|
|
73
|
+
return True
|
|
74
|
+
new = apply_fn(src)
|
|
75
|
+
if new is None:
|
|
76
|
+
print(f"ERROR: anchor not found in {path}")
|
|
77
|
+
return False
|
|
78
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
79
|
+
f.write(new)
|
|
80
|
+
print(f"{path}: patched")
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def main() -> int:
|
|
85
|
+
ok = True
|
|
86
|
+
ok &= patch(MEDIA, VIEW_MARKER, lambda s: s.rstrip("\n") + "\n" + VIEW_CODE)
|
|
87
|
+
ok &= patch(
|
|
88
|
+
INIT,
|
|
89
|
+
INIT_ADD.strip(),
|
|
90
|
+
lambda s: s.replace(INIT_ANCHOR, INIT_ANCHOR + INIT_ADD, 1)
|
|
91
|
+
if INIT_ANCHOR in s
|
|
92
|
+
else None,
|
|
93
|
+
)
|
|
94
|
+
ok &= patch(
|
|
95
|
+
URLS,
|
|
96
|
+
"media_facets",
|
|
97
|
+
lambda s: s.replace(URL_ANCHOR, URL_ANCHOR + URL_ADD, 1)
|
|
98
|
+
if URL_ANCHOR in s
|
|
99
|
+
else None,
|
|
100
|
+
)
|
|
101
|
+
return 0 if ok else 1
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
sys.exit(main())
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""One-off: mint an admin session and trigger the catalog_enrich job locally.
|
|
2
|
+
|
|
3
|
+
Usage (on the server, with the service venv python):
|
|
4
|
+
python trigger_enrich.py steps=tags force=1
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
import urllib.request
|
|
10
|
+
from datetime import datetime, timedelta, UTC
|
|
11
|
+
|
|
12
|
+
import jwt
|
|
13
|
+
|
|
14
|
+
CONFIG_PATH = "/etc/kryten-webqueue/config.json"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main() -> int:
|
|
18
|
+
params = dict(a.split("=", 1) for a in sys.argv[1:] if "=" in a)
|
|
19
|
+
cfg = json.load(open(CONFIG_PATH))
|
|
20
|
+
secret = cfg["secret_key"]
|
|
21
|
+
port = cfg.get("port", 2010)
|
|
22
|
+
token = jwt.encode(
|
|
23
|
+
{
|
|
24
|
+
"sub": "backfill-admin",
|
|
25
|
+
"rank": 3,
|
|
26
|
+
"iat": datetime.now(UTC),
|
|
27
|
+
"exp": datetime.now(UTC) + timedelta(hours=1),
|
|
28
|
+
},
|
|
29
|
+
secret,
|
|
30
|
+
algorithm="HS256",
|
|
31
|
+
)
|
|
32
|
+
body = json.dumps({"params": params}).encode()
|
|
33
|
+
req = urllib.request.Request(
|
|
34
|
+
f"http://127.0.0.1:{port}/admin/jobs/catalog_enrich/run",
|
|
35
|
+
data=body,
|
|
36
|
+
headers={"Content-Type": "application/json", "Cookie": f"session={token}"},
|
|
37
|
+
method="POST",
|
|
38
|
+
)
|
|
39
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
40
|
+
print(resp.status, resp.read().decode())
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
raise SystemExit(main())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/docs/Log for incorrect promo insertion problem.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/classify.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/normalise.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/pipeline.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/providers.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/report.py
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/art.py
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/meta.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/sync.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/catalog/enrichment/steps/title.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/enrichtv.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/cmsutils/fetchurls.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/ytpipe/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/integrations/ytpipe/downloader.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_job_schedules.py
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/routes/admin_moderation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin-playlists.js
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin-queue-mgmt.js
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/static/js/admin-schedules.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/promos.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/catalog/item_detail.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/feedback/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/queue/index.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/user/dashboard.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.36.3 → kryten_webqueue-0.36.5}/kryten_webqueue/templates/user/my_list.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|