codex 1.7.3a2__py3-none-any.whl → 1.7.5__py3-none-any.whl
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.
Potentially problematic release.
This version of codex might be problematic. Click here for more details.
- codex/asgi.py +2 -1
- codex/choices/__init__.py +1 -0
- codex/{choices.py → choices/admin.py} +47 -146
- codex/choices/browser.py +85 -0
- codex/{choices_to_json.py → choices/choices_to_json.py} +25 -19
- codex/choices/notifications.py +16 -0
- codex/choices/reader.py +33 -0
- codex/db.py +2 -2
- codex/exceptions.py +7 -8
- codex/librarian/bookmark/bookmarkd.py +8 -4
- codex/librarian/bookmark/update.py +84 -49
- codex/librarian/covers/create.py +10 -8
- codex/librarian/covers/path.py +6 -5
- codex/librarian/covers/purge.py +4 -4
- codex/librarian/importer/aggregate.py +2 -2
- codex/librarian/importer/cache.py +1 -4
- codex/librarian/importer/create_comics.py +2 -2
- codex/librarian/importer/create_covers.py +1 -1
- codex/librarian/importer/create_fks.py +4 -3
- codex/librarian/importer/deleted.py +3 -3
- codex/librarian/importer/extract.py +56 -37
- codex/librarian/importer/failed_imports.py +1 -1
- codex/librarian/importer/importer.py +8 -3
- codex/librarian/importer/importerd.py +3 -3
- codex/librarian/importer/link_comics.py +2 -1
- codex/librarian/importer/moved.py +2 -7
- codex/librarian/importer/query_fks.py +1 -4
- codex/librarian/importer/tasks.py +1 -1
- codex/librarian/janitor/integrity.py +27 -20
- codex/librarian/janitor/janitor.py +1 -1
- codex/librarian/janitor/latest_version.py +1 -1
- codex/librarian/janitor/update.py +5 -2
- codex/librarian/janitor/vacuum.py +1 -1
- codex/librarian/librariand.py +3 -2
- codex/librarian/notifier/notifierd.py +2 -1
- codex/librarian/notifier/tasks.py +17 -3
- codex/librarian/search/optimize.py +1 -1
- codex/librarian/search/remove.py +1 -1
- codex/librarian/search/update.py +4 -4
- codex/librarian/telemeter/stats.py +7 -6
- codex/librarian/telemeter/telemeter.py +0 -1
- codex/librarian/watchdog/db_snapshot.py +3 -3
- codex/librarian/watchdog/dir_snapshot_diff.py +3 -4
- codex/librarian/watchdog/emitter.py +4 -4
- codex/librarian/watchdog/event_batcherd.py +2 -1
- codex/librarian/watchdog/events.py +1 -2
- codex/librarian/watchdog/observers.py +15 -10
- codex/logger/logging.py +1 -1
- codex/memory.py +3 -2
- codex/migrations/0001_init.py +1 -2
- codex/migrations/0014_pdf_issue_suffix_remove_cover_image_sort_name.py +1 -2
- codex/migrations/0018_rename_userbookmark_bookmark.py +1 -2
- codex/migrations/0028_telemeter.py +1 -5
- codex/models/base.py +3 -1
- codex/models/bookmark.py +4 -7
- codex/models/comic.py +2 -2
- codex/models/functions.py +12 -5
- codex/models/groups.py +1 -1
- codex/models/library.py +1 -1
- codex/models/paths.py +4 -4
- codex/models/query.py +8 -4
- codex/models/util.py +1 -1
- codex/permissions.py +1 -1
- codex/run.py +1 -1
- codex/serializers/admin/libraries.py +1 -3
- codex/serializers/admin/stats.py +4 -4
- codex/serializers/admin/tasks.py +12 -2
- codex/serializers/auth.py +4 -2
- codex/serializers/browser/choices.py +87 -9
- codex/serializers/browser/filters.py +9 -37
- codex/serializers/browser/metadata.py +2 -1
- codex/serializers/browser/mixins.py +7 -3
- codex/serializers/browser/mtime.py +3 -5
- codex/serializers/browser/page.py +5 -4
- codex/serializers/browser/settings.py +8 -6
- codex/serializers/fields/__init__.py +37 -0
- codex/serializers/fields/auth.py +45 -0
- codex/serializers/fields/browser.py +85 -0
- codex/serializers/fields/group.py +13 -0
- codex/serializers/fields/reader.py +22 -0
- codex/serializers/fields/sanitized.py +13 -0
- codex/serializers/fields/session.py +16 -0
- codex/serializers/fields/stats.py +36 -0
- codex/serializers/fields/vuetify.py +62 -0
- codex/serializers/models/base.py +1 -1
- codex/serializers/models/comic.py +2 -2
- codex/serializers/models/groups.py +0 -2
- codex/serializers/models/named.py +1 -1
- codex/serializers/models/pycountry.py +1 -1
- codex/serializers/opds/authentication.py +4 -2
- codex/serializers/opds/urls.py +2 -1
- codex/serializers/opds/v1.py +4 -3
- codex/serializers/opds/v2/feed.py +2 -1
- codex/serializers/opds/v2/links.py +23 -21
- codex/serializers/opds/v2/metadata.py +4 -8
- codex/serializers/opds/v2/progression.py +5 -3
- codex/serializers/opds/v2/publication.py +16 -18
- codex/serializers/opds/v2/unused.py +15 -8
- codex/serializers/reader.py +16 -14
- codex/serializers/redirect.py +2 -1
- codex/serializers/route.py +6 -4
- codex/serializers/settings.py +5 -3
- codex/serializers/versions.py +2 -1
- codex/settings/settings.py +4 -3
- codex/signals/django_signals.py +1 -1
- codex/startup.py +2 -2
- codex/static_root/assets/{VCheckbox-FaT6MGfu.f6732060f734.js → VCheckbox-CWBDr4kF.6da5bf3a4d90.js} +1 -1
- codex/static_root/assets/VCheckbox-CWBDr4kF.6da5bf3a4d90.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWBDr4kF.6da5bf3a4d90.js.gz +0 -0
- codex/static_root/assets/{VCheckbox-FaT6MGfu.js → VCheckbox-CWBDr4kF.js} +1 -1
- codex/static_root/assets/VCheckbox-CWBDr4kF.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWBDr4kF.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.847452d574cf.js → VCheckboxBtn-COaoh3uF.fc880a386827.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-COaoh3uF.fc880a386827.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-COaoh3uF.fc880a386827.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.js → VCheckboxBtn-COaoh3uF.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-COaoh3uF.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-COaoh3uF.js.gz +0 -0
- codex/static_root/assets/{VCombobox-C8QLNlSl.6f431e19a453.js → VCombobox-DkOruNH0.f30f163ee632.js} +1 -1
- codex/static_root/assets/VCombobox-DkOruNH0.f30f163ee632.js.br +0 -0
- codex/static_root/assets/VCombobox-DkOruNH0.f30f163ee632.js.gz +0 -0
- codex/static_root/assets/{VCombobox-C8QLNlSl.js → VCombobox-DkOruNH0.js} +1 -1
- codex/static_root/assets/VCombobox-DkOruNH0.js.br +0 -0
- codex/static_root/assets/VCombobox-DkOruNH0.js.gz +0 -0
- codex/static_root/assets/{VDialog-RVLeW7je.c0c7eef71eec.js → VDialog-Dr7SuC2S.b6ec7df390f4.js} +1 -1
- codex/static_root/assets/VDialog-Dr7SuC2S.b6ec7df390f4.js.br +0 -0
- codex/static_root/assets/VDialog-Dr7SuC2S.b6ec7df390f4.js.gz +0 -0
- codex/static_root/assets/{VDialog-RVLeW7je.js → VDialog-Dr7SuC2S.js} +1 -1
- codex/static_root/assets/VDialog-Dr7SuC2S.js.br +0 -0
- codex/static_root/assets/VDialog-Dr7SuC2S.js.gz +0 -0
- codex/static_root/assets/{VDivider-D1Ot4vR2.9dbee744fb3c.js → VDivider-By4u2EsM.fd5ee9cce906.js} +1 -1
- codex/static_root/assets/VDivider-By4u2EsM.fd5ee9cce906.js.br +0 -0
- codex/static_root/assets/VDivider-By4u2EsM.fd5ee9cce906.js.gz +0 -0
- codex/static_root/assets/{VDivider-D1Ot4vR2.js → VDivider-By4u2EsM.js} +1 -1
- codex/static_root/assets/VDivider-By4u2EsM.js.br +0 -0
- codex/static_root/assets/VDivider-By4u2EsM.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-DQilGHZk.43f06d0a45e0.js → VExpansionPanels-BhO9oj0a.e99bea455c78.js} +1 -1
- codex/static_root/assets/VExpansionPanels-BhO9oj0a.e99bea455c78.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-BhO9oj0a.e99bea455c78.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-DQilGHZk.js → VExpansionPanels-BhO9oj0a.js} +1 -1
- codex/static_root/assets/VExpansionPanels-BhO9oj0a.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-BhO9oj0a.js.gz +0 -0
- codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js +1 -0
- codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js.br +0 -0
- codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js.gz +0 -0
- codex/static_root/assets/VForm-XhA6T0Lc.js +1 -0
- codex/static_root/assets/VForm-XhA6T0Lc.js.br +0 -0
- codex/static_root/assets/VForm-XhA6T0Lc.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-DoayJpcS.fe5e0e74ffee.js → VRadioGroup-C1WCG0rO.6ebc88e4f144.js} +1 -1
- codex/static_root/assets/VRadioGroup-C1WCG0rO.6ebc88e4f144.js.br +3 -0
- codex/static_root/assets/VRadioGroup-C1WCG0rO.6ebc88e4f144.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-DoayJpcS.js → VRadioGroup-C1WCG0rO.js} +1 -1
- codex/static_root/assets/VRadioGroup-C1WCG0rO.js.br +3 -0
- codex/static_root/assets/VRadioGroup-C1WCG0rO.js.gz +0 -0
- codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js +1 -0
- codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js.br +0 -0
- codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js.gz +0 -0
- codex/static_root/assets/VSelect-Bbt1vrBg.js +1 -0
- codex/static_root/assets/VSelect-Bbt1vrBg.js.br +0 -0
- codex/static_root/assets/VSelect-Bbt1vrBg.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-TaKCeZgb.ad6c96efe57c.js → VSelectionControl-BkZZAsWo.ba6eca944e2e.js} +1 -1
- codex/static_root/assets/VSelectionControl-BkZZAsWo.ba6eca944e2e.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BkZZAsWo.ba6eca944e2e.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-TaKCeZgb.js → VSelectionControl-BkZZAsWo.js} +1 -1
- codex/static_root/assets/VSelectionControl-BkZZAsWo.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BkZZAsWo.js.gz +0 -0
- codex/static_root/assets/{VTable-BfcOiEpa.77ef3973bb54.js → VTable-D7P2eIc2.5e94bf2a515b.js} +1 -1
- codex/static_root/assets/VTable-D7P2eIc2.5e94bf2a515b.js.br +0 -0
- codex/static_root/assets/VTable-D7P2eIc2.5e94bf2a515b.js.gz +0 -0
- codex/static_root/assets/{VTable-BfcOiEpa.js → VTable-D7P2eIc2.js} +1 -1
- codex/static_root/assets/VTable-D7P2eIc2.js.br +0 -0
- codex/static_root/assets/VTable-D7P2eIc2.js.gz +0 -0
- codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js +1 -0
- codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js.br +0 -0
- codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js.gz +0 -0
- codex/static_root/assets/VWindowItem-Dm2szrjK.js +1 -0
- codex/static_root/assets/VWindowItem-Dm2szrjK.js.br +0 -0
- codex/static_root/assets/VWindowItem-Dm2szrjK.js.gz +0 -0
- codex/static_root/assets/{admin-80eqCqtz.d6deb9edb8cb.js → admin-B4z2nA5P.da0c92500311.js} +1 -1
- codex/static_root/assets/admin-B4z2nA5P.da0c92500311.js.br +0 -0
- codex/static_root/assets/admin-B4z2nA5P.da0c92500311.js.gz +0 -0
- codex/static_root/assets/{admin-80eqCqtz.js → admin-B4z2nA5P.js} +1 -1
- codex/static_root/assets/admin-B4z2nA5P.js.br +0 -0
- codex/static_root/assets/admin-B4z2nA5P.js.gz +0 -0
- codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js +1 -0
- codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js.br +0 -0
- codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js.gz +0 -0
- codex/static_root/assets/admin-BhW3PNO0.js +1 -0
- codex/static_root/assets/admin-BhW3PNO0.js.br +0 -0
- codex/static_root/assets/admin-BhW3PNO0.js.gz +0 -0
- codex/static_root/assets/{admin-menu-_aAGknKO.0c08c9bd2e7d.js → admin-menu-CL7S-xqR.92b6d84c4b70.js} +1 -1
- codex/static_root/assets/admin-menu-CL7S-xqR.92b6d84c4b70.js.br +0 -0
- codex/static_root/assets/admin-menu-CL7S-xqR.92b6d84c4b70.js.gz +0 -0
- codex/static_root/assets/{admin-menu-_aAGknKO.js → admin-menu-CL7S-xqR.js} +1 -1
- codex/static_root/assets/admin-menu-CL7S-xqR.js.br +0 -0
- codex/static_root/assets/admin-menu-CL7S-xqR.js.gz +0 -0
- codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js → admin-settings-button-progress-BgBgdELw.52d052e38bc1.js} +1 -1
- codex/static_root/assets/admin-settings-button-progress-BgBgdELw.52d052e38bc1.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BgBgdELw.52d052e38bc1.js.gz +0 -0
- codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.js → admin-settings-button-progress-BgBgdELw.js} +1 -1
- codex/static_root/assets/admin-settings-button-progress-BgBgdELw.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BgBgdELw.js.gz +0 -0
- codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js +1 -0
- codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js.br +0 -0
- codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js.gz +0 -0
- codex/static_root/assets/browser-Bi5ov5k8.js +1 -0
- codex/static_root/assets/browser-Bi5ov5k8.js.br +0 -0
- codex/static_root/assets/browser-Bi5ov5k8.js.gz +0 -0
- codex/static_root/assets/{browser-chTZ1CM4.521193ab0710.css → browser-DNzQvgkY.aea8b7d7ca40.css} +1 -1
- codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.br +0 -0
- codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.gz +0 -0
- codex/static_root/assets/{browser-chTZ1CM4.css → browser-DNzQvgkY.css} +1 -1
- codex/static_root/assets/browser-DNzQvgkY.css.br +0 -0
- codex/static_root/assets/browser-DNzQvgkY.css.gz +0 -0
- codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js → change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js} +1 -1
- codex/static_root/assets/change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.js → change-password-dialog-BKar2Bhb.js} +1 -1
- codex/static_root/assets/change-password-dialog-BKar2Bhb.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BKar2Bhb.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-DqCey9Iq.e315d7f8f752.js → confirm-dialog-BoBL4OoS.abef9614fea9.js} +1 -1
- codex/static_root/assets/confirm-dialog-BoBL4OoS.abef9614fea9.js.br +0 -0
- codex/static_root/assets/confirm-dialog-BoBL4OoS.abef9614fea9.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-DqCey9Iq.js → confirm-dialog-BoBL4OoS.js} +1 -1
- codex/static_root/assets/confirm-dialog-BoBL4OoS.js.br +0 -0
- codex/static_root/assets/confirm-dialog-BoBL4OoS.js.gz +0 -0
- codex/static_root/assets/{datetime-column-CBA8bYeO.17a31f189d66.js → datetime-column-BsiYRUKO.cadd764c06f4.js} +1 -1
- codex/static_root/assets/datetime-column-BsiYRUKO.cadd764c06f4.js.br +0 -0
- codex/static_root/assets/datetime-column-BsiYRUKO.cadd764c06f4.js.gz +0 -0
- codex/static_root/assets/{datetime-column-CBA8bYeO.js → datetime-column-BsiYRUKO.js} +1 -1
- codex/static_root/assets/datetime-column-BsiYRUKO.js.br +0 -0
- codex/static_root/assets/datetime-column-BsiYRUKO.js.gz +0 -0
- codex/static_root/assets/{filter-mnO3lLPo.659442401b16.js → filter-HnY0knto.07f807227dcc.js} +1 -1
- codex/static_root/assets/filter-HnY0knto.07f807227dcc.js.br +0 -0
- codex/static_root/assets/filter-HnY0knto.07f807227dcc.js.gz +0 -0
- codex/static_root/assets/{filter-mnO3lLPo.js → filter-HnY0knto.js} +1 -1
- codex/static_root/assets/filter-HnY0knto.js.br +0 -0
- codex/static_root/assets/filter-HnY0knto.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Bc677pNb.1b8a2f7c0dc3.js → flag-tab-CcS5pve2.a1df5d92e222.js} +1 -1
- codex/static_root/assets/flag-tab-CcS5pve2.a1df5d92e222.js.br +0 -0
- codex/static_root/assets/flag-tab-CcS5pve2.a1df5d92e222.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Bc677pNb.js → flag-tab-CcS5pve2.js} +1 -1
- codex/static_root/assets/flag-tab-CcS5pve2.js.br +0 -0
- codex/static_root/assets/flag-tab-CcS5pve2.js.gz +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css +1 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.br +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.gz +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.css +1 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.css.br +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.css.gz +0 -0
- codex/static_root/assets/{forwardRefs-DPRKg5wq.6f09e9d22fa6.js → forwardRefs-i80xmxX3.db393aeef392.js} +1 -1
- codex/static_root/assets/forwardRefs-i80xmxX3.db393aeef392.js.br +0 -0
- codex/static_root/assets/forwardRefs-i80xmxX3.db393aeef392.js.gz +0 -0
- codex/static_root/assets/{forwardRefs-DPRKg5wq.js → forwardRefs-i80xmxX3.js} +1 -1
- codex/static_root/assets/forwardRefs-i80xmxX3.js.br +0 -0
- codex/static_root/assets/forwardRefs-i80xmxX3.js.gz +0 -0
- codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js +1 -0
- codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js.br +0 -0
- codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js.gz +0 -0
- codex/static_root/assets/group-tab-SY0gxmHW.js +1 -0
- codex/static_root/assets/group-tab-SY0gxmHW.js.br +0 -0
- codex/static_root/assets/group-tab-SY0gxmHW.js.gz +0 -0
- codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js +1 -0
- codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js.br +0 -0
- codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js.gz +0 -0
- codex/static_root/assets/http-error-EE8gtq5A.js +1 -0
- codex/static_root/assets/http-error-EE8gtq5A.js.br +0 -0
- codex/static_root/assets/http-error-EE8gtq5A.js.gz +0 -0
- codex/static_root/assets/{index-9nBCksDQ.4561d2608773.js → index-gVdawuuE.a940fadbac00.js} +1 -1
- codex/static_root/assets/index-gVdawuuE.a940fadbac00.js.br +0 -0
- codex/static_root/assets/index-gVdawuuE.a940fadbac00.js.gz +0 -0
- codex/static_root/assets/{index-9nBCksDQ.js → index-gVdawuuE.js} +1 -1
- codex/static_root/assets/index-gVdawuuE.js.br +0 -0
- codex/static_root/assets/index-gVdawuuE.js.gz +0 -0
- codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js +1 -0
- codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js.br +0 -0
- codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js.gz +0 -0
- codex/static_root/assets/library-tab-BkDIAvyS.js +1 -0
- codex/static_root/assets/library-tab-BkDIAvyS.js.br +0 -0
- codex/static_root/assets/library-tab-BkDIAvyS.js.gz +0 -0
- codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js +35 -0
- codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js.br +0 -0
- codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js.gz +0 -0
- codex/static_root/assets/main-B5uflU6E.js +35 -0
- codex/static_root/assets/main-B5uflU6E.js.br +0 -0
- codex/static_root/assets/main-B5uflU6E.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js +1 -0
- codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-wuQbceLs.js +1 -0
- codex/static_root/assets/pager-full-pdf-wuQbceLs.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-wuQbceLs.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js → pagination-toolbar-D3JQmDiD.8513a4db85f0.js} +1 -1
- codex/static_root/assets/pagination-toolbar-D3JQmDiD.8513a4db85f0.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-D3JQmDiD.8513a4db85f0.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.js → pagination-toolbar-D3JQmDiD.js} +1 -1
- codex/static_root/assets/pagination-toolbar-D3JQmDiD.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-D3JQmDiD.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-6ZE_HmLT.10d1e16abc78.js → pdf-doc-DRBtl9_2.225a6d89e842.js} +1 -1
- codex/static_root/assets/pdf-doc-DRBtl9_2.225a6d89e842.js.br +0 -0
- codex/static_root/assets/pdf-doc-DRBtl9_2.225a6d89e842.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-6ZE_HmLT.js → pdf-doc-DRBtl9_2.js} +1 -1
- codex/static_root/assets/pdf-doc-DRBtl9_2.js.br +0 -0
- codex/static_root/assets/pdf-doc-DRBtl9_2.js.gz +0 -0
- codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js +2 -0
- codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js.br +0 -0
- codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js.gz +0 -0
- codex/static_root/assets/reader-DycvjN42.js +2 -0
- codex/static_root/assets/reader-DycvjN42.js.br +0 -0
- codex/static_root/assets/reader-DycvjN42.js.gz +0 -0
- codex/static_root/assets/{relation-chips-C96hi5jm.js → relation-chips-CeHIEjvZ.04d302f4fa89.js} +1 -1
- codex/static_root/assets/relation-chips-CeHIEjvZ.04d302f4fa89.js.br +0 -0
- codex/static_root/assets/relation-chips-CeHIEjvZ.04d302f4fa89.js.gz +0 -0
- codex/static_root/assets/{relation-chips-C96hi5jm.9f07c9eb533a.js → relation-chips-CeHIEjvZ.js} +1 -1
- codex/static_root/assets/relation-chips-CeHIEjvZ.js.br +0 -0
- codex/static_root/assets/relation-chips-CeHIEjvZ.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js +2 -0
- codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js.br +0 -0
- codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DY8o-jF3.js +2 -0
- codex/static_root/assets/settings-drawer-DY8o-jF3.js.br +0 -0
- codex/static_root/assets/settings-drawer-DY8o-jF3.js.gz +0 -0
- codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js +1 -0
- codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js.br +0 -0
- codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js.gz +0 -0
- codex/static_root/assets/ssrBoot-DRKt_yWP.js +1 -0
- codex/static_root/assets/ssrBoot-DRKt_yWP.js.br +0 -0
- codex/static_root/assets/ssrBoot-DRKt_yWP.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CkT2ZGUQ.b704fa46dc88.js → stats-tab-C1XYGBKj.812896e1d09c.js} +1 -1
- codex/static_root/assets/stats-tab-C1XYGBKj.812896e1d09c.js.br +0 -0
- codex/static_root/assets/stats-tab-C1XYGBKj.812896e1d09c.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CkT2ZGUQ.js → stats-tab-C1XYGBKj.js} +1 -1
- codex/static_root/assets/stats-tab-C1XYGBKj.js.br +0 -0
- codex/static_root/assets/stats-tab-C1XYGBKj.js.gz +0 -0
- codex/static_root/assets/task-tab-CW4DQ3KN.81812f9208e5.css +1 -0
- codex/static_root/assets/{task-tab-DfdUY9Rc.478e74041f80.css.br → task-tab-CW4DQ3KN.81812f9208e5.css.br} +1 -1
- codex/static_root/assets/task-tab-CW4DQ3KN.81812f9208e5.css.gz +0 -0
- codex/static_root/assets/task-tab-CW4DQ3KN.css +1 -0
- codex/static_root/assets/{task-tab-DfdUY9Rc.css.br → task-tab-CW4DQ3KN.css.br} +1 -1
- codex/static_root/assets/task-tab-CW4DQ3KN.css.gz +0 -0
- codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js +1 -0
- codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js.br +0 -0
- codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js.gz +0 -0
- codex/static_root/assets/task-tab-lFRZ0NMd.js +1 -0
- codex/static_root/assets/task-tab-lFRZ0NMd.js.br +0 -0
- codex/static_root/assets/task-tab-lFRZ0NMd.js.gz +0 -0
- codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js +1 -0
- codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js.br +0 -0
- codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js.gz +0 -0
- codex/static_root/assets/unauthorized-Dld9cV8d.js +1 -0
- codex/static_root/assets/unauthorized-Dld9cV8d.js.br +0 -0
- codex/static_root/assets/unauthorized-Dld9cV8d.js.gz +0 -0
- codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js +1 -0
- codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js.br +0 -0
- codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js.gz +0 -0
- codex/static_root/assets/user-tab-CN9nBOMS.js +1 -0
- codex/static_root/assets/user-tab-CN9nBOMS.js.br +0 -0
- codex/static_root/assets/user-tab-CN9nBOMS.js.gz +0 -0
- codex/static_root/{manifest.225989a17f49.json → manifest.66d7229aa07a.json} +297 -277
- codex/static_root/manifest.66d7229aa07a.json.br +0 -0
- codex/static_root/manifest.66d7229aa07a.json.gz +0 -0
- codex/static_root/manifest.json +297 -277
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/status_controller.py +10 -13
- codex/urls/root.py +4 -3
- codex/views/admin/api_key.py +1 -4
- codex/views/admin/flag.py +2 -2
- codex/views/admin/group.py +2 -2
- codex/views/admin/library.py +4 -4
- codex/views/admin/stats.py +10 -10
- codex/views/admin/tasks.py +32 -13
- codex/views/admin/user.py +36 -10
- codex/views/auth.py +3 -8
- codex/views/bookmark.py +3 -3
- codex/views/browser/annotate/card.py +2 -2
- codex/views/browser/annotate/order.py +6 -7
- codex/views/browser/bookmark.py +3 -1
- codex/views/browser/breadcrumbs.py +6 -5
- codex/views/browser/browser.py +2 -2
- codex/views/browser/choices.py +7 -8
- codex/views/browser/cover.py +6 -3
- codex/views/browser/download.py +2 -1
- codex/views/browser/filters/bookmark.py +1 -1
- codex/views/browser/filters/field.py +1 -1
- codex/views/browser/filters/filter.py +6 -8
- codex/views/browser/filters/group.py +8 -8
- codex/views/browser/filters/search/field/expression.py +1 -2
- codex/views/browser/filters/search/field/optimize.py +1 -1
- codex/views/browser/filters/search/field/parse.py +2 -2
- codex/views/browser/filters/search/parse.py +4 -19
- codex/views/browser/group_mtime.py +1 -1
- codex/views/browser/metadata/copy_intersections.py +1 -1
- codex/views/browser/metadata/metadata.py +1 -1
- codex/views/browser/mtime.py +1 -1
- codex/views/browser/order_by.py +3 -5
- codex/views/browser/params.py +1 -1
- codex/views/browser/settings.py +1 -1
- codex/views/browser/validate.py +9 -6
- codex/views/const.py +0 -3
- codex/views/frontend.py +1 -1
- codex/views/mixins.py +3 -2
- codex/views/opds/authentication_v1.py +1 -1
- codex/views/opds/const.py +4 -4
- codex/views/opds/urls.py +1 -1
- codex/views/opds/util.py +4 -4
- codex/views/opds/v1/entry/entry.py +4 -2
- codex/views/opds/v1/entry/links.py +4 -5
- codex/views/opds/v1/facets.py +8 -4
- codex/views/opds/v1/feed.py +5 -5
- codex/views/opds/v1/links.py +11 -6
- codex/views/opds/v2/const.py +5 -2
- codex/views/opds/v2/feed.py +3 -3
- codex/views/opds/v2/links.py +18 -11
- codex/views/opds/v2/progression.py +8 -9
- codex/views/opds/v2/publications.py +10 -20
- codex/views/public.py +2 -2
- codex/views/reader/books.py +3 -2
- codex/views/reader/page.py +3 -3
- codex/views/reader/params.py +2 -2
- codex/views/reader/reader.py +6 -6
- codex/views/reader/settings.py +1 -1
- codex/views/session.py +10 -3
- codex/views/settings.py +2 -2
- codex/views/timezone.py +2 -2
- codex/views/util.py +10 -9
- codex/views/version.py +1 -1
- {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/METADATA +3 -1
- {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/RECORD +433 -415
- {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/WHEEL +1 -1
- codex/serializers/fields.py +0 -183
- codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.br +0 -0
- codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.gz +0 -0
- codex/static_root/assets/VCheckbox-FaT6MGfu.js.br +0 -0
- codex/static_root/assets/VCheckbox-FaT6MGfu.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.gz +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.br +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.gz +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.js.br +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.js.gz +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.br +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.gz +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.js.br +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.js.gz +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.br +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.gz +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.js.br +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.js.gz +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js +0 -1
- codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.br +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.gz +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.js +0 -1
- codex/static_root/assets/VForm-DNmY-qFJ.js.br +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.js.gz +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js +0 -1
- codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.br +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.gz +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.js +0 -1
- codex/static_root/assets/VSelect-BSLmHX_P.js.br +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.js.br +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.js.gz +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.br +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.gz +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.js.br +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js +0 -1
- codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.br +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.js +0 -1
- codex/static_root/assets/VWindowItem-D8lWcbQY.js.br +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.js.gz +0 -0
- codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.br +0 -0
- codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.gz +0 -0
- codex/static_root/assets/admin-80eqCqtz.js.br +0 -0
- codex/static_root/assets/admin-80eqCqtz.js.gz +0 -0
- codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js +0 -1
- codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.br +0 -0
- codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.gz +0 -0
- codex/static_root/assets/admin-CBLHOrM9.js +0 -1
- codex/static_root/assets/admin-CBLHOrM9.js.br +0 -0
- codex/static_root/assets/admin-CBLHOrM9.js.gz +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.br +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.gz +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.js.br +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.gz +0 -0
- codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js +0 -1
- codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.br +0 -0
- codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.gz +0 -0
- codex/static_root/assets/browser-DAprXYuP.js +0 -1
- codex/static_root/assets/browser-DAprXYuP.js.br +0 -0
- codex/static_root/assets/browser-DAprXYuP.js.gz +0 -0
- codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.br +0 -0
- codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.gz +0 -0
- codex/static_root/assets/browser-chTZ1CM4.css.br +0 -0
- codex/static_root/assets/browser-chTZ1CM4.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.br +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.br +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.js.gz +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.br +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.gz +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.js.br +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.js.gz +0 -0
- codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.br +0 -0
- codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.gz +0 -0
- codex/static_root/assets/filter-mnO3lLPo.js.br +0 -0
- codex/static_root/assets/filter-mnO3lLPo.js.gz +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.br +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.gz +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.js.br +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.js.gz +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css +0 -1
- codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.br +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.gz +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.css +0 -1
- codex/static_root/assets/flag-tab-glFvEgEl.css.br +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.css.gz +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.br +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.gz +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.js.br +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.js.gz +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js +0 -1
- codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.br +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.gz +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.js +0 -1
- codex/static_root/assets/group-tab-BVlBrMwo.js.br +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.js.gz +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js +0 -1
- codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.br +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.gz +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.js +0 -1
- codex/static_root/assets/http-error-C9-_aEBF.js.br +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.js.gz +0 -0
- codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.br +0 -0
- codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.gz +0 -0
- codex/static_root/assets/index-9nBCksDQ.js.br +0 -0
- codex/static_root/assets/index-9nBCksDQ.js.gz +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js +0 -1
- codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.br +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.gz +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.js +0 -1
- codex/static_root/assets/library-tab-BMe3dKzy.js.br +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.js.gz +0 -0
- codex/static_root/assets/main-BbNUiWEb.565b616eb122.js +0 -35
- codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.br +0 -0
- codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.gz +0 -0
- codex/static_root/assets/main-BbNUiWEb.js +0 -35
- codex/static_root/assets/main-BbNUiWEb.js.br +0 -0
- codex/static_root/assets/main-BbNUiWEb.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js +0 -1
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.js +0 -1
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.gz +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.br +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.gz +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.js.br +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.js.gz +0 -0
- codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js +0 -2
- codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.br +0 -0
- codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.gz +0 -0
- codex/static_root/assets/reader-DdPKDuh5.js +0 -2
- codex/static_root/assets/reader-DdPKDuh5.js.br +0 -0
- codex/static_root/assets/reader-DdPKDuh5.js.gz +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.br +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.gz +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.js.br +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js +0 -2
- codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.br +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.js +0 -2
- codex/static_root/assets/settings-drawer-CqbT1rid.js.br +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.js.gz +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.br +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.gz +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.js.br +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.js.gz +0 -0
- codex/static_root/assets/task-tab-DfdUY9Rc.478e74041f80.css +0 -1
- codex/static_root/assets/task-tab-DfdUY9Rc.478e74041f80.css.gz +0 -0
- codex/static_root/assets/task-tab-DfdUY9Rc.css +0 -1
- codex/static_root/assets/task-tab-DfdUY9Rc.css.gz +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js +0 -1
- codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.br +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.gz +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.js +0 -1
- codex/static_root/assets/task-tab-a-DcU3Tj.js.br +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.js.gz +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js +0 -1
- codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.br +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.gz +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.js +0 -1
- codex/static_root/assets/unauthorized-BOl5YpL3.js.br +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.js.gz +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js +0 -1
- codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.br +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.gz +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.js +0 -1
- codex/static_root/assets/user-tab-BBDZvD8G.js.br +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.js.gz +0 -0
- codex/static_root/manifest.225989a17f49.json.br +0 -0
- codex/static_root/manifest.225989a17f49.json.gz +0 -0
- {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/LICENSE +0 -0
- {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/entry_points.txt +0 -0
|
@@ -186,7 +186,8 @@ class LinkComicsImporter(LinkCoversImporter):
|
|
|
186
186
|
return tms, all_del_pks
|
|
187
187
|
|
|
188
188
|
def bulk_fix_comic_m2m_field(self, field_name, m2m_links, status):
|
|
189
|
-
"""
|
|
189
|
+
"""
|
|
190
|
+
Recreate an m2m field for a set of comics.
|
|
190
191
|
|
|
191
192
|
Since we can't bulk_update or bulk_create m2m fields use a trick.
|
|
192
193
|
bulk_create() on the through table:
|
|
@@ -46,9 +46,7 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
46
46
|
new_path = self.task.files_moved[comic.path]
|
|
47
47
|
comic.path = new_path
|
|
48
48
|
new_path = Path(new_path)
|
|
49
|
-
comic.parent_folder = Folder.objects.get(
|
|
50
|
-
path=new_path.parent
|
|
51
|
-
)
|
|
49
|
+
comic.parent_folder = Folder.objects.get(path=new_path.parent)
|
|
52
50
|
comic.updated_at = Now()
|
|
53
51
|
comic.presave()
|
|
54
52
|
folder_m2m_links[comic.pk] = Folder.objects.filter(
|
|
@@ -137,7 +135,7 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
137
135
|
f"Unlinked {len(unlink_groups)} {model.__name__} moved custom covers."
|
|
138
136
|
)
|
|
139
137
|
|
|
140
|
-
self._remove_covers(unlink_pks, custom=True)
|
|
138
|
+
self._remove_covers(unlink_pks, custom=True)
|
|
141
139
|
|
|
142
140
|
def _bulk_covers_moved(self, status=None):
|
|
143
141
|
"""Move covers."""
|
|
@@ -194,8 +192,6 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
194
192
|
folder.updated_at = Now()
|
|
195
193
|
update_folders.append(folder)
|
|
196
194
|
|
|
197
|
-
# delete_folders = Folder.objects.filter(library=self.library, path__in=new_paths)
|
|
198
|
-
|
|
199
195
|
update_folders = sorted(update_folders, key=lambda x: len(Path(x.path).parts))
|
|
200
196
|
|
|
201
197
|
Folder.objects.bulk_update(update_folders, MOVED_BULK_FOLDER_UPDATE_FIELDS)
|
|
@@ -212,7 +208,6 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
212
208
|
"""Move folders under existing folders."""
|
|
213
209
|
while True:
|
|
214
210
|
# Get existing parent folders
|
|
215
|
-
# dest_parent_paths = tuple(str(path) for path in dest_parent_folder_paths_map)
|
|
216
211
|
dest_parent_paths = tuple(dest_parent_folder_paths_map.keys())
|
|
217
212
|
extant_parent_folders = Folder.objects.filter(
|
|
218
213
|
library=self.library, path__in=dest_parent_paths
|
|
@@ -229,9 +229,7 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
|
|
|
229
229
|
|
|
230
230
|
group_filter = Q()
|
|
231
231
|
for group_tree, count_value in update_group_trees.items():
|
|
232
|
-
compare_filter =
|
|
233
|
-
for field_name, value in zip(compare_fields, group_tree, strict=False):
|
|
234
|
-
compare_filter[field_name] = value
|
|
232
|
+
compare_filter = dict(zip(compare_fields, group_tree, strict=False))
|
|
235
233
|
compare_filter[count_field_name] = count_value
|
|
236
234
|
group_filter |= Q(**compare_filter)
|
|
237
235
|
return group_filter
|
|
@@ -494,7 +492,6 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
|
|
|
494
492
|
|
|
495
493
|
def _query_missing_simple_models(self, names, fk_data, status):
|
|
496
494
|
"""Find missing named models and folders."""
|
|
497
|
-
# count = 0
|
|
498
495
|
if not names:
|
|
499
496
|
return 0
|
|
500
497
|
create_fks, base_cls, field, fk_field = fk_data
|
|
@@ -18,7 +18,7 @@ class ImportDBDiffTask(ImportTask):
|
|
|
18
18
|
|
|
19
19
|
dirs_moved: Mapping[str, str] = field(default_factory=dict)
|
|
20
20
|
dirs_modified: frozenset[str] = frozenset()
|
|
21
|
-
# dirs_created: frozenset[str] | None = frozenset()
|
|
21
|
+
# dirs_created: frozenset[str] | None = frozenset() # noqa: ERA001
|
|
22
22
|
dirs_deleted: frozenset[str] = frozenset()
|
|
23
23
|
|
|
24
24
|
files_moved: Mapping[str, str] = field(default_factory=dict)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Uses app.get_model() because functions may also be called before the models are ready on startup.
|
|
3
3
|
|
|
4
4
|
import re
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
6
7
|
from django.apps import apps
|
|
7
8
|
from django.db import DEFAULT_DB_ALIAS, connections
|
|
@@ -19,6 +20,13 @@ from codex.settings.settings import (
|
|
|
19
20
|
from codex.status import Status
|
|
20
21
|
from codex.worker_base import WorkerBaseMixin
|
|
21
22
|
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from django.db.models.manager import BaseManager
|
|
25
|
+
|
|
26
|
+
from codex.models.base import BaseModel
|
|
27
|
+
from codex.models.comic import Comic
|
|
28
|
+
|
|
29
|
+
|
|
22
30
|
REPAIR_FLAG_PATH = CONFIG_PATH / "rebuild_db"
|
|
23
31
|
REBUILT_DB_PATH = DB_PATH.parent / (DB_PATH.name + ".rebuilt")
|
|
24
32
|
BACKUP_DB_PATH = DB_PATH.parent / (DB_PATH.name + ".bak")
|
|
@@ -105,7 +113,7 @@ def _null_bad_fk_rels_table(table_name, bad_rows, log):
|
|
|
105
113
|
fields = model._meta.fields
|
|
106
114
|
field_names = {}
|
|
107
115
|
update_objs = []
|
|
108
|
-
objs = model.objects.filter(pk__in=bad_rows.keys())
|
|
116
|
+
objs: BaseManager[BaseModel] = model.objects.filter(pk__in=bad_rows.keys()) # type: ignore[reportAssignmentType]
|
|
109
117
|
update_fields = {"updated_at"}
|
|
110
118
|
now = Now()
|
|
111
119
|
for obj in objs:
|
|
@@ -119,7 +127,7 @@ def _null_bad_fk_rels_table(table_name, bad_rows, log):
|
|
|
119
127
|
field_name = field_names[fkid]
|
|
120
128
|
setattr(obj, field_name, None)
|
|
121
129
|
update_fields.add(field_name)
|
|
122
|
-
obj.updated_at = now
|
|
130
|
+
obj.updated_at = now
|
|
123
131
|
update_objs.append(obj)
|
|
124
132
|
except Exception as exc:
|
|
125
133
|
log.warning(
|
|
@@ -142,10 +150,10 @@ def _null_bad_fk_rels(bad_fk_rels, log):
|
|
|
142
150
|
for table_name, bad_rows in bad_fk_rels.items():
|
|
143
151
|
try:
|
|
144
152
|
fix_comic_pks |= _null_bad_fk_rels_table(table_name, bad_rows, log)
|
|
145
|
-
except Exception
|
|
153
|
+
except Exception:
|
|
146
154
|
pks = sorted(bad_rows.keys())
|
|
147
|
-
log.
|
|
148
|
-
f"Unable to null {len(pks)} {table_name} rows with bad foreign keys
|
|
155
|
+
log.exception(
|
|
156
|
+
f"Unable to null {len(pks)} {table_name} rows with bad foreign keys"
|
|
149
157
|
)
|
|
150
158
|
log.error(f"{table_name}: {pks}") # noqa: TRY400
|
|
151
159
|
return fix_comic_pks
|
|
@@ -175,10 +183,8 @@ def _delete_bad_m2m_rows(bad_m2m_rows, log):
|
|
|
175
183
|
for table_name, ids in bad_m2m_rows.items():
|
|
176
184
|
try:
|
|
177
185
|
count += _delete_bad_m2m_rows_table(table_name, ids, fix_comic_pks, log)
|
|
178
|
-
except Exception
|
|
179
|
-
log.
|
|
180
|
-
f"Could not delete bad foreign keys in table {table_name} - {exc}"
|
|
181
|
-
)
|
|
186
|
+
except Exception:
|
|
187
|
+
log.exception(f"Could not delete bad foreign keys in table {table_name}")
|
|
182
188
|
if count:
|
|
183
189
|
log.info(f"Removed {count} bad foreign key relations.")
|
|
184
190
|
return fix_comic_pks
|
|
@@ -189,7 +195,9 @@ def _mark_comics_for_update(fix_comic_pks, log):
|
|
|
189
195
|
if not fix_comic_pks:
|
|
190
196
|
return
|
|
191
197
|
comic_model = apps.get_model(app_label="codex", model_name="comic")
|
|
192
|
-
outdated_comics = comic_model.objects.filter(
|
|
198
|
+
outdated_comics: BaseManager[Comic] = comic_model.objects.filter(
|
|
199
|
+
pk__in=fix_comic_pks
|
|
200
|
+
).only( # type: ignore[reportAssignmentType]
|
|
193
201
|
"stat", "updated_at"
|
|
194
202
|
)
|
|
195
203
|
if not outdated_comics:
|
|
@@ -198,12 +206,12 @@ def _mark_comics_for_update(fix_comic_pks, log):
|
|
|
198
206
|
update_comics = []
|
|
199
207
|
now = Now()
|
|
200
208
|
for comic in outdated_comics:
|
|
201
|
-
stat_list = comic.stat
|
|
209
|
+
stat_list = comic.stat
|
|
202
210
|
if not stat_list:
|
|
203
211
|
continue
|
|
204
212
|
stat_list[8] = 0.0
|
|
205
|
-
comic.stat = stat_list # type: ignore
|
|
206
|
-
comic.updated_at = now
|
|
213
|
+
comic.stat = stat_list # type: ignore[reportAttributeAccessIssue]
|
|
214
|
+
comic.updated_at = now
|
|
207
215
|
update_comics.append(comic)
|
|
208
216
|
|
|
209
217
|
if update_comics:
|
|
@@ -227,8 +235,8 @@ def fix_foreign_keys(log=None):
|
|
|
227
235
|
fix_comic_pks |= _delete_bad_m2m_rows(bad_m2m_rows, log)
|
|
228
236
|
try:
|
|
229
237
|
_mark_comics_for_update(fix_comic_pks, log)
|
|
230
|
-
except Exception
|
|
231
|
-
LOG.
|
|
238
|
+
except Exception:
|
|
239
|
+
LOG.exception("Could not mark comics with bad relations for update")
|
|
232
240
|
|
|
233
241
|
|
|
234
242
|
def _repair_extra_custom_cover_libraries(library_model, log):
|
|
@@ -277,7 +285,7 @@ def _is_integrity_ok(results):
|
|
|
277
285
|
)
|
|
278
286
|
|
|
279
287
|
|
|
280
|
-
def integrity_check(long
|
|
288
|
+
def integrity_check(long, log=None):
|
|
281
289
|
"""Run sqlite3 integrity check."""
|
|
282
290
|
if not log:
|
|
283
291
|
log = LOG
|
|
@@ -318,9 +326,8 @@ def fts_integrity_check(log=None):
|
|
|
318
326
|
if results:
|
|
319
327
|
# I'm not sure if this raises or puts the error in the results.
|
|
320
328
|
raise ValueError(results) # noqa: TRY301
|
|
321
|
-
except Exception
|
|
322
|
-
log.
|
|
323
|
-
log.warning(exc)
|
|
329
|
+
except Exception:
|
|
330
|
+
log.exception("Full Text Search Index failed integrity check.")
|
|
324
331
|
else:
|
|
325
332
|
log.info("Full Text Search Index passed integrity check.")
|
|
326
333
|
success = True
|
|
@@ -340,7 +347,7 @@ class IntegrityMixin(WorkerBaseMixin):
|
|
|
340
347
|
finally:
|
|
341
348
|
self.status_controller.finish(status)
|
|
342
349
|
|
|
343
|
-
def integrity_check(self, long
|
|
350
|
+
def integrity_check(self, long: bool):
|
|
344
351
|
"""Integrity check task."""
|
|
345
352
|
subtitle = "" if long else "Quick"
|
|
346
353
|
status = Status(JanitorStatusTypes.INTEGRITY_CHECK, subtitle=subtitle)
|
|
@@ -109,7 +109,7 @@ class Janitor(
|
|
|
109
109
|
case JanitorVacuumTask():
|
|
110
110
|
self.vacuum_db()
|
|
111
111
|
case JanitorBackupTask():
|
|
112
|
-
self.backup_db()
|
|
112
|
+
self.backup_db(show_status=True)
|
|
113
113
|
case JanitorLatestVersionTask():
|
|
114
114
|
self.update_latest_version(task.force)
|
|
115
115
|
case JanitorUpdateTask():
|
|
@@ -28,7 +28,7 @@ class LatestVersionMixin(WorkerBaseMixin):
|
|
|
28
28
|
response = requests.get(_REPO_URL, timeout=_REPO_TIMEOUT)
|
|
29
29
|
return json.loads(response.text)["info"]["version"]
|
|
30
30
|
|
|
31
|
-
def update_latest_version(self, force
|
|
31
|
+
def update_latest_version(self, force: bool):
|
|
32
32
|
"""Get the latest version from a remote repo using a cache."""
|
|
33
33
|
status = Status(JanitorStatusTypes.CODEX_LATEST_VERSION)
|
|
34
34
|
try:
|
|
@@ -22,12 +22,15 @@ class UpdateMixin(WorkerBaseMixin):
|
|
|
22
22
|
def _is_outdated(self):
|
|
23
23
|
"""Is codex outdated."""
|
|
24
24
|
result = False
|
|
25
|
+
if VERSION is None:
|
|
26
|
+
self.log.warning("Cannot determine installed Codex version.")
|
|
27
|
+
return result
|
|
25
28
|
ts = Timestamp.objects.get(key=Timestamp.TimestampChoices.CODEX_VERSION.value)
|
|
26
29
|
latest_version = ts.version
|
|
27
30
|
versio_latest_version = Version(latest_version)
|
|
28
31
|
|
|
29
32
|
installed_versio_version = Version(VERSION)
|
|
30
|
-
if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore
|
|
33
|
+
if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore[reportIndexIssue]
|
|
31
34
|
pre_blurb = "latest version is a prerelease. But installed version is not."
|
|
32
35
|
else:
|
|
33
36
|
result = versio_latest_version > installed_versio_version
|
|
@@ -35,7 +38,7 @@ class UpdateMixin(WorkerBaseMixin):
|
|
|
35
38
|
self.log.debug(f"{latest_version=} > {VERSION=} = {result}{pre_blurb}")
|
|
36
39
|
return result
|
|
37
40
|
|
|
38
|
-
def update_codex(self, force
|
|
41
|
+
def update_codex(self, force: bool):
|
|
39
42
|
"""Update the package and restart everything if the version changed."""
|
|
40
43
|
status = Status(JanitorStatusTypes.CODEX_UPDATE)
|
|
41
44
|
try:
|
|
@@ -30,7 +30,7 @@ class VacuumMixin(WorkerBaseMixin):
|
|
|
30
30
|
finally:
|
|
31
31
|
self.status_controller.finish(status)
|
|
32
32
|
|
|
33
|
-
def backup_db(self, backup_path=BACKUP_DB_PATH
|
|
33
|
+
def backup_db(self, show_status: bool, backup_path=BACKUP_DB_PATH):
|
|
34
34
|
"""Backup the database."""
|
|
35
35
|
status = Status(JanitorStatusTypes.DB_BACKUP) if show_status else ""
|
|
36
36
|
try:
|
codex/librarian/librariand.py
CHANGED
|
@@ -79,7 +79,7 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
|
|
|
79
79
|
startup_tasks = (
|
|
80
80
|
AdoptOrphanFoldersTask(),
|
|
81
81
|
WatchdogSyncTask(),
|
|
82
|
-
SearchIndexUpdateTask(False),
|
|
82
|
+
SearchIndexUpdateTask(rebuild=False),
|
|
83
83
|
)
|
|
84
84
|
|
|
85
85
|
for task in startup_tasks:
|
|
@@ -193,7 +193,8 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
|
|
|
193
193
|
self.log_queue.join_thread()
|
|
194
194
|
|
|
195
195
|
def run(self):
|
|
196
|
-
"""
|
|
196
|
+
"""
|
|
197
|
+
Process tasks from the queue.
|
|
197
198
|
|
|
198
199
|
This process also runs the crond thread and the Watchdog Observer
|
|
199
200
|
threads.
|
|
@@ -16,7 +16,8 @@ class NotifierThread(AggregateMessageQueuedThread):
|
|
|
16
16
|
self.cache[item.text] = item
|
|
17
17
|
|
|
18
18
|
def _send_task(self, task):
|
|
19
|
-
"""
|
|
19
|
+
"""
|
|
20
|
+
Send a group_send message to the mulitprocess broadcast channel.
|
|
20
21
|
|
|
21
22
|
A random consumer awaiting the broadcast channel will consume it,
|
|
22
23
|
and do a group_send with it's message.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
|
|
5
|
+
from codex.choices.notifications import Notifications
|
|
5
6
|
from codex.websockets.consumers import ChannelGroups
|
|
6
7
|
|
|
7
8
|
|
|
@@ -13,6 +14,19 @@ class NotifierTask:
|
|
|
13
14
|
group: str
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
ADMIN_FLAGS_CHANGED_TASK = NotifierTask(
|
|
18
|
+
Notifications.ADMIN_FLAGS.value, ChannelGroups.ALL.name
|
|
19
|
+
)
|
|
20
|
+
COVERS_CHANGED_TASK = NotifierTask(Notifications.COVERS.value, ChannelGroups.ALL.name)
|
|
21
|
+
FAILED_IMPORTS_CHANGED_TASK = NotifierTask(
|
|
22
|
+
Notifications.FAILED_IMPORTS.value, ChannelGroups.ADMIN.name
|
|
23
|
+
)
|
|
24
|
+
GROUPS_CHANGED_TASK = NotifierTask(Notifications.GROUPS.value, ChannelGroups.ALL.name)
|
|
25
|
+
LIBRARIAN_STATUS_TASK = NotifierTask(
|
|
26
|
+
Notifications.LIBRARIAN_STATUS.value, ChannelGroups.ADMIN.name
|
|
27
|
+
)
|
|
28
|
+
LIBRARY_CHANGED_TASK = NotifierTask(Notifications.LIBRARY.value, ChannelGroups.ALL.name)
|
|
29
|
+
USERS_CHANGED_TASK = NotifierTask(Notifications.USERS.value, ChannelGroups.ALL.name)
|
|
30
|
+
ADMIN_USERS_CHANGED_TASK = NotifierTask(
|
|
31
|
+
Notifications.USERS.value, ChannelGroups.ADMIN.name
|
|
32
|
+
)
|
|
@@ -22,7 +22,7 @@ class OptimizeMixin(QueuedThread):
|
|
|
22
22
|
self.abort_event = abort_event
|
|
23
23
|
super().__init__(*args, **kwargs)
|
|
24
24
|
|
|
25
|
-
def optimize(self, janitor
|
|
25
|
+
def optimize(self, janitor: bool):
|
|
26
26
|
"""Remove records not in the database from the index, trapping exceptions."""
|
|
27
27
|
start_time = time()
|
|
28
28
|
status = Status(SearchIndexStatusTypes.SEARCH_INDEX_OPTIMIZE)
|
codex/librarian/search/remove.py
CHANGED
|
@@ -21,7 +21,7 @@ class RemoveMixin(OptimizeMixin):
|
|
|
21
21
|
self.status_controller.finish(clear_status)
|
|
22
22
|
self.log.info("Old search index cleared.")
|
|
23
23
|
|
|
24
|
-
def _remove_stale_records(self, status):
|
|
24
|
+
def _remove_stale_records(self, status):
|
|
25
25
|
"""Remove records not in the database from the index."""
|
|
26
26
|
start_time = time()
|
|
27
27
|
delete_comicfts = ComicFTS.objects.filter(comic__isnull=True)
|
codex/librarian/search/update.py
CHANGED
|
@@ -14,7 +14,7 @@ from codex.librarian.search.status import SearchIndexStatusTypes
|
|
|
14
14
|
from codex.models import Comic, Library
|
|
15
15
|
from codex.models.comic import ComicFTS
|
|
16
16
|
from codex.models.functions import GroupConcat
|
|
17
|
-
from codex.serializers.fields import CountryField, LanguageField, PyCountryField
|
|
17
|
+
from codex.serializers.fields.browser import CountryField, LanguageField, PyCountryField
|
|
18
18
|
from codex.settings.settings import SEARCH_INDEX_BATCH_SIZE
|
|
19
19
|
from codex.status import Status
|
|
20
20
|
|
|
@@ -159,7 +159,7 @@ class FTSUpdateMixin(RemoveMixin):
|
|
|
159
159
|
obj_list.append(comicfts)
|
|
160
160
|
self.log.debug(f"{len(obj_list)}/{comics.count()} entries prepped.")
|
|
161
161
|
|
|
162
|
-
def _get_comicfts_list(self, comics, create
|
|
162
|
+
def _get_comicfts_list(self, comics, create: bool):
|
|
163
163
|
"""Create a ComicFTS object for bulk_create or bulk_update."""
|
|
164
164
|
country_field = CountryField()
|
|
165
165
|
language_field = LanguageField()
|
|
@@ -178,7 +178,7 @@ class FTSUpdateMixin(RemoveMixin):
|
|
|
178
178
|
self.log.debug(f"{verb} no search entries.")
|
|
179
179
|
self.status_controller.finish(status)
|
|
180
180
|
|
|
181
|
-
def _update_search_index_operate(self, comics_qs, create
|
|
181
|
+
def _update_search_index_operate(self, comics_qs, create: bool):
|
|
182
182
|
count = comics_qs.count()
|
|
183
183
|
verb = "create" if create else "update"
|
|
184
184
|
if not count:
|
|
@@ -285,7 +285,7 @@ class FTSUpdateMixin(RemoveMixin):
|
|
|
285
285
|
elapsed = naturaldelta(elapsed_time)
|
|
286
286
|
self.log.info(f"Search index updated in {elapsed}.")
|
|
287
287
|
|
|
288
|
-
def update_search_index(self, rebuild
|
|
288
|
+
def update_search_index(self, rebuild: bool):
|
|
289
289
|
"""Update or Rebuild the search index."""
|
|
290
290
|
start_time = time()
|
|
291
291
|
self.abort_event.clear()
|
|
@@ -62,11 +62,12 @@ class CodexStats:
|
|
|
62
62
|
request_model_set = self.params.get(key, {})
|
|
63
63
|
all_models = _KEY_MODELS_MAP[key]
|
|
64
64
|
if request_model_set:
|
|
65
|
-
models = [
|
|
66
|
-
|
|
67
|
-
for model in all_models
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
models = [
|
|
66
|
+
model
|
|
67
|
+
for model in all_models
|
|
68
|
+
for model_name in request_model_set
|
|
69
|
+
if model.__name__.lower() == model_name.lower()
|
|
70
|
+
]
|
|
70
71
|
else:
|
|
71
72
|
models = all_models
|
|
72
73
|
return tuple(models)
|
|
@@ -149,7 +150,7 @@ class CodexStats:
|
|
|
149
150
|
|
|
150
151
|
def _add_file_types(self, obj):
|
|
151
152
|
"""Query for file types."""
|
|
152
|
-
if self.params and "
|
|
153
|
+
if self.params and "file_types" not in self.params:
|
|
153
154
|
return
|
|
154
155
|
file_types = {}
|
|
155
156
|
qs = (
|
|
@@ -61,12 +61,12 @@ class CodexDatabaseSnapshot(DirectorySnapshot, LoggerBaseMixin):
|
|
|
61
61
|
def __init__(
|
|
62
62
|
self,
|
|
63
63
|
path,
|
|
64
|
-
_recursive=True, # unused, always recursive
|
|
64
|
+
_recursive=True, # noqa: FBT002 unused, always recursive
|
|
65
65
|
stat=os.stat,
|
|
66
66
|
_listdir=os.listdir, # unused for database
|
|
67
|
-
force=False,
|
|
67
|
+
force=False, # noqa: FBT002
|
|
68
68
|
log_queue=None,
|
|
69
|
-
covers_only=False,
|
|
69
|
+
covers_only=False, # noqa: FBT002
|
|
70
70
|
):
|
|
71
71
|
"""Initialize like DirectorySnapshot but use a database walk."""
|
|
72
72
|
self._covers_only = covers_only
|
|
@@ -33,7 +33,8 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
|
|
|
33
33
|
return self._get_inode(data.ref, path) == self._get_inode(data.snapshot, path)
|
|
34
34
|
|
|
35
35
|
def _is_stats_equal(self, data, old_path, new_path):
|
|
36
|
-
"""
|
|
36
|
+
"""
|
|
37
|
+
Return if the mtime and size are equal.
|
|
37
38
|
|
|
38
39
|
For old paths in the ref and new paths in the snapshot.
|
|
39
40
|
"""
|
|
@@ -84,9 +85,7 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
|
|
|
84
85
|
if not self._is_stats_equal(data, old_path, new_path):
|
|
85
86
|
data.modified.add(new_path)
|
|
86
87
|
|
|
87
|
-
def __init__(
|
|
88
|
-
self, ref, snapshot, ignore_device=False, inode_only_modified=False
|
|
89
|
-
):
|
|
88
|
+
def __init__(self, ref, snapshot, ignore_device: bool, inode_only_modified: bool):
|
|
90
89
|
"""Create diff object."""
|
|
91
90
|
self._ignore_device = ignore_device
|
|
92
91
|
self._inode_only_modified = inode_only_modified
|
|
@@ -46,14 +46,14 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
|
|
|
46
46
|
|
|
47
47
|
_DIR_NOT_FOUND_TIMEOUT = 15 * 60
|
|
48
48
|
|
|
49
|
-
def __init__( # noqa PLR0913
|
|
49
|
+
def __init__( # noqa: PLR0913
|
|
50
50
|
self,
|
|
51
51
|
event_queue,
|
|
52
52
|
watch,
|
|
53
53
|
timeout=DEFAULT_EMITTER_TIMEOUT,
|
|
54
54
|
log_queue=None,
|
|
55
55
|
librarian_queue=None,
|
|
56
|
-
covers_only=False,
|
|
56
|
+
covers_only=False, # noqa: FBT002
|
|
57
57
|
):
|
|
58
58
|
"""Initialize snapshot methods."""
|
|
59
59
|
self.init_worker(log_queue, librarian_queue)
|
|
@@ -72,7 +72,7 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
|
|
|
72
72
|
# default stat and listdir params
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
-
def poll(self, force
|
|
75
|
+
def poll(self, force: bool):
|
|
76
76
|
"""Poll now, sooner than timeout."""
|
|
77
77
|
self._force = force
|
|
78
78
|
with self._poll_cond:
|
|
@@ -118,7 +118,7 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
|
|
|
118
118
|
return ok
|
|
119
119
|
|
|
120
120
|
@property
|
|
121
|
-
def timeout(self) -> int | None: # type: ignore
|
|
121
|
+
def timeout(self) -> int | None: # type: ignore[reportIncompatibleMethodOverride]
|
|
122
122
|
"""Get the timeout for this emitter from its library."""
|
|
123
123
|
# The timeout from the constructor, self._timeout, is thrown away in favor
|
|
124
124
|
# of a dynamic timeout from the database.
|
|
@@ -198,8 +198,7 @@ class CodexLibraryEventHandler(CodexEventHandlerBase):
|
|
|
198
198
|
task = WatchdogEventTask(self.library_pk, sub_event)
|
|
199
199
|
self.librarian_queue.put(task)
|
|
200
200
|
|
|
201
|
-
#
|
|
202
|
-
# super().dispatch(event)
|
|
201
|
+
# Do not call super dispatch to call stub event dispatchers
|
|
203
202
|
except Exception:
|
|
204
203
|
self.log.exception(f"{self.__class__.__name__} dispatch")
|
|
205
204
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""The Codex Library Watchdog Observer threads."""
|
|
2
2
|
|
|
3
|
+
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
|
3
4
|
from watchdog.observers import Observer
|
|
4
5
|
from watchdog.observers.api import (
|
|
5
6
|
DEFAULT_OBSERVER_TIMEOUT,
|
|
@@ -98,14 +99,16 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
|
|
|
98
99
|
except Exception:
|
|
99
100
|
self.log.exception(f"{self.__class__.__name__} sync library watches")
|
|
100
101
|
|
|
101
|
-
def schedule(
|
|
102
|
+
def schedule(
|
|
102
103
|
self,
|
|
103
|
-
event_handler,
|
|
104
|
-
path,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
event_handler: FileSystemEventHandler,
|
|
105
|
+
path: str,
|
|
106
|
+
*,
|
|
107
|
+
recursive: bool = False,
|
|
108
|
+
event_filter: list[type[FileSystemEvent]] | None = None, # noqa: ARG002, F841, RUF100
|
|
109
|
+
) -> ObservedWatch:
|
|
110
|
+
"""
|
|
111
|
+
Override BaseObserver for Codex emitter class.
|
|
109
112
|
|
|
110
113
|
https://pythonhosted.org/watchdog/_modules/watchdog/observers/api.html#BaseObserver
|
|
111
114
|
"""
|
|
@@ -137,9 +140,11 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
|
|
|
137
140
|
# watchdog class structure doesn't work that way.
|
|
138
141
|
|
|
139
142
|
|
|
140
|
-
class LibraryEventObserver(UatuMixin, Observer): # type: ignore
|
|
143
|
+
class LibraryEventObserver(UatuMixin, Observer): # type: ignore[reportGeneralTypeIssues]
|
|
141
144
|
"""Regular observer."""
|
|
142
145
|
|
|
146
|
+
# Observer is a dynamically generated class by platform at runtime.
|
|
147
|
+
|
|
143
148
|
ENABLE_FIELD = "events"
|
|
144
149
|
|
|
145
150
|
|
|
@@ -156,7 +161,7 @@ class LibraryPollingObserver(UatuMixin):
|
|
|
156
161
|
*args, emitter_class=DatabasePollingEmitter, timeout=timeout, **kwargs
|
|
157
162
|
)
|
|
158
163
|
|
|
159
|
-
def poll(self, library_pks, force
|
|
164
|
+
def poll(self, library_pks, force: bool):
|
|
160
165
|
"""Poll each requested emitter."""
|
|
161
166
|
try:
|
|
162
167
|
qs = Library.objects.all()
|
|
@@ -165,7 +170,7 @@ class LibraryPollingObserver(UatuMixin):
|
|
|
165
170
|
paths = frozenset(qs.values_list("path", flat=True))
|
|
166
171
|
|
|
167
172
|
for emitter in self.emitters:
|
|
168
|
-
polling_emitter: DatabasePollingEmitter = emitter # type: ignore
|
|
173
|
+
polling_emitter: DatabasePollingEmitter = emitter # type: ignore[reportAssignmentType]
|
|
169
174
|
if emitter.watch.path in paths:
|
|
170
175
|
polling_emitter.poll(force)
|
|
171
176
|
except Exception:
|
codex/logger/logging.py
CHANGED
codex/memory.py
CHANGED
|
@@ -26,7 +26,7 @@ def get_cgroups1_mem_limit():
|
|
|
26
26
|
with Path(MEMORY_STAT_PATH).open("r") as mem_stat_file:
|
|
27
27
|
for line in mem_stat_file:
|
|
28
28
|
parts = line.split()
|
|
29
|
-
if not parts or len(parts) < 2: # noqa PLR2004
|
|
29
|
+
if not parts or len(parts) < 2: # noqa: PLR2004
|
|
30
30
|
continue
|
|
31
31
|
if "hierarchical_memory_limit" in parts[0]:
|
|
32
32
|
return int(parts[1])
|
|
@@ -34,7 +34,8 @@ def get_cgroups1_mem_limit():
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def get_mem_limit(divisor="b"):
|
|
37
|
-
"""
|
|
37
|
+
"""
|
|
38
|
+
Get the current memlimit.
|
|
38
39
|
|
|
39
40
|
If we're in a container set the limit too.
|
|
40
41
|
"""
|
codex/migrations/0001_init.py
CHANGED
|
@@ -632,8 +632,7 @@ class Migration(migrations.Migration):
|
|
|
632
632
|
default=None,
|
|
633
633
|
max_length=6,
|
|
634
634
|
null=True,
|
|
635
|
-
#
|
|
636
|
-
# validators=[codex.models.validate_fit_to_choice],
|
|
635
|
+
# Old code dependent validators removed in the future
|
|
637
636
|
),
|
|
638
637
|
),
|
|
639
638
|
("two_pages", models.BooleanField(default=None, null=True)),
|
|
@@ -151,8 +151,7 @@ class Migration(migrations.Migration):
|
|
|
151
151
|
field=models.CharField(
|
|
152
152
|
default="comic",
|
|
153
153
|
max_length=5,
|
|
154
|
-
#
|
|
155
|
-
# validators=[codex.models.validate_file_format_choice],
|
|
154
|
+
# codex dependent validators removed in the future
|
|
156
155
|
),
|
|
157
156
|
),
|
|
158
157
|
migrations.AddField(
|
|
@@ -28,8 +28,7 @@ class Migration(migrations.Migration):
|
|
|
28
28
|
blank=True,
|
|
29
29
|
default="",
|
|
30
30
|
max_length=6,
|
|
31
|
-
# removed in the future
|
|
32
|
-
# validators=[codex.models.validate_fit_to_choice],
|
|
31
|
+
# Code dependent validators removed in the future
|
|
33
32
|
),
|
|
34
33
|
),
|
|
35
34
|
migrations.AlterField(
|