codex 1.6.0__py3-none-any.whl → 1.6.0a1__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/exceptions.py +18 -22
- codex/integrity.py +3 -66
- codex/librarian/covers/coverd.py +2 -2
- codex/librarian/covers/create.py +29 -50
- codex/librarian/covers/path.py +6 -8
- codex/librarian/covers/purge.py +18 -28
- codex/librarian/covers/tasks.py +2 -3
- codex/librarian/importer/{aggregate.py → aggregate_metadata.py} +88 -58
- codex/librarian/importer/{extract.py → clean_metadata.py} +4 -33
- codex/librarian/importer/const.py +13 -46
- codex/librarian/importer/create_comics.py +17 -101
- codex/librarian/importer/create_fks.py +134 -99
- codex/librarian/importer/deleted.py +28 -103
- codex/librarian/importer/failed_imports.py +64 -42
- codex/librarian/importer/importerd.py +290 -71
- codex/librarian/importer/link_comics.py +26 -26
- codex/librarian/importer/moved.py +81 -128
- codex/librarian/importer/query_fks.py +122 -142
- codex/librarian/importer/status.py +39 -8
- codex/librarian/importer/tasks.py +9 -26
- codex/librarian/importer/update_comics.py +89 -0
- codex/librarian/janitor/cleanup.py +2 -26
- codex/librarian/janitor/failed_imports.py +1 -1
- codex/librarian/janitor/janitor.py +1 -13
- codex/librarian/janitor/janitord.py +1 -1
- codex/librarian/janitor/status.py +0 -1
- codex/librarian/janitor/tasks.py +0 -5
- codex/librarian/librariand.py +5 -9
- codex/librarian/notifier/notifierd.py +1 -1
- codex/librarian/notifier/tasks.py +4 -4
- codex/librarian/search/update.py +1 -1
- codex/librarian/watchdog/db_snapshot.py +6 -9
- codex/librarian/watchdog/emitter.py +0 -3
- codex/librarian/watchdog/event_batcherd.py +3 -19
- codex/librarian/watchdog/events.py +36 -176
- codex/librarian/watchdog/observers.py +3 -13
- codex/middleware.py +2 -1
- codex/migrations/0027_sort_name.py +149 -0
- codex/models/admin.py +2 -0
- codex/models/base.py +0 -3
- codex/models/comic.py +9 -13
- codex/models/{util.py → const.py} +2 -15
- codex/models/groups.py +14 -28
- codex/models/library.py +3 -11
- codex/models/paths.py +14 -61
- codex/permissions.py +1 -1
- codex/search/query.py +2 -25
- codex/search/writing.py +1 -1
- codex/serializers/admin.py +44 -53
- codex/serializers/browser/choices.py +42 -37
- codex/serializers/browser/filters.py +131 -32
- codex/serializers/browser/metadata.py +1 -45
- codex/serializers/browser/mixins.py +4 -37
- codex/serializers/browser/page.py +6 -3
- codex/serializers/browser/settings.py +11 -42
- codex/serializers/choices.py +1 -14
- codex/serializers/models/pycountry.py +51 -11
- codex/serializers/opds/v2.py +16 -10
- codex/serializers/opds/v2_unused.py +3 -5
- codex/serializers/reader.py +7 -21
- codex/serializers/redirect.py +2 -3
- codex/serializers/route.py +7 -16
- codex/settings/settings.py +8 -23
- codex/startup.py +3 -77
- codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js +1 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js.br +0 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js.gz +0 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.js +1 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.js.br +0 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js → VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.js → VCheckboxBtn-yCN3TB6A.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.js.gz +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js +1 -0
- codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js.br +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js.gz +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.js +1 -0
- codex/static_root/assets/VCombobox-dsIKbjps.js.br +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.js.gz +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js +1 -0
- codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js.br +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js.gz +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.js +1 -0
- codex/static_root/assets/VDialog-BNHg8txZ.js.br +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.js.gz +0 -0
- codex/static_root/assets/{VDialog-lQ13FnRR.1c801ad756b2.css → VDialog-C5Nxd_zP.css} +1 -1
- codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
- codex/static_root/assets/{VDialog-lQ13FnRR.css → VDialog-C5Nxd_zP.d327f505ef0e.css} +1 -1
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js +1 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.js +1 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js +1 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js.br +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.js +1 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.js.br +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.js.gz +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js +1 -0
- codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js.br +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js.gz +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.js +1 -0
- codex/static_root/assets/VSelect--XHNpxvB.js.br +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js +1 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.js +1 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js +1 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js.br +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.js +1 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.js.br +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.f0f2106b158b.js → VTable-hGJDFK5t.d2f84d68747b.js} +1 -1
- codex/static_root/assets/VTable-hGJDFK5t.d2f84d68747b.js.br +0 -0
- codex/static_root/assets/VTable-hGJDFK5t.d2f84d68747b.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.js → VTable-hGJDFK5t.js} +1 -1
- codex/static_root/assets/VTable-hGJDFK5t.js.br +0 -0
- codex/static_root/assets/VTable-hGJDFK5t.js.gz +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js +1 -0
- codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js.br +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js.gz +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.js +1 -0
- codex/static_root/assets/VTextField-C8y3lyz9.js.br +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.js.gz +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css +1 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css +1 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js +1 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js.br +0 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js.gz +0 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.js +1 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.js.br +0 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.js.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.82c98714bcf1.css → admin-BbrOU9Y9.29936748af9e.css} +1 -1
- codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
- codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.css → admin-BbrOU9Y9.css} +1 -1
- codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
- codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
- codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js +1 -0
- codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js.br +0 -0
- codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js.gz +0 -0
- codex/static_root/assets/admin-Bvd-SvGS.js +1 -0
- codex/static_root/assets/admin-Bvd-SvGS.js.br +0 -0
- codex/static_root/assets/admin-Bvd-SvGS.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +1 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +1 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js +30 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js +30 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js.gz +0 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css +1 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.br +0 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.gz +0 -0
- codex/static_root/assets/browser-CZeVqRNy.css +1 -0
- codex/static_root/assets/browser-CZeVqRNy.css.br +0 -0
- codex/static_root/assets/browser-CZeVqRNy.css.gz +0 -0
- codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js +1 -0
- codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js.br +0 -0
- codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js.gz +0 -0
- codex/static_root/assets/browser-rc5ObkeN.js +1 -0
- codex/static_root/assets/browser-rc5ObkeN.js.br +0 -0
- codex/static_root/assets/browser-rc5ObkeN.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js +1 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.js +1 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +1 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css +1 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js +1 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.js +1 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.js.gz +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js +1 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js.br +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js.gz +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.js +1 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.js.br +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +1 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.css +1 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
- codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js +1 -0
- codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js.br +0 -0
- codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js.gz +0 -0
- codex/static_root/assets/filter-_wYL6jQC.js +1 -0
- codex/static_root/assets/filter-_wYL6jQC.js.br +0 -0
- codex/static_root/assets/filter-_wYL6jQC.js.gz +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +1 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css +1 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js +1 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js.br +0 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js.gz +0 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.js +1 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.js.br +0 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.1afb2ce453ff.js → group-tab-DAjvuSUO.4494d4846fa6.js} +1 -1
- codex/static_root/assets/group-tab-DAjvuSUO.4494d4846fa6.js.br +0 -0
- codex/static_root/assets/group-tab-DAjvuSUO.4494d4846fa6.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.js → group-tab-DAjvuSUO.js} +1 -1
- codex/static_root/assets/group-tab-DAjvuSUO.js.br +0 -0
- codex/static_root/assets/group-tab-DAjvuSUO.js.gz +0 -0
- codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js +1 -0
- codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js.br +0 -0
- codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js.gz +0 -0
- codex/static_root/assets/http-error-CDQ-296a.js +1 -0
- codex/static_root/assets/http-error-CDQ-296a.js.br +0 -0
- codex/static_root/assets/http-error-CDQ-296a.js.gz +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css +1 -0
- codex/static_root/assets/{http-error-DKulXuTl.95e42988d5d5.css.br → http-error-_wlDqEcp.5b78d3e332c9.css.br} +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css.gz +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.css +1 -0
- codex/static_root/assets/{http-error-DKulXuTl.css.br → http-error-_wlDqEcp.css.br} +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.css.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +1 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.js +1 -0
- codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +1 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +2 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css +1 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css.br +2 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
- codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js +1 -0
- codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js.br +0 -0
- codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js.gz +0 -0
- codex/static_root/assets/library-tab-LsexSRi2.js +1 -0
- codex/static_root/assets/library-tab-LsexSRi2.js.br +0 -0
- codex/static_root/assets/library-tab-LsexSRi2.js.gz +0 -0
- codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js +32 -0
- codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js.br +0 -0
- codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js.gz +0 -0
- codex/static_root/assets/main-CjPnwTfF.js +32 -0
- codex/static_root/assets/main-CjPnwTfF.js.br +0 -0
- codex/static_root/assets/main-CjPnwTfF.js.gz +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css → main-sK7HC5h4.6f04ddfc5f1a.css} +1 -1
- codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css.gz → main-sK7HC5h4.6f04ddfc5f1a.css.gz} +0 -0
- codex/static_root/assets/{main-Ztp855PW.css → main-sK7HC5h4.css} +1 -1
- codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.css.gz → main-sK7HC5h4.css.gz} +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css +1 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css +1 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js +1 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.js +1 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.js.gz +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js +35 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js.br +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js.gz +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.js +35 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.js.br +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.js.gz +0 -0
- codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js +2 -0
- codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js.br +0 -0
- codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js.gz +0 -0
- codex/static_root/assets/reader-BuMh4x3f.js +2 -0
- codex/static_root/assets/reader-BuMh4x3f.js.br +0 -0
- codex/static_root/assets/reader-BuMh4x3f.js.gz +0 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css +1 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.br +0 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.gz +0 -0
- codex/static_root/assets/reader-Dspy46e8.css +1 -0
- codex/static_root/assets/reader-Dspy46e8.css.br +0 -0
- codex/static_root/assets/reader-Dspy46e8.css.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.98e65384c3c9.css → relation-chips-D6T6FX7G.css} +1 -1
- codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
- codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.css → relation-chips-D6T6FX7G.db095b2497e4.css} +1 -1
- codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
- codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js +1 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js.br +0 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js.gz +0 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.js +1 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.js.br +0 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css +1 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.br +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.gz +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css +1 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css.br +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css.gz +0 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js +2 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js.br +0 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js.gz +0 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.js +2 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.js.br +0 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.js.gz +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js +1 -0
- codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js.br +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js.gz +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.js +1 -0
- codex/static_root/assets/stats-tab-AwbCeW63.js.br +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.js.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +1 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +2 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css +1 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css.br +2 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
- codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js +1 -0
- codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js.br +0 -0
- codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js.gz +0 -0
- codex/static_root/assets/task-tab-DeHQTHuz.js +1 -0
- codex/static_root/assets/task-tab-DeHQTHuz.js.br +0 -0
- codex/static_root/assets/task-tab-DeHQTHuz.js.gz +0 -0
- codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +1 -0
- codex/static_root/assets/to-case-kUm8NPpW.js +1 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js +1 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js.br +0 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js.gz +0 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.js +1 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.js.br +0 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.js.gz +0 -0
- codex/static_root/assets/{unauthorized-CibJYD8o.4c2de98c2b53.css → unauthorized-DWgytUow.ca2f13dbdbf5.css} +1 -1
- codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.br +0 -0
- codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.gz +0 -0
- codex/static_root/assets/{unauthorized-CibJYD8o.css → unauthorized-DWgytUow.css} +1 -1
- codex/static_root/assets/unauthorized-DWgytUow.css.br +0 -0
- codex/static_root/assets/unauthorized-DWgytUow.css.gz +0 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js +1 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js.br +0 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js.gz +0 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.js +1 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.js.br +0 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.js.gz +0 -0
- codex/static_root/js/choices-admin.248384ece3f6.json +1 -0
- codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
- codex/static_root/js/choices-admin.248384ece3f6.json.gz +0 -0
- codex/static_root/js/choices-admin.json +1 -1
- codex/static_root/js/choices-admin.json.br +0 -0
- codex/static_root/js/choices-admin.json.gz +0 -0
- codex/static_root/js/choices.b60817ba0a93.json +1 -0
- codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
- codex/static_root/js/choices.b60817ba0a93.json.gz +0 -0
- codex/static_root/js/choices.json +1 -1
- codex/static_root/js/choices.json.br +0 -0
- codex/static_root/js/choices.json.gz +0 -0
- codex/static_root/manifest.b5fe5d7ef44e.json +641 -0
- codex/static_root/manifest.b5fe5d7ef44e.json.br +0 -0
- codex/static_root/manifest.b5fe5d7ef44e.json.gz +0 -0
- codex/static_root/manifest.json +298 -279
- 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 +2 -11
- codex/urls/api/auth.py +1 -2
- codex/urls/api/browser.py +4 -13
- codex/urls/api/reader.py +9 -3
- codex/urls/api/v3.py +0 -2
- codex/urls/opds/binary.py +1 -1
- codex/views/admin/api_key.py +8 -7
- codex/views/admin/flag.py +7 -2
- codex/views/admin/group.py +6 -2
- codex/views/admin/library.py +37 -44
- codex/views/admin/stats.py +83 -20
- codex/views/admin/tasks.py +12 -26
- codex/views/admin/user.py +9 -3
- codex/views/auth.py +54 -31
- codex/views/bookmark.py +72 -92
- codex/views/browser/base.py +90 -42
- codex/views/browser/browser.py +371 -92
- codex/views/browser/{annotations.py → browser_annotations.py} +121 -67
- codex/views/browser/browser_breadcrumbs.py +175 -0
- codex/views/browser/browser_order_by.py +63 -0
- codex/views/browser/choices.py +85 -38
- codex/views/browser/const.py +3 -0
- codex/views/browser/filters/annotations.py +19 -87
- codex/views/browser/filters/bookmark.py +12 -24
- codex/views/browser/filters/field.py +6 -8
- codex/views/browser/filters/group.py +14 -35
- codex/views/browser/filters/search.py +20 -31
- codex/views/browser/metadata.py +73 -112
- codex/views/browser/session.py +12 -0
- codex/views/const.py +0 -140
- codex/views/cover.py +77 -0
- codex/views/download.py +8 -5
- codex/views/frontend.py +2 -4
- codex/views/opds/authentication_v1.py +45 -46
- codex/views/opds/binary.py +10 -5
- codex/views/opds/const.py +0 -13
- codex/views/opds/urls.py +4 -6
- codex/views/opds/util.py +12 -4
- codex/views/opds/v1/entry/links.py +33 -18
- codex/views/opds/v1/facets.py +7 -7
- codex/views/opds/v1/feed.py +45 -55
- codex/views/opds/v1/links.py +5 -7
- codex/views/opds/v1/opensearch_v1.py +6 -2
- codex/views/opds/v2/feed.py +26 -25
- codex/views/opds/v2/links.py +2 -4
- codex/views/opds/v2/publications.py +54 -34
- codex/views/opds/v2/top_links.py +2 -2
- codex/views/reader/page.py +8 -15
- codex/views/reader/reader.py +322 -19
- codex/views/reader/{settings.py → session.py} +2 -4
- codex/views/session.py +102 -83
- codex/views/template.py +3 -3
- codex/views/version.py +2 -2
- codex/websockets/consumers.py +10 -14
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/METADATA +15 -22
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/RECORD +448 -466
- codex/librarian/importer/cache.py +0 -93
- codex/librarian/importer/create_covers.py +0 -96
- codex/librarian/importer/importer.py +0 -96
- codex/librarian/importer/init.py +0 -265
- codex/librarian/importer/link_covers.py +0 -73
- codex/librarian/importer/query_covers.py +0 -52
- codex/migrations/0027_import_order_and_covers.py +0 -280
- codex/serializers/browser/mtime.py +0 -21
- codex/serializers/fields.py +0 -149
- codex/serializers/settings.py +0 -12
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js +0 -1
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.br +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.js +0 -1
- codex/static_root/assets/VCheckbox-DanrzdHo.js.br +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.gz +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js +0 -1
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.br +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.gz +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.js +0 -1
- codex/static_root/assets/VCombobox-DDvdoPTG.js.br +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.js.gz +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js +0 -1
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.br +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.gz +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.js +0 -1
- codex/static_root/assets/VDialog-Bd5dN_E1.js.br +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.js.gz +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js +0 -1
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js +0 -1
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js +0 -1
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js +0 -1
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js.gz +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js +0 -1
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.br +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.gz +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.js +0 -1
- codex/static_root/assets/VSelect-C_yPbyCG.js.br +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js +0 -1
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js +0 -1
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js +0 -1
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js +0 -1
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js.gz +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.br +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.gz +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.js.br +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.js.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js +0 -1
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.br +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.gz +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.js +0 -1
- codex/static_root/assets/VTextField-o1N9ylIb.js.br +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js +0 -1
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.br +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.js +0 -1
- codex/static_root/assets/VWindowItem-C_zvuz9C.js.br +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.js.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js +0 -1
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.br +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.gz +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.js +0 -1
- codex/static_root/assets/admin-DHyHEWZ3.js.br +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js +0 -30
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js +0 -30
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css +0 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css +0 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js +0 -1
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.br +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.gz +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.js +0 -1
- codex/static_root/assets/browser-D0Vjmmp_.js.br +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.js.gz +0 -0
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css +0 -1
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.br +0 -0
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.gz +0 -0
- codex/static_root/assets/browser-D7QQDneO.css +0 -1
- codex/static_root/assets/browser-D7QQDneO.css.br +0 -0
- codex/static_root/assets/browser-D7QQDneO.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js +0 -1
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js +0 -1
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js +0 -1
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js +0 -1
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js.gz +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +0 -1
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +0 -1
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
- codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +0 -1
- codex/static_root/assets/datetime-column-DeCthByU.css +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.br +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.gz +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.js +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.js.br +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.js.gz +0 -0
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js +0 -1
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.br +0 -0
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.gz +0 -0
- codex/static_root/assets/filter-DgPg2ts9.js +0 -1
- codex/static_root/assets/filter-DgPg2ts9.js.br +0 -0
- codex/static_root/assets/filter-DgPg2ts9.js.gz +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js +0 -1
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.br +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.gz +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.js +0 -1
- codex/static_root/assets/flag-tab-9qxOALxP.js.br +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.js.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.br +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.gz +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.js.br +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.js.gz +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js +0 -1
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.br +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.gz +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.js +0 -1
- codex/static_root/assets/http-error-B4FHoKfs.js.br +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.js.gz +0 -0
- codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css +0 -1
- codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css.gz +0 -0
- codex/static_root/assets/http-error-DKulXuTl.css +0 -1
- codex/static_root/assets/http-error-DKulXuTl.css.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +0 -1
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css +0 -1
- codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js +0 -1
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.br +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.gz +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.js +0 -1
- codex/static_root/assets/library-tab-BWo0cmzk.js.br +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.js.gz +0 -0
- codex/static_root/assets/main-Ztp855PW.css.br +0 -0
- codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js +0 -32
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.br +0 -0
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.gz +0 -0
- codex/static_root/assets/main-ipmlipkF.js +0 -32
- codex/static_root/assets/main-ipmlipkF.js.br +0 -0
- codex/static_root/assets/main-ipmlipkF.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js +0 -1
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js +0 -1
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js +0 -305
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.br +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.js +0 -305
- codex/static_root/assets/pdf-doc-C2StaYUz.js.br +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.js.gz +0 -0
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js +0 -2
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.br +0 -0
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.gz +0 -0
- codex/static_root/assets/reader-CCnq3j1-.js +0 -2
- codex/static_root/assets/reader-CCnq3j1-.js.br +0 -0
- codex/static_root/assets/reader-CCnq3j1-.js.gz +0 -0
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css +0 -1
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.br +0 -0
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.gz +0 -0
- codex/static_root/assets/reader-CqXFa_gE.css +0 -1
- codex/static_root/assets/reader-CqXFa_gE.css.br +0 -0
- codex/static_root/assets/reader-CqXFa_gE.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js +0 -1
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.br +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.gz +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.js +0 -1
- codex/static_root/assets/relation-chips-C9ysW0tU.js.br +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js +0 -2
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.br +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.js +0 -2
- codex/static_root/assets/settings-drawer-C0ofzAhW.js.br +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.js.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +0 -1
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css +0 -1
- codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js +0 -1
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.br +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.gz +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.js +0 -1
- codex/static_root/assets/stats-tab-BqJj7j1w.js.br +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.js.gz +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js +0 -1
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.br +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.gz +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.js +0 -1
- codex/static_root/assets/task-tab-BsPzNk8S.js.br +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.js.gz +0 -0
- codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js +0 -1
- codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js.br +0 -0
- codex/static_root/assets/to-case-KvqtSug2.js +0 -1
- codex/static_root/assets/to-case-KvqtSug2.js.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js +0 -1
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.br +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.gz +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.js +0 -1
- codex/static_root/assets/unauthorized-DwSdNzX2.js.br +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.js.gz +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js +0 -1
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.br +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.gz +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.js +0 -1
- codex/static_root/assets/user-tab-DrJOb9Ys.js.br +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.js.gz +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json +0 -1
- codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json +0 -1
- codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
- codex/static_root/manifest.1c84116d9a0a.json +0 -622
- codex/static_root/manifest.1c84116d9a0a.json.br +0 -0
- codex/static_root/manifest.1c84116d9a0a.json.gz +0 -0
- codex/util.py +0 -12
- codex/views/admin/auth.py +0 -28
- codex/views/browser/breadcrumbs.py +0 -202
- codex/views/browser/cover.py +0 -165
- codex/views/browser/mtime.py +0 -61
- codex/views/browser/order_by.py +0 -65
- codex/views/browser/paginate.py +0 -106
- codex/views/browser/settings.py +0 -66
- codex/views/browser/title.py +0 -48
- codex/views/browser/validate.py +0 -161
- codex/views/opds/auth.py +0 -17
- codex/views/public.py +0 -36
- codex/views/reader/arcs.py +0 -92
- codex/views/reader/books.py +0 -181
- codex/views/reader/init.py +0 -125
- codex/views/settings.py +0 -47
- codex/views/util.py +0 -77
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/LICENSE +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/WHEEL +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/entry_points.txt +0 -0
codex/views/browser/metadata.py
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
"""View for marking comics read and unread."""
|
|
2
2
|
|
|
3
3
|
from types import MappingProxyType
|
|
4
|
+
from typing import ClassVar
|
|
4
5
|
|
|
6
|
+
from django.core.exceptions import FieldDoesNotExist
|
|
5
7
|
from django.db.models import Count, IntegerField, Sum, Value
|
|
6
8
|
from drf_spectacular.utils import extend_schema
|
|
7
9
|
from rest_framework.exceptions import NotFound
|
|
8
10
|
from rest_framework.response import Response
|
|
9
11
|
|
|
10
|
-
from codex.librarian.importer.const import
|
|
11
|
-
COMIC_M2M_FIELD_NAMES,
|
|
12
|
-
)
|
|
12
|
+
from codex.librarian.importer.const import COMIC_M2M_FIELD_NAMES
|
|
13
13
|
from codex.logger.logging import get_logger
|
|
14
14
|
from codex.models import AdminFlag, Comic
|
|
15
|
-
from codex.
|
|
16
|
-
from codex.
|
|
17
|
-
from codex.
|
|
18
|
-
from codex.views.browser.annotations import BrowserAnnotationsView
|
|
19
|
-
from codex.views.const import METADATA_GROUP_RELATION
|
|
15
|
+
from codex.serializers.browser.metadata import MetadataSerializer
|
|
16
|
+
from codex.views.auth import IsAuthenticatedOrEnabledNonUsers
|
|
17
|
+
from codex.views.browser.browser_annotations import BrowserAnnotationsView
|
|
20
18
|
|
|
21
19
|
LOG = get_logger(__name__)
|
|
22
20
|
_ADMIN_OR_FILE_VIEW_ENABLED_COMIC_VALUE_FIELDS = frozenset({"path"})
|
|
@@ -39,50 +37,25 @@ _COMIC_VALUE_FIELD_NAMES = frozenset(
|
|
|
39
37
|
)
|
|
40
38
|
_COMIC_VALUE_FIELDS_CONFLICTING_PREFIX = "conflict_"
|
|
41
39
|
_COMIC_RELATED_VALUE_FIELDS = frozenset({"series__volume_count", "volume__issue_count"})
|
|
42
|
-
_PATH_GROUPS =
|
|
40
|
+
_PATH_GROUPS = ("c", "f")
|
|
43
41
|
_CONTRIBUTOR_RELATIONS = ("role", "person")
|
|
44
42
|
_SUM_FIELDS = frozenset({"page_count", "size"})
|
|
45
|
-
_GROUP_RELS =
|
|
46
|
-
{
|
|
47
|
-
"i": ("publisher",),
|
|
48
|
-
"s": (
|
|
49
|
-
"publisher",
|
|
50
|
-
"imprint",
|
|
51
|
-
),
|
|
52
|
-
"v": (
|
|
53
|
-
"publisher",
|
|
54
|
-
"imprint",
|
|
55
|
-
"series",
|
|
56
|
-
),
|
|
57
|
-
"c": ("publisher", "imprint", "series", "volume"),
|
|
58
|
-
}
|
|
59
|
-
)
|
|
43
|
+
_GROUP_RELS = frozenset({"publisher", "imprint", "series", "volume"})
|
|
60
44
|
|
|
61
45
|
|
|
62
46
|
class MetadataView(BrowserAnnotationsView):
|
|
63
47
|
"""Comic metadata."""
|
|
64
48
|
|
|
49
|
+
permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
|
|
65
50
|
serializer_class = MetadataSerializer
|
|
66
|
-
input_serializer_class = BrowserFilterChoicesInputSerilalizer
|
|
67
51
|
TARGET = "metadata"
|
|
68
52
|
ADMIN_FLAG_VALUE_KEY_MAP = MappingProxyType(
|
|
69
53
|
{
|
|
70
54
|
AdminFlag.FlagChoices.FOLDER_VIEW.value: "folder_view",
|
|
55
|
+
AdminFlag.FlagChoices.DYNAMIC_GROUP_COVERS.value: "dynamic_group_covers",
|
|
71
56
|
}
|
|
72
57
|
)
|
|
73
58
|
|
|
74
|
-
def init_request(self):
|
|
75
|
-
"""Initialize request."""
|
|
76
|
-
self.set_admin_flags()
|
|
77
|
-
self.parse_params()
|
|
78
|
-
self.set_model()
|
|
79
|
-
self.set_rel_prefix()
|
|
80
|
-
|
|
81
|
-
def set_valid_browse_nav_groups(self, valid_top_groups): # noqa: ARG002
|
|
82
|
-
"""Limited allowed nav groups for metadata."""
|
|
83
|
-
group = self.kwargs["group"]
|
|
84
|
-
self.valid_nav_groups = (group,)
|
|
85
|
-
|
|
86
59
|
def _get_comic_value_fields(self):
|
|
87
60
|
"""Include the path field for staff."""
|
|
88
61
|
fields = set(_COMIC_VALUE_FIELD_NAMES)
|
|
@@ -103,7 +76,6 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
103
76
|
):
|
|
104
77
|
"""Annotate the intersection of value and fk fields."""
|
|
105
78
|
(filtered_qs, qs) = querysets
|
|
106
|
-
|
|
107
79
|
for field in fields:
|
|
108
80
|
# Annotate variant counts
|
|
109
81
|
# If 1 variant, annotate value, otherwise None
|
|
@@ -158,48 +130,6 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
158
130
|
)
|
|
159
131
|
return qs
|
|
160
132
|
|
|
161
|
-
def _query_groups(self):
|
|
162
|
-
"""Query the through models to show group lists."""
|
|
163
|
-
groups = {}
|
|
164
|
-
if not self.model:
|
|
165
|
-
return groups
|
|
166
|
-
group = self.kwargs["group"]
|
|
167
|
-
rel = METADATA_GROUP_RELATION.get(group)
|
|
168
|
-
if not rel:
|
|
169
|
-
return groups
|
|
170
|
-
rel = rel + "__in"
|
|
171
|
-
pks = self.kwargs["pks"]
|
|
172
|
-
group_filter = {rel: pks}
|
|
173
|
-
|
|
174
|
-
for field_name in _GROUP_RELS.get(group, ()):
|
|
175
|
-
field = self.model._meta.get_field(field_name)
|
|
176
|
-
model = field.related_model
|
|
177
|
-
if not model:
|
|
178
|
-
continue
|
|
179
|
-
|
|
180
|
-
qs = model.objects.filter(**group_filter)
|
|
181
|
-
qs = qs.only("name").distinct()
|
|
182
|
-
qs = qs.group_by("name")
|
|
183
|
-
qs = qs.annotate(ids=JsonGroupArray("id", distinct=True))
|
|
184
|
-
qs = qs.values("ids", "name")
|
|
185
|
-
groups[field_name] = qs
|
|
186
|
-
return groups
|
|
187
|
-
|
|
188
|
-
@staticmethod
|
|
189
|
-
def _get_optimized_m2m_query(key, qs):
|
|
190
|
-
if key == "contributors":
|
|
191
|
-
qs = qs.prefetch_related(*_CONTRIBUTOR_RELATIONS)
|
|
192
|
-
qs = qs.only(*_CONTRIBUTOR_RELATIONS)
|
|
193
|
-
elif key == "story_arc_numbers":
|
|
194
|
-
qs = qs.select_related("story_arc")
|
|
195
|
-
qs = qs.only("story_arc", "number")
|
|
196
|
-
elif key == "identifiers":
|
|
197
|
-
qs = qs.select_related("identifier_type")
|
|
198
|
-
qs = qs.only("identifier_type", "nss", "url")
|
|
199
|
-
else:
|
|
200
|
-
qs = qs.only("name")
|
|
201
|
-
return qs
|
|
202
|
-
|
|
203
133
|
def _query_m2m_intersections(self, filtered_qs):
|
|
204
134
|
"""Query the through models to figure out m2m intersections."""
|
|
205
135
|
# Speed ok, but still does a query per m2m model
|
|
@@ -207,17 +137,29 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
207
137
|
pk_field = self.rel_prefix + "pk"
|
|
208
138
|
comic_pks = filtered_qs.values_list(pk_field, flat=True)
|
|
209
139
|
comic_pks_count = comic_pks.count()
|
|
210
|
-
for field_name in COMIC_M2M_FIELD_NAMES:
|
|
140
|
+
for field_name in COMIC_M2M_FIELD_NAMES | _GROUP_RELS:
|
|
141
|
+
try:
|
|
142
|
+
if (
|
|
143
|
+
field_name in _GROUP_RELS
|
|
144
|
+
and self.model
|
|
145
|
+
and not self.model._meta.get_field(field_name)
|
|
146
|
+
):
|
|
147
|
+
continue
|
|
148
|
+
except FieldDoesNotExist:
|
|
149
|
+
continue
|
|
150
|
+
|
|
211
151
|
model = Comic._meta.get_field(field_name).related_model
|
|
212
152
|
if not model:
|
|
213
153
|
reason = f"No model found for comic field: {field_name}"
|
|
214
154
|
raise ValueError(reason)
|
|
215
155
|
|
|
216
156
|
intersection_qs = model.objects.filter(comic__pk__in=comic_pks)
|
|
157
|
+
if field_name in _GROUP_RELS:
|
|
158
|
+
group_by = "name" if field_name == "volume" else "sort_name"
|
|
159
|
+
intersection_qs = intersection_qs.group_by(group_by)
|
|
217
160
|
intersection_qs = intersection_qs.alias(count=Count("comic")).filter(
|
|
218
161
|
count=comic_pks_count
|
|
219
162
|
)
|
|
220
|
-
intersection_qs = self._get_optimized_m2m_query(field_name, intersection_qs)
|
|
221
163
|
|
|
222
164
|
m2m_intersections[field_name] = intersection_qs
|
|
223
165
|
return m2m_intersections
|
|
@@ -238,36 +180,42 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
238
180
|
"""Values for highlighting the current group."""
|
|
239
181
|
if self.model and not self.is_model_comic:
|
|
240
182
|
# move the name of the group to the correct field
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
setattr(obj,
|
|
183
|
+
group_field = self.model.__name__.lower()
|
|
184
|
+
group_obj = {"pk": obj.pk, "name": obj.name}
|
|
185
|
+
setattr(obj, group_field, group_obj)
|
|
244
186
|
obj.name = None
|
|
245
187
|
|
|
188
|
+
@staticmethod
|
|
189
|
+
def _get_optimized_m2m_query(key, qs):
|
|
190
|
+
# XXX The prefetch gets removed by field.set() :(
|
|
191
|
+
if key == "contributors":
|
|
192
|
+
optimized_qs = qs.prefetch_related(*_CONTRIBUTOR_RELATIONS).only(
|
|
193
|
+
*_CONTRIBUTOR_RELATIONS
|
|
194
|
+
)
|
|
195
|
+
elif key == "story_arc_numbers":
|
|
196
|
+
optimized_qs = qs.prefetch_related("story_arc").only("story_arc", "number")
|
|
197
|
+
elif key == "identifiers":
|
|
198
|
+
optimized_qs = qs.prefetch_related("identifier_type").only(
|
|
199
|
+
"identifier_type", "nss", "url"
|
|
200
|
+
)
|
|
201
|
+
else:
|
|
202
|
+
optimized_qs = qs.only("name")
|
|
203
|
+
return optimized_qs
|
|
204
|
+
|
|
246
205
|
@classmethod
|
|
247
206
|
def _copy_m2m_intersections(cls, obj, m2m_intersections):
|
|
248
207
|
"""Copy the m2m intersections into the object."""
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
f"{PREFETCH_PREFIX}{key}"
|
|
253
|
-
if key in ("identifiers", "contributors", "story_arc_numbers")
|
|
254
|
-
else key
|
|
255
|
-
)
|
|
256
|
-
if hasattr(obj, serializer_key):
|
|
208
|
+
for key, value in m2m_intersections.items():
|
|
209
|
+
optimized_qs = cls._get_optimized_m2m_query(key, value)
|
|
210
|
+
if hasattr(obj, key) and key not in _GROUP_RELS:
|
|
257
211
|
# real db fields need to use their special set method.
|
|
258
|
-
field = getattr(obj,
|
|
259
|
-
field.set(
|
|
260
|
-
qs,
|
|
261
|
-
clear=True,
|
|
262
|
-
)
|
|
212
|
+
field = getattr(obj, key)
|
|
213
|
+
field.set(optimized_qs)
|
|
263
214
|
else:
|
|
264
215
|
# fake db field is just a queryset attached.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def _copy_groups(obj, groups):
|
|
269
|
-
for field, group_qs in groups.items():
|
|
270
|
-
setattr(obj, field + "_list", group_qs)
|
|
216
|
+
if key in _GROUP_RELS:
|
|
217
|
+
optimized_qs = optimized_qs[0] if len(optimized_qs) else None
|
|
218
|
+
setattr(obj, key, optimized_qs)
|
|
271
219
|
|
|
272
220
|
@staticmethod
|
|
273
221
|
def _copy_conflicting_simple_fields(obj):
|
|
@@ -277,11 +225,10 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
277
225
|
val = getattr(obj, conflict_field)
|
|
278
226
|
setattr(obj, field, val)
|
|
279
227
|
|
|
280
|
-
def _copy_annotations_into_comic_fields(self, obj,
|
|
228
|
+
def _copy_annotations_into_comic_fields(self, obj, m2m_intersections):
|
|
281
229
|
"""Copy a bunch of values that i couldn't fit cleanly in the main queryset."""
|
|
282
230
|
self._path_security(obj)
|
|
283
231
|
self._highlight_current_group(obj)
|
|
284
|
-
self._copy_groups(obj, groups)
|
|
285
232
|
self._copy_m2m_intersections(obj, m2m_intersections)
|
|
286
233
|
if not self.is_model_comic:
|
|
287
234
|
self._copy_conflicting_simple_fields(obj)
|
|
@@ -301,28 +248,42 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
301
248
|
# values dicts don't copy relations to the serializer. The values
|
|
302
249
|
# dict is necessary because of the folders view union in browser.py.
|
|
303
250
|
|
|
304
|
-
|
|
251
|
+
if self.model is None:
|
|
252
|
+
# TODO this looks redundant after set_browse_model
|
|
253
|
+
group = self.kwargs["group"]
|
|
254
|
+
raise NotFound(detail=f"Cannot get metadata for {group=}")
|
|
305
255
|
|
|
256
|
+
object_filter = self.get_query_filters_without_group(self.model) # type: ignore
|
|
257
|
+
pks = self.kwargs["pks"]
|
|
258
|
+
qs = self.model.objects.filter(object_filter, pk__in=pks)
|
|
259
|
+
qs = self.filter_by_annotations(qs, self.model, binary=True)
|
|
306
260
|
filtered_qs = qs
|
|
307
261
|
qs = self.annotate_order_aggregates(qs, self.model)
|
|
308
262
|
qs = self.annotate_card_aggregates(qs, self.model)
|
|
309
|
-
qs = self.
|
|
263
|
+
qs = self.annotate_for_metadata(qs, self.model)
|
|
310
264
|
group_by = self.get_group_by()
|
|
311
265
|
qs = qs.group_by(group_by)
|
|
266
|
+
qs = self._annotate_values_and_fks(qs, filtered_qs)
|
|
267
|
+
|
|
268
|
+
qs_list = self.re_cover_multi_groups(qs)
|
|
312
269
|
|
|
313
270
|
try:
|
|
314
|
-
obj =
|
|
271
|
+
obj = qs_list[0]
|
|
315
272
|
if not obj:
|
|
316
273
|
reason = "Empty obj"
|
|
317
274
|
raise ValueError(reason) # noqa TRY301
|
|
318
275
|
except (IndexError, ValueError) as exc:
|
|
319
276
|
self._raise_not_found(exc)
|
|
320
277
|
|
|
321
|
-
groups = self._query_groups()
|
|
322
278
|
m2m_intersections = self._query_m2m_intersections(filtered_qs)
|
|
323
|
-
return self._copy_annotations_into_comic_fields(obj,
|
|
279
|
+
return self._copy_annotations_into_comic_fields(obj, m2m_intersections) # type: ignore
|
|
280
|
+
|
|
281
|
+
def set_valid_browse_nav_groups(self, _valid_top_groups):
|
|
282
|
+
"""Limited allowed nav groups for metadata."""
|
|
283
|
+
group = self.kwargs["group"]
|
|
284
|
+
self.valid_nav_groups = (group,)
|
|
324
285
|
|
|
325
|
-
@extend_schema(
|
|
286
|
+
@extend_schema(request=BrowserAnnotationsView.input_serializer_class)
|
|
326
287
|
def get(self, *_args, **_kwargs):
|
|
327
288
|
"""Get metadata for a filtered browse group."""
|
|
328
289
|
# Init
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Browser session view."""
|
|
2
|
+
|
|
3
|
+
from codex.serializers.browser.settings import BrowserSettingsSerializer
|
|
4
|
+
from codex.views.session import BrowserSessionViewBase, SessionViewBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BrowserSessionView(BrowserSessionViewBase, SessionViewBase):
|
|
8
|
+
"""Get Browser Settings."""
|
|
9
|
+
|
|
10
|
+
# Put Browser Settings is normally done through BrowserView.get()
|
|
11
|
+
|
|
12
|
+
serializer_class = BrowserSettingsSerializer # type: ignore
|
codex/views/const.py
CHANGED
|
@@ -1,42 +1,7 @@
|
|
|
1
1
|
"""Common view constants."""
|
|
2
2
|
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
3
|
from types import MappingProxyType
|
|
5
4
|
|
|
6
|
-
from django.contrib.auth.models import Group, User
|
|
7
|
-
from django.contrib.sessions.models import Session
|
|
8
|
-
from django.db.models.expressions import Value
|
|
9
|
-
from django.db.models.fields import DateTimeField, PositiveSmallIntegerField
|
|
10
|
-
|
|
11
|
-
from codex.models import (
|
|
12
|
-
AgeRating,
|
|
13
|
-
BrowserGroupModel,
|
|
14
|
-
Character,
|
|
15
|
-
Comic,
|
|
16
|
-
Contributor,
|
|
17
|
-
ContributorPerson,
|
|
18
|
-
ContributorRole,
|
|
19
|
-
Country,
|
|
20
|
-
Folder,
|
|
21
|
-
Genre,
|
|
22
|
-
Identifier,
|
|
23
|
-
IdentifierType,
|
|
24
|
-
Imprint,
|
|
25
|
-
Language,
|
|
26
|
-
Library,
|
|
27
|
-
Location,
|
|
28
|
-
OriginalFormat,
|
|
29
|
-
Publisher,
|
|
30
|
-
ScanInfo,
|
|
31
|
-
Series,
|
|
32
|
-
SeriesGroup,
|
|
33
|
-
StoryArc,
|
|
34
|
-
StoryArcNumber,
|
|
35
|
-
Tag,
|
|
36
|
-
Tagger,
|
|
37
|
-
Team,
|
|
38
|
-
Volume,
|
|
39
|
-
)
|
|
40
5
|
from codex.settings.settings import CODEX_PATH
|
|
41
6
|
|
|
42
7
|
FALSY = {None, "", "false", "0", False}
|
|
@@ -53,108 +18,3 @@ MISSING_COVER_NAME_MAP = MappingProxyType(
|
|
|
53
18
|
)
|
|
54
19
|
MISSING_COVER_FN = "missing-cover-165.webp"
|
|
55
20
|
MISSING_COVER_PATH = STATIC_IMG_PATH / MISSING_COVER_FN
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
ROOT_GROUP = "r"
|
|
59
|
-
FOLDER_GROUP = "f"
|
|
60
|
-
STORY_ARC_GROUP = "a"
|
|
61
|
-
COMIC_GROUP = "c"
|
|
62
|
-
GROUP_RELATION = MappingProxyType(
|
|
63
|
-
{
|
|
64
|
-
**GROUP_NAME_MAP,
|
|
65
|
-
COMIC_GROUP: "pk",
|
|
66
|
-
FOLDER_GROUP: "parent_folder",
|
|
67
|
-
STORY_ARC_GROUP: "story_arc_numbers__story_arc",
|
|
68
|
-
}
|
|
69
|
-
)
|
|
70
|
-
FILTER_ONLY_GROUP_RELATION = MappingProxyType(
|
|
71
|
-
{
|
|
72
|
-
**GROUP_NAME_MAP,
|
|
73
|
-
FOLDER_GROUP: "folders",
|
|
74
|
-
STORY_ARC_GROUP: "story_arc_numbers__story_arc",
|
|
75
|
-
}
|
|
76
|
-
)
|
|
77
|
-
METADATA_GROUP_RELATION = MappingProxyType(
|
|
78
|
-
{
|
|
79
|
-
**GROUP_NAME_MAP,
|
|
80
|
-
COMIC_GROUP: "pk",
|
|
81
|
-
# Possible to list all groups in a non group view, but could get crazy.
|
|
82
|
-
# FOLDER_GROUP: "comic__folder",
|
|
83
|
-
# STORY_ARC_GROUP: "comic__story_arc_number__story_arc"
|
|
84
|
-
}
|
|
85
|
-
)
|
|
86
|
-
CUSTOM_COVER_GROUP_RELATION = MappingProxyType(
|
|
87
|
-
{**GROUP_NAME_MAP, FOLDER_GROUP: "folder", STORY_ARC_GROUP: "storyarc"}
|
|
88
|
-
)
|
|
89
|
-
GROUP_ORDER = "rpisv"
|
|
90
|
-
MODEL_REL_MAP = MappingProxyType(
|
|
91
|
-
{
|
|
92
|
-
Publisher: "publisher",
|
|
93
|
-
Imprint: "imprint",
|
|
94
|
-
Series: "series",
|
|
95
|
-
Volume: "volume",
|
|
96
|
-
Folder: "parent_folder",
|
|
97
|
-
StoryArc: "story_arc_numbers__story_arc",
|
|
98
|
-
Comic: "pk",
|
|
99
|
-
}
|
|
100
|
-
)
|
|
101
|
-
GROUP_MODEL_MAP: MappingProxyType[str, type[BrowserGroupModel] | None] = (
|
|
102
|
-
MappingProxyType(
|
|
103
|
-
{
|
|
104
|
-
ROOT_GROUP: None,
|
|
105
|
-
"p": Publisher,
|
|
106
|
-
"i": Imprint,
|
|
107
|
-
"s": Series,
|
|
108
|
-
"v": Volume,
|
|
109
|
-
COMIC_GROUP: Comic,
|
|
110
|
-
FOLDER_GROUP: Folder,
|
|
111
|
-
STORY_ARC_GROUP: StoryArc,
|
|
112
|
-
}
|
|
113
|
-
)
|
|
114
|
-
)
|
|
115
|
-
GROUP_MODELS = (
|
|
116
|
-
Publisher,
|
|
117
|
-
Imprint,
|
|
118
|
-
Series,
|
|
119
|
-
Volume,
|
|
120
|
-
Folder,
|
|
121
|
-
StoryArc,
|
|
122
|
-
)
|
|
123
|
-
STATS_GROUP_MODELS = (
|
|
124
|
-
*GROUP_MODELS,
|
|
125
|
-
Comic,
|
|
126
|
-
)
|
|
127
|
-
METADATA_MODELS = (
|
|
128
|
-
AgeRating,
|
|
129
|
-
Character,
|
|
130
|
-
Country,
|
|
131
|
-
Genre,
|
|
132
|
-
Identifier,
|
|
133
|
-
IdentifierType,
|
|
134
|
-
Language,
|
|
135
|
-
Location,
|
|
136
|
-
OriginalFormat,
|
|
137
|
-
SeriesGroup,
|
|
138
|
-
ScanInfo,
|
|
139
|
-
StoryArc,
|
|
140
|
-
StoryArcNumber,
|
|
141
|
-
Team,
|
|
142
|
-
Tag,
|
|
143
|
-
Tagger,
|
|
144
|
-
Contributor,
|
|
145
|
-
ContributorPerson,
|
|
146
|
-
ContributorRole,
|
|
147
|
-
)
|
|
148
|
-
CONFIG_MODELS = (
|
|
149
|
-
Library,
|
|
150
|
-
User,
|
|
151
|
-
Group,
|
|
152
|
-
Session,
|
|
153
|
-
)
|
|
154
|
-
MAX_OBJ_PER_PAGE = 100
|
|
155
|
-
GROUP_MTIME_MODEL_MAP = MappingProxyType({"r": Publisher, "a": StoryArc, "f": Folder})
|
|
156
|
-
EPOCH_START = datetime.fromtimestamp(0, tz=timezone.utc)
|
|
157
|
-
ONE_INTEGERFIELD = Value(1, PositiveSmallIntegerField())
|
|
158
|
-
NONE_INTEGERFIELD = Value(None, PositiveSmallIntegerField())
|
|
159
|
-
NONE_DATETIMEFIELD = Value(None, DateTimeField())
|
|
160
|
-
EPOCH_START_DATETIMEFIELD = Value(EPOCH_START)
|
codex/views/cover.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Comic cover thumbnail view."""
|
|
2
|
+
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from django.http import HttpResponse
|
|
6
|
+
from drf_spectacular.types import OpenApiTypes
|
|
7
|
+
from drf_spectacular.utils import extend_schema
|
|
8
|
+
from rest_framework.renderers import BaseRenderer
|
|
9
|
+
from rest_framework.views import APIView
|
|
10
|
+
|
|
11
|
+
from codex.librarian.covers.create import CoverCreateMixin
|
|
12
|
+
from codex.librarian.covers.path import CoverPathMixin
|
|
13
|
+
from codex.librarian.mp_queue import LIBRARIAN_QUEUE
|
|
14
|
+
from codex.logger.logging import get_logger
|
|
15
|
+
from codex.views.auth import GroupACLMixin, IsAuthenticatedOrEnabledNonUsers
|
|
16
|
+
from codex.views.const import MISSING_COVER_FN, MISSING_COVER_NAME_MAP, STATIC_IMG_PATH
|
|
17
|
+
|
|
18
|
+
LOG = get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class WEBPRenderer(BaseRenderer):
|
|
22
|
+
"""Render WEBP images."""
|
|
23
|
+
|
|
24
|
+
media_type = "image/webp"
|
|
25
|
+
format = "webp"
|
|
26
|
+
charset = None
|
|
27
|
+
render_style = "binary"
|
|
28
|
+
|
|
29
|
+
def render(self, data, *_args, **_kwargs):
|
|
30
|
+
"""Return raw data."""
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CoverView(APIView, GroupACLMixin):
|
|
35
|
+
"""ComicCover View."""
|
|
36
|
+
|
|
37
|
+
permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
|
|
38
|
+
renderer_classes = (WEBPRenderer,)
|
|
39
|
+
content_type = "image/webp"
|
|
40
|
+
|
|
41
|
+
def _get_missing_cover_path(self):
|
|
42
|
+
# Get the missing cover, which is a default svg if fetched for a group.
|
|
43
|
+
data = self.request.GET
|
|
44
|
+
group: str = data.get("group", "c") # type: ignore
|
|
45
|
+
cover_name = MISSING_COVER_NAME_MAP.get(group)
|
|
46
|
+
if cover_name:
|
|
47
|
+
cover_fn = cover_name + ".svg"
|
|
48
|
+
content_type = "image/svg+xml"
|
|
49
|
+
else:
|
|
50
|
+
cover_fn = MISSING_COVER_FN
|
|
51
|
+
content_type = "image/webp"
|
|
52
|
+
cover_path = STATIC_IMG_PATH / cover_fn
|
|
53
|
+
return cover_path, content_type
|
|
54
|
+
|
|
55
|
+
@extend_schema(responses={(200, content_type): OpenApiTypes.BINARY})
|
|
56
|
+
def get(self, *_args, **_kwargs):
|
|
57
|
+
"""Get comic cover."""
|
|
58
|
+
thumb_image_data = None
|
|
59
|
+
content_type = "image/webp"
|
|
60
|
+
|
|
61
|
+
pk = self.kwargs.get("pk")
|
|
62
|
+
cover_path = CoverPathMixin.get_cover_path(pk)
|
|
63
|
+
if not cover_path.exists():
|
|
64
|
+
thumb_image_data = CoverCreateMixin.create_cover_from_path(
|
|
65
|
+
pk, cover_path, LOG, LIBRARIAN_QUEUE
|
|
66
|
+
)
|
|
67
|
+
if not thumb_image_data:
|
|
68
|
+
cover_path, content_type = self._get_missing_cover_path()
|
|
69
|
+
elif cover_path.stat().st_size == 0:
|
|
70
|
+
cover_path, content_type = self._get_missing_cover_path()
|
|
71
|
+
|
|
72
|
+
if not thumb_image_data:
|
|
73
|
+
# if not thumb_image_data:
|
|
74
|
+
with cover_path.open("rb") as f:
|
|
75
|
+
thumb_image_data = f.read()
|
|
76
|
+
|
|
77
|
+
return HttpResponse(thumb_image_data, content_type=content_type)
|
codex/views/download.py
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
"""Download a comic archive."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import ClassVar
|
|
4
5
|
|
|
5
6
|
from django.http import FileResponse, Http404
|
|
6
7
|
from drf_spectacular.types import OpenApiTypes
|
|
7
8
|
from drf_spectacular.utils import extend_schema
|
|
9
|
+
from rest_framework.views import APIView
|
|
8
10
|
|
|
9
11
|
from codex.models import Comic
|
|
10
|
-
from codex.views.auth import
|
|
12
|
+
from codex.views.auth import GroupACLMixin, IsAuthenticatedOrEnabledNonUsers
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
class DownloadView(
|
|
15
|
+
class DownloadView(APIView, GroupACLMixin):
|
|
14
16
|
"""Return the comic archive file as an attachment."""
|
|
15
17
|
|
|
18
|
+
permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
|
|
16
19
|
content_type = "application/vnd.comicbook+zip"
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
AS_ATTACHMENT = True
|
|
19
22
|
|
|
20
23
|
@extend_schema(responses={(200, content_type): OpenApiTypes.BINARY})
|
|
21
24
|
def get(self, *_args, **kwargs):
|
|
@@ -45,7 +48,7 @@ class DownloadView(AuthFilterAPIView):
|
|
|
45
48
|
filename = comic.get_filename()
|
|
46
49
|
return FileResponse(
|
|
47
50
|
comic_file,
|
|
48
|
-
as_attachment=self.
|
|
51
|
+
as_attachment=self.AS_ATTACHMENT,
|
|
49
52
|
content_type=content_type,
|
|
50
53
|
filename=filename,
|
|
51
54
|
)
|
|
@@ -54,4 +57,4 @@ class DownloadView(AuthFilterAPIView):
|
|
|
54
57
|
class FileView(DownloadView):
|
|
55
58
|
"""View a comic in the browser."""
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
AS_ATTACHMENT = False
|
codex/views/frontend.py
CHANGED
|
@@ -6,14 +6,12 @@ from rest_framework.renderers import TemplateHTMLRenderer
|
|
|
6
6
|
from rest_framework.response import Response
|
|
7
7
|
|
|
8
8
|
from codex.serializers.route import RouteSerializer
|
|
9
|
-
from codex.views.session import
|
|
9
|
+
from codex.views.session import BrowserSessionViewBase
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class IndexView(
|
|
12
|
+
class IndexView(BrowserSessionViewBase):
|
|
13
13
|
"""The main app."""
|
|
14
14
|
|
|
15
|
-
SESSION_KEY = SessionView.BROWSER_SESSION_KEY
|
|
16
|
-
|
|
17
15
|
renderer_classes: ClassVar[list] = [TemplateHTMLRenderer] # type: ignore
|
|
18
16
|
template_name = "index.html"
|
|
19
17
|
|