codex 1.6.0a7__py3-none-any.whl → 1.6.0a9__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/integrity.py +5 -6
- codex/librarian/importer/{aggregate_metadata.py → aggregate.py} +54 -76
- codex/librarian/importer/cache.py +162 -29
- codex/librarian/importer/const.py +15 -6
- codex/librarian/importer/create_comics.py +101 -17
- codex/librarian/importer/create_covers.py +96 -0
- codex/librarian/importer/create_fks.py +73 -170
- codex/librarian/importer/deleted.py +59 -46
- codex/librarian/importer/{clean_metadata.py → extract.py} +33 -4
- codex/librarian/importer/failed_imports.py +42 -64
- codex/librarian/importer/importer.py +96 -0
- codex/librarian/importer/importerd.py +71 -397
- codex/librarian/importer/init.py +265 -0
- codex/librarian/importer/link_comics.py +26 -81
- codex/librarian/importer/link_covers.py +73 -0
- codex/librarian/importer/moved.py +76 -104
- codex/librarian/importer/query_covers.py +52 -0
- codex/librarian/importer/query_fks.py +128 -172
- codex/librarian/importer/status.py +2 -39
- codex/librarian/importer/tasks.py +22 -13
- codex/librarian/janitor/cleanup.py +2 -2
- codex/librarian/janitor/janitor.py +10 -0
- codex/librarian/librariand.py +7 -1
- codex/migrations/0027_import_order_and_covers.py +345 -0
- codex/models/comic.py +1 -2
- codex/models/groups.py +3 -2
- codex/models/paths.py +0 -1
- codex/search/query.py +25 -2
- codex/search/writing.py +1 -1
- codex/serializers/browser/mixins.py +0 -18
- codex/serializers/browser/mtime.py +21 -0
- codex/serializers/browser/settings.py +2 -3
- codex/serializers/choices.py +10 -1
- codex/serializers/fields.py +8 -9
- codex/serializers/reader.py +5 -5
- codex/serializers/route.py +9 -4
- codex/settings/settings.py +2 -2
- codex/static_root/assets/{VCheckbox-BOUtyxuo.c690f0cdbe48.js → VCheckbox-eNspVUje.da8282a08876.js} +1 -1
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.br +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.gz +0 -0
- codex/static_root/assets/{VCheckbox-BOUtyxuo.js → VCheckbox-eNspVUje.js} +1 -1
- codex/static_root/assets/VCheckbox-eNspVUje.js.br +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-B1-m5pEh.e2b8cbdb92c9.js → VCheckboxBtn-DLzF_WHB.9a47873b8548.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-B1-m5pEh.js → VCheckboxBtn-DLzF_WHB.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.gz +0 -0
- codex/static_root/assets/{VCombobox-DjkDXe33.22617ea193b1.js → VCombobox-BIO7lU_B.d607ada71b06.js} +1 -1
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.br +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.gz +0 -0
- codex/static_root/assets/{VCombobox-DjkDXe33.js → VCombobox-BIO7lU_B.js} +1 -1
- codex/static_root/assets/VCombobox-BIO7lU_B.js.br +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.js.gz +0 -0
- codex/static_root/assets/{VDialog-X0zn9AGX.0d89c749b9a6.js → VDialog-87AymJC5.4a46716358c0.js} +1 -1
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.br +0 -0
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.gz +0 -0
- codex/static_root/assets/{VDialog-X0zn9AGX.js → VDialog-87AymJC5.js} +1 -1
- codex/static_root/assets/VDialog-87AymJC5.js.br +0 -0
- codex/static_root/assets/VDialog-87AymJC5.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-CRevojaF.43d65c777c58.js → VExpansionPanels-CgA3shsd.9e524c73726a.js} +1 -1
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-CRevojaF.js → VExpansionPanels-CgA3shsd.js} +1 -1
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-8nrrxjDv.2b4dfb984a8c.js → VRadioGroup-B--7K_v5.4a4e9c2d6c42.js} +1 -1
- codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-8nrrxjDv.js → VRadioGroup-B--7K_v5.js} +1 -1
- codex/static_root/assets/VRadioGroup-B--7K_v5.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.js.gz +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +1 -0
- codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.css +1 -0
- codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js +1 -0
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.br +0 -0
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.gz +0 -0
- codex/static_root/assets/VSelect-BoJq222l.js +1 -0
- codex/static_root/assets/VSelect-BoJq222l.js.br +0 -0
- codex/static_root/assets/VSelect-BoJq222l.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CGu0oz0K.3030b7a270d6.js → VSelectionControl-CVQ9wkE2.c02186cceba6.js} +1 -1
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CGu0oz0K.js → VSelectionControl-CVQ9wkE2.js} +1 -1
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-D_oNvCOd.71a7101cdddd.js → VSlideGroup-Ce0j2BKK.85e64443a928.js} +1 -1
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.br +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-D_oNvCOd.js → VSlideGroup-Ce0j2BKK.js} +1 -1
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.br +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.gz +0 -0
- codex/static_root/assets/{VTable-C7pKI7gU.087912f706f5.js → VTable-CrWkhkiP.6d530eea0c09.js} +1 -1
- codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.br +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.gz +0 -0
- codex/static_root/assets/{VTable-C7pKI7gU.js → VTable-CrWkhkiP.js} +1 -1
- codex/static_root/assets/VTable-CrWkhkiP.js.br +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.js.gz +0 -0
- codex/static_root/assets/{VTextField-Bs8oq9mk.js → VTextField-CkWbil3K.2cac13161dfe.js} +1 -1
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.br +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.gz +0 -0
- codex/static_root/assets/{VTextField-Bs8oq9mk.7999fef12a03.js → VTextField-CkWbil3K.js} +1 -1
- codex/static_root/assets/VTextField-CkWbil3K.js.br +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-iyZ1XOkP.409f7d292253.js → VWindowItem-7z55Y4lK.98eb7487e039.js} +1 -1
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.br +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-iyZ1XOkP.js → VWindowItem-7z55Y4lK.js} +1 -1
- codex/static_root/assets/VWindowItem-7z55Y4lK.js.br +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.js.gz +0 -0
- codex/static_root/assets/{admin-B0pCBjma.2407da79bd20.js → admin-D3OK784R.f69891726ec5.js} +1 -1
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.br +0 -0
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.gz +0 -0
- codex/static_root/assets/{admin-B0pCBjma.js → admin-D3OK784R.js} +1 -1
- codex/static_root/assets/admin-D3OK784R.js.br +0 -0
- codex/static_root/assets/admin-D3OK784R.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BxoNUHQr.5e2e1a4a255d.js → admin-drawer-panel-1pnfvY8O.5438f252970e.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BxoNUHQr.js → admin-drawer-panel-1pnfvY8O.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.gz +0 -0
- codex/static_root/assets/browser-BuU2x9y7.css +1 -0
- codex/static_root/assets/browser-BuU2x9y7.css.br +0 -0
- codex/static_root/assets/browser-BuU2x9y7.css.gz +0 -0
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css +1 -0
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.br +0 -0
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.gz +0 -0
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js +1 -0
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.br +0 -0
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.gz +0 -0
- codex/static_root/assets/browser-CO9EYHIr.js +1 -0
- codex/static_root/assets/browser-CO9EYHIr.js.br +0 -0
- codex/static_root/assets/browser-CO9EYHIr.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-DZ9dP8_F.930f4cae3cb0.js → change-password-dialog-CjZdzaIk.188c765431ab.js} +1 -1
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-DZ9dP8_F.js → change-password-dialog-CjZdzaIk.js} +1 -1
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-RP7JNStF.a752a7c1e697.js → confirm-dialog-jwL7B98r.1cfd579cbcfe.js} +1 -1
- codex/static_root/assets/confirm-dialog-jwL7B98r.1cfd579cbcfe.js.br +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.1cfd579cbcfe.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-RP7JNStF.js → confirm-dialog-jwL7B98r.js} +1 -1
- codex/static_root/assets/confirm-dialog-jwL7B98r.js.br +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.js.gz +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js +1 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.br +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.gz +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.js +1 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.js.br +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.js.gz +0 -0
- codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +1 -0
- codex/static_root/assets/datetime-column-DeCthByU.css +1 -0
- codex/static_root/assets/{filter-DAdGUt-1.3261cbcd50b5.js → filter-C-pghOri.eeb7c2ecf398.js} +1 -1
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.br +0 -0
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.gz +0 -0
- codex/static_root/assets/{filter-DAdGUt-1.js → filter-C-pghOri.js} +1 -1
- codex/static_root/assets/filter-C-pghOri.js.br +0 -0
- codex/static_root/assets/filter-C-pghOri.js.gz +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js +1 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.br +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.gz +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.js +1 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.js.br +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.js.gz +0 -0
- codex/static_root/assets/{group-tab-DEwh4jfB.a95097b5e320.js → group-tab-BONYWOc1.6ddf321a4a56.js} +1 -1
- codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.br +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.gz +0 -0
- codex/static_root/assets/{group-tab-DEwh4jfB.js → group-tab-BONYWOc1.js} +1 -1
- codex/static_root/assets/group-tab-BONYWOc1.js.br +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.js.gz +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js +1 -0
- codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.br +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.gz +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.js +1 -0
- codex/static_root/assets/http-error-D2Jnc20C.js.br +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.js.gz +0 -0
- codex/static_root/assets/{library-tab-DrXvD9B2.js → library-tab-B419agXA.d4b778172aaa.js} +1 -1
- codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.br +0 -0
- codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.gz +0 -0
- codex/static_root/assets/{library-tab-DrXvD9B2.f765f9d3bae4.js → library-tab-B419agXA.js} +1 -1
- codex/static_root/assets/library-tab-B419agXA.js.br +0 -0
- codex/static_root/assets/library-tab-B419agXA.js.gz +0 -0
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js +32 -0
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.br +0 -0
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.gz +0 -0
- codex/static_root/assets/main-CMQwJ9aG.js +32 -0
- codex/static_root/assets/main-CMQwJ9aG.js.br +0 -0
- codex/static_root/assets/main-CMQwJ9aG.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css +1 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css +1 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js +1 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js +1 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-obrBwxa6.ec9bd13a5786.js → pdf-doc-8RVZqILx.2e7166dd26fb.js} +1 -1
- codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.br +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-obrBwxa6.js → pdf-doc-8RVZqILx.js} +1 -1
- codex/static_root/assets/pdf-doc-8RVZqILx.js.br +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.js.gz +0 -0
- codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css +1 -0
- codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.br +0 -0
- codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.gz +0 -0
- codex/static_root/assets/reader-CCCfpM3x.css +1 -0
- codex/static_root/assets/reader-CCCfpM3x.css.br +0 -0
- codex/static_root/assets/reader-CCCfpM3x.css.gz +0 -0
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js +2 -0
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.br +0 -0
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.gz +0 -0
- codex/static_root/assets/reader-DNnK_Chr.js +2 -0
- codex/static_root/assets/reader-DNnK_Chr.js.br +0 -0
- codex/static_root/assets/reader-DNnK_Chr.js.gz +0 -0
- codex/static_root/assets/{relation-chips-CZJcLjc8.1d7426dab654.js → relation-chips-PYua7aXv.428ba0b5861e.js} +1 -1
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.br +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.gz +0 -0
- codex/static_root/assets/{relation-chips-CZJcLjc8.js → relation-chips-PYua7aXv.js} +1 -1
- codex/static_root/assets/relation-chips-PYua7aXv.js.br +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-7iTnAdjf.337eecf2fdbb.js → settings-drawer-DbN6ISRH.d14f525d8eee.js} +2 -2
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.br +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-7iTnAdjf.js → settings-drawer-DbN6ISRH.js} +2 -2
- codex/static_root/assets/settings-drawer-DbN6ISRH.js.br +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CQsK1n8O.544bf50ffb22.js → stats-tab-DImNhH8O.d5026283841a.js} +1 -1
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.br +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CQsK1n8O.js → stats-tab-DImNhH8O.js} +1 -1
- codex/static_root/assets/stats-tab-DImNhH8O.js.br +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.js.gz +0 -0
- codex/static_root/assets/{task-tab-C60TLP6e.63aa929a4ed6.js → task-tab-DPD53kjv.e56a35ff1691.js} +1 -1
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.br +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.gz +0 -0
- codex/static_root/assets/{task-tab-C60TLP6e.js → task-tab-DPD53kjv.js} +1 -1
- codex/static_root/assets/task-tab-DPD53kjv.js.br +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.js.gz +0 -0
- codex/static_root/assets/to-case-ehC9Ccyj.a7b8e25b391f.js +1 -0
- codex/static_root/assets/to-case-ehC9Ccyj.js +1 -0
- codex/static_root/assets/{unauthorized-Bs7NSqea.c55650fac055.js → unauthorized-BOIx7Y18.ee3961abfccb.js} +1 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.br +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.gz +0 -0
- codex/static_root/assets/{unauthorized-Bs7NSqea.js → unauthorized-BOIx7Y18.js} +1 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.js.br +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.js.gz +0 -0
- codex/static_root/assets/{user-tab-ChuH7Atm.7e80cf1fb78e.js → user-tab-BABwfxLO.37dd0440fbdd.js} +1 -1
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.br +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.gz +0 -0
- codex/static_root/assets/{user-tab-ChuH7Atm.js → user-tab-BABwfxLO.js} +1 -1
- codex/static_root/assets/user-tab-BABwfxLO.js.br +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.js.gz +0 -0
- codex/static_root/js/choices-admin.d790df01c20a.json +1 -0
- codex/static_root/js/choices-admin.d790df01c20a.json.br +0 -0
- codex/static_root/js/choices-admin.d790df01c20a.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.079a01e0be1a.json → choices.a0aaaa6c7ef7.json} +1 -1
- codex/static_root/js/choices.a0aaaa6c7ef7.json.br +0 -0
- codex/static_root/js/choices.a0aaaa6c7ef7.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.dad79c8475ae.json → manifest.cbea5663003a.json} +239 -239
- codex/static_root/manifest.cbea5663003a.json.br +0 -0
- codex/static_root/manifest.cbea5663003a.json.gz +0 -0
- codex/static_root/manifest.json +239 -239
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/urls/api/browser.py +11 -2
- codex/urls/api/reader.py +1 -7
- codex/urls/api/v3.py +1 -1
- codex/urls/opds/binary.py +1 -1
- codex/views/admin/auth.py +0 -2
- codex/views/admin/tasks.py +10 -1
- codex/views/auth.py +2 -4
- codex/views/bookmark.py +44 -40
- codex/views/browser/annotations.py +38 -174
- codex/views/browser/base.py +6 -22
- codex/views/browser/breadcrumbs.py +16 -14
- codex/views/browser/browser.py +11 -20
- codex/views/browser/choices.py +15 -39
- codex/views/browser/cover.py +177 -0
- codex/views/browser/filters/annotations.py +63 -18
- codex/views/browser/filters/bookmark.py +21 -5
- codex/views/browser/filters/field.py +1 -2
- codex/views/browser/filters/group.py +19 -11
- codex/views/browser/filters/search.py +19 -10
- codex/views/browser/metadata.py +20 -25
- codex/views/browser/mtime.py +72 -0
- codex/views/const.py +20 -0
- codex/views/opds/binary.py +1 -1
- codex/views/opds/urls.py +2 -1
- codex/views/opds/util.py +2 -4
- codex/views/opds/v1/entry/links.py +18 -33
- codex/views/opds/v1/facets.py +2 -1
- codex/views/opds/v1/feed.py +42 -23
- codex/views/opds/v1/links.py +2 -1
- codex/views/opds/v2/feed.py +11 -9
- codex/views/opds/v2/links.py +2 -1
- codex/views/opds/v2/publications.py +13 -34
- codex/views/public.py +0 -2
- codex/views/reader/books.py +7 -9
- codex/views/reader/reader.py +25 -22
- codex/views/session.py +5 -3
- codex/views/util.py +32 -0
- {codex-1.6.0a7.dist-info → codex-1.6.0a9.dist-info}/METADATA +8 -7
- {codex-1.6.0a7.dist-info → codex-1.6.0a9.dist-info}/RECORD +324 -325
- codex/librarian/importer/update_comics.py +0 -85
- codex/migrations/0027_sort_name.py +0 -139
- codex/migrations/0028_custom_covers.py +0 -166
- codex/migrations/0029_choices_adminflag_and_timestamp.py +0 -44
- codex/serializers/mtime.py +0 -25
- codex/static_root/assets/VCheckbox-BOUtyxuo.c690f0cdbe48.js.br +0 -0
- codex/static_root/assets/VCheckbox-BOUtyxuo.c690f0cdbe48.js.gz +0 -0
- codex/static_root/assets/VCheckbox-BOUtyxuo.js.br +0 -0
- codex/static_root/assets/VCheckbox-BOUtyxuo.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-B1-m5pEh.e2b8cbdb92c9.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B1-m5pEh.e2b8cbdb92c9.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-B1-m5pEh.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B1-m5pEh.js.gz +0 -0
- codex/static_root/assets/VCombobox-DjkDXe33.22617ea193b1.js.br +0 -0
- codex/static_root/assets/VCombobox-DjkDXe33.22617ea193b1.js.gz +0 -0
- codex/static_root/assets/VCombobox-DjkDXe33.js.br +0 -0
- codex/static_root/assets/VCombobox-DjkDXe33.js.gz +0 -0
- codex/static_root/assets/VDialog-X0zn9AGX.0d89c749b9a6.js.br +0 -0
- codex/static_root/assets/VDialog-X0zn9AGX.0d89c749b9a6.js.gz +0 -0
- codex/static_root/assets/VDialog-X0zn9AGX.js.br +0 -0
- codex/static_root/assets/VDialog-X0zn9AGX.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CRevojaF.43d65c777c58.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CRevojaF.43d65c777c58.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CRevojaF.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CRevojaF.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-8nrrxjDv.2b4dfb984a8c.js.br +0 -0
- codex/static_root/assets/VRadioGroup-8nrrxjDv.2b4dfb984a8c.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-8nrrxjDv.js.br +0 -0
- codex/static_root/assets/VRadioGroup-8nrrxjDv.js.gz +0 -0
- codex/static_root/assets/VSelect-DgfIKRnC.fa7a73a9c415.js +0 -1
- codex/static_root/assets/VSelect-DgfIKRnC.fa7a73a9c415.js.br +0 -0
- codex/static_root/assets/VSelect-DgfIKRnC.fa7a73a9c415.js.gz +0 -0
- codex/static_root/assets/VSelect-DgfIKRnC.js +0 -1
- codex/static_root/assets/VSelect-DgfIKRnC.js.br +0 -0
- codex/static_root/assets/VSelect-DgfIKRnC.js.gz +0 -0
- codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css +0 -1
- codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.br +0 -0
- codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.gz +0 -0
- codex/static_root/assets/VSelect-MGVSeLgr.css +0 -1
- codex/static_root/assets/VSelect-MGVSeLgr.css.br +0 -0
- codex/static_root/assets/VSelect-MGVSeLgr.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-CGu0oz0K.3030b7a270d6.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CGu0oz0K.3030b7a270d6.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CGu0oz0K.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CGu0oz0K.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D_oNvCOd.71a7101cdddd.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D_oNvCOd.71a7101cdddd.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D_oNvCOd.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D_oNvCOd.js.gz +0 -0
- codex/static_root/assets/VTable-C7pKI7gU.087912f706f5.js.br +0 -0
- codex/static_root/assets/VTable-C7pKI7gU.087912f706f5.js.gz +0 -0
- codex/static_root/assets/VTable-C7pKI7gU.js.br +0 -0
- codex/static_root/assets/VTable-C7pKI7gU.js.gz +0 -0
- codex/static_root/assets/VTextField-Bs8oq9mk.7999fef12a03.js.br +0 -0
- codex/static_root/assets/VTextField-Bs8oq9mk.7999fef12a03.js.gz +0 -0
- codex/static_root/assets/VTextField-Bs8oq9mk.js.br +0 -0
- codex/static_root/assets/VTextField-Bs8oq9mk.js.gz +0 -0
- codex/static_root/assets/VWindowItem-iyZ1XOkP.409f7d292253.js.br +0 -0
- codex/static_root/assets/VWindowItem-iyZ1XOkP.409f7d292253.js.gz +0 -0
- codex/static_root/assets/VWindowItem-iyZ1XOkP.js.br +0 -0
- codex/static_root/assets/VWindowItem-iyZ1XOkP.js.gz +0 -0
- codex/static_root/assets/admin-B0pCBjma.2407da79bd20.js.br +0 -0
- codex/static_root/assets/admin-B0pCBjma.2407da79bd20.js.gz +0 -0
- codex/static_root/assets/admin-B0pCBjma.js.br +0 -0
- codex/static_root/assets/admin-B0pCBjma.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BxoNUHQr.5e2e1a4a255d.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BxoNUHQr.5e2e1a4a255d.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BxoNUHQr.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BxoNUHQr.js.gz +0 -0
- codex/static_root/assets/browser-DbyAjCNh.577a228a966b.js +0 -1
- codex/static_root/assets/browser-DbyAjCNh.577a228a966b.js.br +0 -0
- codex/static_root/assets/browser-DbyAjCNh.577a228a966b.js.gz +0 -0
- codex/static_root/assets/browser-DbyAjCNh.js +0 -1
- codex/static_root/assets/browser-DbyAjCNh.js.br +0 -0
- codex/static_root/assets/browser-DbyAjCNh.js.gz +0 -0
- codex/static_root/assets/browser-VnzNj1t3.cff861ad2ca3.css +0 -1
- codex/static_root/assets/browser-VnzNj1t3.cff861ad2ca3.css.br +0 -0
- codex/static_root/assets/browser-VnzNj1t3.cff861ad2ca3.css.gz +0 -0
- codex/static_root/assets/browser-VnzNj1t3.css +0 -1
- codex/static_root/assets/browser-VnzNj1t3.css.br +0 -0
- codex/static_root/assets/browser-VnzNj1t3.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-DZ9dP8_F.930f4cae3cb0.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DZ9dP8_F.930f4cae3cb0.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DZ9dP8_F.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DZ9dP8_F.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-RP7JNStF.a752a7c1e697.js.br +0 -0
- codex/static_root/assets/confirm-dialog-RP7JNStF.a752a7c1e697.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-RP7JNStF.js.br +0 -0
- codex/static_root/assets/confirm-dialog-RP7JNStF.js.gz +0 -0
- codex/static_root/assets/datetime-column-Cu3WYyPj.54b4c3817b74.js +0 -1
- codex/static_root/assets/datetime-column-Cu3WYyPj.54b4c3817b74.js.br +0 -0
- codex/static_root/assets/datetime-column-Cu3WYyPj.54b4c3817b74.js.gz +0 -0
- codex/static_root/assets/datetime-column-Cu3WYyPj.js +0 -1
- codex/static_root/assets/datetime-column-Cu3WYyPj.js.br +0 -0
- codex/static_root/assets/datetime-column-Cu3WYyPj.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +0 -1
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.css +0 -1
- codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
- codex/static_root/assets/filter-DAdGUt-1.3261cbcd50b5.js.br +0 -0
- codex/static_root/assets/filter-DAdGUt-1.3261cbcd50b5.js.gz +0 -0
- codex/static_root/assets/filter-DAdGUt-1.js.br +0 -0
- codex/static_root/assets/filter-DAdGUt-1.js.gz +0 -0
- codex/static_root/assets/flag-tab-BP7Zs7iQ.cb16ef6363d4.js +0 -1
- codex/static_root/assets/flag-tab-BP7Zs7iQ.cb16ef6363d4.js.br +0 -0
- codex/static_root/assets/flag-tab-BP7Zs7iQ.cb16ef6363d4.js.gz +0 -0
- codex/static_root/assets/flag-tab-BP7Zs7iQ.js +0 -1
- codex/static_root/assets/flag-tab-BP7Zs7iQ.js.br +0 -0
- codex/static_root/assets/flag-tab-BP7Zs7iQ.js.gz +0 -0
- codex/static_root/assets/group-tab-DEwh4jfB.a95097b5e320.js.br +0 -0
- codex/static_root/assets/group-tab-DEwh4jfB.a95097b5e320.js.gz +0 -0
- codex/static_root/assets/group-tab-DEwh4jfB.js.br +0 -0
- codex/static_root/assets/group-tab-DEwh4jfB.js.gz +0 -0
- codex/static_root/assets/http-error-xs3mIL8U.e143c472a96c.js +0 -1
- codex/static_root/assets/http-error-xs3mIL8U.e143c472a96c.js.br +0 -0
- codex/static_root/assets/http-error-xs3mIL8U.e143c472a96c.js.gz +0 -0
- codex/static_root/assets/http-error-xs3mIL8U.js +0 -1
- codex/static_root/assets/http-error-xs3mIL8U.js.br +0 -0
- codex/static_root/assets/http-error-xs3mIL8U.js.gz +0 -0
- codex/static_root/assets/library-tab-DrXvD9B2.f765f9d3bae4.js.br +0 -0
- codex/static_root/assets/library-tab-DrXvD9B2.f765f9d3bae4.js.gz +0 -0
- codex/static_root/assets/library-tab-DrXvD9B2.js.br +0 -0
- codex/static_root/assets/library-tab-DrXvD9B2.js.gz +0 -0
- codex/static_root/assets/main-Dy9Uqpmh.2f99b1914b3e.js +0 -32
- codex/static_root/assets/main-Dy9Uqpmh.2f99b1914b3e.js.br +0 -0
- codex/static_root/assets/main-Dy9Uqpmh.2f99b1914b3e.js.gz +0 -0
- codex/static_root/assets/main-Dy9Uqpmh.js +0 -32
- codex/static_root/assets/main-Dy9Uqpmh.js.br +0 -0
- codex/static_root/assets/main-Dy9Uqpmh.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DbS_HOT1.81222ab51ef4.js +0 -1
- codex/static_root/assets/pagination-toolbar-DbS_HOT1.81222ab51ef4.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-DbS_HOT1.81222ab51ef4.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DbS_HOT1.js +0 -1
- codex/static_root/assets/pagination-toolbar-DbS_HOT1.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-DbS_HOT1.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-r6nbtWxg.4088ab042c0c.css +0 -1
- codex/static_root/assets/pagination-toolbar-r6nbtWxg.4088ab042c0c.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-r6nbtWxg.4088ab042c0c.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-r6nbtWxg.css +0 -1
- codex/static_root/assets/pagination-toolbar-r6nbtWxg.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-r6nbtWxg.css.gz +0 -0
- codex/static_root/assets/pdf-doc-obrBwxa6.ec9bd13a5786.js.br +0 -0
- codex/static_root/assets/pdf-doc-obrBwxa6.ec9bd13a5786.js.gz +0 -0
- codex/static_root/assets/pdf-doc-obrBwxa6.js.br +0 -0
- codex/static_root/assets/pdf-doc-obrBwxa6.js.gz +0 -0
- codex/static_root/assets/reader-BXKUaUeC.953d30b969ef.js +0 -2
- codex/static_root/assets/reader-BXKUaUeC.953d30b969ef.js.br +0 -0
- codex/static_root/assets/reader-BXKUaUeC.953d30b969ef.js.gz +0 -0
- codex/static_root/assets/reader-BXKUaUeC.js +0 -2
- codex/static_root/assets/reader-BXKUaUeC.js.br +0 -0
- codex/static_root/assets/reader-BXKUaUeC.js.gz +0 -0
- codex/static_root/assets/reader-OXJ1KVaW.a463b19d0508.css +0 -1
- codex/static_root/assets/reader-OXJ1KVaW.a463b19d0508.css.br +0 -0
- codex/static_root/assets/reader-OXJ1KVaW.a463b19d0508.css.gz +0 -0
- codex/static_root/assets/reader-OXJ1KVaW.css +0 -1
- codex/static_root/assets/reader-OXJ1KVaW.css.br +0 -0
- codex/static_root/assets/reader-OXJ1KVaW.css.gz +0 -0
- codex/static_root/assets/relation-chips-CZJcLjc8.1d7426dab654.js.br +0 -0
- codex/static_root/assets/relation-chips-CZJcLjc8.1d7426dab654.js.gz +0 -0
- codex/static_root/assets/relation-chips-CZJcLjc8.js.br +0 -0
- codex/static_root/assets/relation-chips-CZJcLjc8.js.gz +0 -0
- codex/static_root/assets/settings-drawer-7iTnAdjf.337eecf2fdbb.js.br +0 -0
- codex/static_root/assets/settings-drawer-7iTnAdjf.337eecf2fdbb.js.gz +0 -0
- codex/static_root/assets/settings-drawer-7iTnAdjf.js.br +0 -0
- codex/static_root/assets/settings-drawer-7iTnAdjf.js.gz +0 -0
- codex/static_root/assets/stats-tab-CQsK1n8O.544bf50ffb22.js.br +0 -0
- codex/static_root/assets/stats-tab-CQsK1n8O.544bf50ffb22.js.gz +0 -0
- codex/static_root/assets/stats-tab-CQsK1n8O.js.br +0 -0
- codex/static_root/assets/stats-tab-CQsK1n8O.js.gz +0 -0
- codex/static_root/assets/task-tab-C60TLP6e.63aa929a4ed6.js.br +0 -0
- codex/static_root/assets/task-tab-C60TLP6e.63aa929a4ed6.js.gz +0 -0
- codex/static_root/assets/task-tab-C60TLP6e.js.br +0 -0
- codex/static_root/assets/task-tab-C60TLP6e.js.gz +0 -0
- codex/static_root/assets/to-case-CR9beRR0.21bb805fdab4.js +0 -1
- codex/static_root/assets/to-case-CR9beRR0.js +0 -1
- codex/static_root/assets/unauthorized-Bs7NSqea.c55650fac055.js.br +0 -0
- codex/static_root/assets/unauthorized-Bs7NSqea.c55650fac055.js.gz +0 -0
- codex/static_root/assets/unauthorized-Bs7NSqea.js.br +0 -0
- codex/static_root/assets/unauthorized-Bs7NSqea.js.gz +0 -0
- codex/static_root/assets/user-tab-ChuH7Atm.7e80cf1fb78e.js.br +0 -0
- codex/static_root/assets/user-tab-ChuH7Atm.7e80cf1fb78e.js.gz +0 -0
- codex/static_root/assets/user-tab-ChuH7Atm.js.br +0 -0
- codex/static_root/assets/user-tab-ChuH7Atm.js.gz +0 -0
- codex/static_root/js/choices-admin.ef1ff3a8b9da.json +0 -1
- codex/static_root/js/choices-admin.ef1ff3a8b9da.json.br +0 -0
- codex/static_root/js/choices-admin.ef1ff3a8b9da.json.gz +0 -0
- codex/static_root/js/choices.079a01e0be1a.json.br +0 -0
- codex/static_root/js/choices.079a01e0be1a.json.gz +0 -0
- codex/static_root/manifest.dad79c8475ae.json.br +0 -0
- codex/static_root/manifest.dad79c8475ae.json.gz +0 -0
- codex/views/cover.py +0 -76
- codex/views/mtime.py +0 -70
- codex/views/utils.py +0 -26
- {codex-1.6.0a7.dist-info → codex-1.6.0a9.dist-info}/LICENSE +0 -0
- {codex-1.6.0a7.dist-info → codex-1.6.0a9.dist-info}/WHEEL +0 -0
- {codex-1.6.0a7.dist-info → codex-1.6.0a9.dist-info}/entry_points.txt +0 -0
codex/views/browser/metadata.py
CHANGED
|
@@ -8,7 +8,9 @@ from drf_spectacular.utils import extend_schema
|
|
|
8
8
|
from rest_framework.exceptions import NotFound
|
|
9
9
|
from rest_framework.response import Response
|
|
10
10
|
|
|
11
|
-
from codex.librarian.importer.const import
|
|
11
|
+
from codex.librarian.importer.const import (
|
|
12
|
+
COMIC_M2M_FIELD_NAMES,
|
|
13
|
+
)
|
|
12
14
|
from codex.logger.logging import get_logger
|
|
13
15
|
from codex.models import AdminFlag, Comic
|
|
14
16
|
from codex.serializers.browser.metadata import MetadataSerializer
|
|
@@ -52,6 +54,11 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
52
54
|
}
|
|
53
55
|
)
|
|
54
56
|
|
|
57
|
+
def set_valid_browse_nav_groups(self, valid_top_groups): # noqa: ARG002
|
|
58
|
+
"""Limited allowed nav groups for metadata."""
|
|
59
|
+
group = self.kwargs["group"]
|
|
60
|
+
self.valid_nav_groups = (group,)
|
|
61
|
+
|
|
55
62
|
def _get_comic_value_fields(self):
|
|
56
63
|
"""Include the path field for staff."""
|
|
57
64
|
fields = set(_COMIC_VALUE_FIELD_NAMES)
|
|
@@ -153,6 +160,8 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
153
160
|
if field_name in _GROUP_RELS:
|
|
154
161
|
group_by = "name" if field_name == "volume" else "sort_name"
|
|
155
162
|
intersection_qs = intersection_qs.group_by(group_by)
|
|
163
|
+
if field_name == "identifiers":
|
|
164
|
+
intersection_qs = intersection_qs.select_related("identifier_type")
|
|
156
165
|
intersection_qs = intersection_qs.alias(count=Count("comic")).filter(
|
|
157
166
|
count=comic_pks_count
|
|
158
167
|
)
|
|
@@ -185,18 +194,18 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
185
194
|
def _get_optimized_m2m_query(key, qs):
|
|
186
195
|
# XXX The prefetch gets removed by field.set() :(
|
|
187
196
|
if key == "contributors":
|
|
188
|
-
|
|
197
|
+
qs = qs.prefetch_related(*_CONTRIBUTOR_RELATIONS).only(
|
|
189
198
|
*_CONTRIBUTOR_RELATIONS
|
|
190
199
|
)
|
|
191
200
|
elif key == "story_arc_numbers":
|
|
192
|
-
|
|
201
|
+
qs = qs.prefetch_related("story_arc")
|
|
202
|
+
qs = qs.only("story_arc", "number")
|
|
193
203
|
elif key == "identifiers":
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
)
|
|
204
|
+
qs = qs.prefetch_related("identifier_type")
|
|
205
|
+
qs = qs.only("identifier_type", "nss", "url")
|
|
197
206
|
else:
|
|
198
|
-
|
|
199
|
-
return
|
|
207
|
+
qs = qs.only("name")
|
|
208
|
+
return qs
|
|
200
209
|
|
|
201
210
|
@classmethod
|
|
202
211
|
def _copy_m2m_intersections(cls, obj, m2m_intersections):
|
|
@@ -244,26 +253,17 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
244
253
|
# values dicts don't copy relations to the serializer. The values
|
|
245
254
|
# dict is necessary because of the folders view union in browser.py.
|
|
246
255
|
|
|
247
|
-
|
|
248
|
-
# TODO this looks redundant after set_browse_model
|
|
249
|
-
group = self.kwargs["group"]
|
|
250
|
-
raise NotFound(detail=f"Cannot get metadata for {group=}")
|
|
256
|
+
qs = self.get_filtered_queryset(self.model)
|
|
251
257
|
|
|
252
|
-
object_filter = self.get_query_filters_without_group(self.model) # type: ignore
|
|
253
|
-
pks = self.kwargs["pks"]
|
|
254
|
-
qs = self.model.objects.filter(object_filter, pk__in=pks)
|
|
255
|
-
qs = self.filter_by_annotations(qs, self.model, binary=True)
|
|
256
258
|
filtered_qs = qs
|
|
257
259
|
qs = self.annotate_order_aggregates(qs, self.model)
|
|
258
260
|
qs = self.annotate_card_aggregates(qs, self.model)
|
|
261
|
+
qs = self._annotate_values_and_fks(qs, filtered_qs)
|
|
259
262
|
group_by = self.get_group_by()
|
|
260
263
|
qs = qs.group_by(group_by)
|
|
261
|
-
qs = self._annotate_values_and_fks(qs, filtered_qs)
|
|
262
|
-
|
|
263
|
-
qs_list = self.re_cover_multi_groups(qs)
|
|
264
264
|
|
|
265
265
|
try:
|
|
266
|
-
obj =
|
|
266
|
+
obj = qs[0]
|
|
267
267
|
if not obj:
|
|
268
268
|
reason = "Empty obj"
|
|
269
269
|
raise ValueError(reason) # noqa TRY301
|
|
@@ -273,11 +273,6 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
273
273
|
m2m_intersections = self._query_m2m_intersections(filtered_qs)
|
|
274
274
|
return self._copy_annotations_into_comic_fields(obj, m2m_intersections) # type: ignore
|
|
275
275
|
|
|
276
|
-
def set_valid_browse_nav_groups(self, valid_top_groups): # noqa: ARG002
|
|
277
|
-
"""Limited allowed nav groups for metadata."""
|
|
278
|
-
group = self.kwargs["group"]
|
|
279
|
-
self.valid_nav_groups = (group,)
|
|
280
|
-
|
|
281
276
|
@extend_schema(request=BrowserAnnotationsView.input_serializer_class)
|
|
282
277
|
def get(self, *_args, **_kwargs):
|
|
283
278
|
"""Get metadata for a filtered browse group."""
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Get the mtimes for the submitted groups."""
|
|
2
|
+
|
|
3
|
+
from types import MappingProxyType
|
|
4
|
+
|
|
5
|
+
from rest_framework.response import Response
|
|
6
|
+
|
|
7
|
+
from codex.logger.logging import get_logger
|
|
8
|
+
from codex.models.groups import Publisher
|
|
9
|
+
from codex.serializers.browser.mtime import GroupsMtimeSerializer, MtimeSerializer
|
|
10
|
+
from codex.util import max_none
|
|
11
|
+
from codex.views.browser.filters.annotations import BrowserAnnotationsFilterView
|
|
12
|
+
from codex.views.const import GROUP_MODEL_MAP
|
|
13
|
+
|
|
14
|
+
LOG = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MtimeView(BrowserAnnotationsFilterView):
|
|
18
|
+
"""Get the mtimes for the submitted groups."""
|
|
19
|
+
|
|
20
|
+
input_serializer_class = GroupsMtimeSerializer
|
|
21
|
+
serializer_class = MtimeSerializer
|
|
22
|
+
|
|
23
|
+
REPARSE_JSON_FIELDS = frozenset({"groups", "filters"})
|
|
24
|
+
|
|
25
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
26
|
+
"""Initialize."""
|
|
27
|
+
super().__init__(*args, **kwargs)
|
|
28
|
+
self.init_bookmark_data()
|
|
29
|
+
|
|
30
|
+
def parse_params(self):
|
|
31
|
+
"""Parse GET params."""
|
|
32
|
+
try:
|
|
33
|
+
validated_data = super().parse_params()
|
|
34
|
+
params = dict(self.params)
|
|
35
|
+
params["groups"] = validated_data["groups"] # type: ignore
|
|
36
|
+
self.params = MappingProxyType(params)
|
|
37
|
+
|
|
38
|
+
except Exception:
|
|
39
|
+
LOG.exception("parse")
|
|
40
|
+
raise
|
|
41
|
+
|
|
42
|
+
def _get_group_mtime(self, item):
|
|
43
|
+
"""Get one group's mtimes."""
|
|
44
|
+
group = item["group"]
|
|
45
|
+
pks = item["pks"]
|
|
46
|
+
|
|
47
|
+
model = GROUP_MODEL_MAP[group]
|
|
48
|
+
if not model:
|
|
49
|
+
model = Publisher
|
|
50
|
+
|
|
51
|
+
return self.get_group_mtime(model, group, pks)
|
|
52
|
+
|
|
53
|
+
def get_max_groups_mtime(self):
|
|
54
|
+
"""Get max mtime for all groups."""
|
|
55
|
+
max_mtime = None
|
|
56
|
+
|
|
57
|
+
for item in self.params["groups"]: # type: ignore
|
|
58
|
+
mtime = self._get_group_mtime(item)
|
|
59
|
+
max_mtime = max_none(max_mtime, mtime)
|
|
60
|
+
return max_mtime
|
|
61
|
+
|
|
62
|
+
def get(self, *args, **kwargs):
|
|
63
|
+
"""Get the mtimes for the submitted groups."""
|
|
64
|
+
# Parse Request
|
|
65
|
+
self.parse_params()
|
|
66
|
+
|
|
67
|
+
max_mtime = self.get_max_groups_mtime()
|
|
68
|
+
|
|
69
|
+
# Serialize Response
|
|
70
|
+
result = {"max_mtime": max_mtime}
|
|
71
|
+
serializer = self.get_serializer(result)
|
|
72
|
+
return Response(serializer.data)
|
codex/views/const.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"""Common view constants."""
|
|
2
2
|
|
|
3
|
+
from datetime import datetime, timezone
|
|
3
4
|
from types import MappingProxyType
|
|
4
5
|
|
|
5
6
|
from django.contrib.auth.models import Group, User
|
|
6
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
|
|
7
10
|
|
|
8
11
|
from codex.models import (
|
|
9
12
|
AgeRating,
|
|
@@ -64,6 +67,18 @@ GROUP_RELATION = MappingProxyType(
|
|
|
64
67
|
STORY_ARC_GROUP: "story_arc_numbers__story_arc",
|
|
65
68
|
}
|
|
66
69
|
)
|
|
70
|
+
GROUP_ORDER = "rpisv"
|
|
71
|
+
MODEL_REL_MAP = MappingProxyType(
|
|
72
|
+
{
|
|
73
|
+
Publisher: "publisher",
|
|
74
|
+
Imprint: "imprint",
|
|
75
|
+
Series: "series",
|
|
76
|
+
Volume: "volume",
|
|
77
|
+
Folder: "parent_folder",
|
|
78
|
+
StoryArc: "story_arc_numbers__story_arc",
|
|
79
|
+
Comic: "pk",
|
|
80
|
+
}
|
|
81
|
+
)
|
|
67
82
|
GROUP_MODEL_MAP: MappingProxyType[str, type[BrowserGroupModel] | None] = (
|
|
68
83
|
MappingProxyType(
|
|
69
84
|
{
|
|
@@ -115,3 +130,8 @@ CONFIG_MODELS = (
|
|
|
115
130
|
)
|
|
116
131
|
MAX_OBJ_PER_PAGE = 100
|
|
117
132
|
GROUP_MTIME_MODEL_MAP = MappingProxyType({"r": Publisher, "a": StoryArc, "f": Folder})
|
|
133
|
+
EPOCH_START = datetime.fromtimestamp(0, tz=timezone.utc)
|
|
134
|
+
ONE_INTEGERFIELD = Value(1, PositiveSmallIntegerField())
|
|
135
|
+
NONE_INTEGERFIELD = Value(None, PositiveSmallIntegerField())
|
|
136
|
+
NONE_DATETIMEFIELD = Value(None, DateTimeField())
|
|
137
|
+
EPOCH_START_DATETIMEFILED = Value(EPOCH_START)
|
codex/views/opds/binary.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Binary views with Basic Authentication added."""
|
|
2
2
|
|
|
3
|
-
from codex.views.cover import CoverView
|
|
3
|
+
from codex.views.browser.cover import CoverView
|
|
4
4
|
from codex.views.download import DownloadView
|
|
5
5
|
from codex.views.opds.auth import OPDSAuthMixin
|
|
6
6
|
from codex.views.reader.page import ReaderPageView
|
codex/views/opds/urls.py
CHANGED
|
@@ -6,6 +6,7 @@ from rest_framework.response import Response
|
|
|
6
6
|
from codex.serializers.choices import DEFAULTS
|
|
7
7
|
from codex.serializers.opds.urls import OPDSURLsSerializer
|
|
8
8
|
from codex.views.auth import AuthGenericAPIView
|
|
9
|
+
from codex.views.util import pop_name
|
|
9
10
|
|
|
10
11
|
_OPDS_VERSIONS = (1, 2)
|
|
11
12
|
|
|
@@ -19,7 +20,7 @@ class OPDSURLsView(AuthGenericAPIView):
|
|
|
19
20
|
"""Resolve the urls."""
|
|
20
21
|
obj = {}
|
|
21
22
|
route = DEFAULTS["breadcrumbs"][0]
|
|
22
|
-
route
|
|
23
|
+
route = pop_name(route)
|
|
23
24
|
for version in _OPDS_VERSIONS:
|
|
24
25
|
key = f"v{version}"
|
|
25
26
|
name = f"opds:v{version}:feed"
|
codex/views/opds/util.py
CHANGED
|
@@ -8,9 +8,9 @@ from django.utils.http import urlencode
|
|
|
8
8
|
from codex.models import (
|
|
9
9
|
Contributor,
|
|
10
10
|
ContributorPerson,
|
|
11
|
-
StoryArc,
|
|
12
11
|
)
|
|
13
12
|
from codex.serializers.choices import DEFAULTS
|
|
13
|
+
from codex.views.auth import GroupACLMixin
|
|
14
14
|
from codex.views.opds.const import OPDS_M2M_MODELS
|
|
15
15
|
|
|
16
16
|
|
|
@@ -58,9 +58,7 @@ def get_m2m_objects(pks) -> dict:
|
|
|
58
58
|
cats = {}
|
|
59
59
|
for model in OPDS_M2M_MODELS:
|
|
60
60
|
table = model.__name__.lower()
|
|
61
|
-
rel =
|
|
62
|
-
if model == StoryArc:
|
|
63
|
-
rel = "storyarcnumber__" + rel
|
|
61
|
+
rel = GroupACLMixin.get_rel_prefix(model)
|
|
64
62
|
comic_filter = {rel + "__in": pks}
|
|
65
63
|
qs = model.objects.filter(**comic_filter).order_by("name").only("name")
|
|
66
64
|
cats[table] = qs
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""OPDS v1 Entry Links Methods."""
|
|
2
2
|
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from math import floor
|
|
3
5
|
from urllib.parse import quote_plus
|
|
4
6
|
|
|
5
|
-
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
6
7
|
from django.urls import reverse
|
|
7
8
|
|
|
8
9
|
from codex.logger.logging import get_logger
|
|
9
|
-
from codex.views.const import MISSING_COVER_FN, MISSING_COVER_NAME_MAP
|
|
10
10
|
from codex.views.opds.const import MimeType, Rel
|
|
11
11
|
from codex.views.opds.util import update_href_query_params
|
|
12
12
|
from codex.views.opds.v1.data import OPDS1Link
|
|
@@ -31,39 +31,24 @@ class OPDS1EntryLinksMixin:
|
|
|
31
31
|
self.mime_type_map = data.mime_type_map
|
|
32
32
|
self.title_filename_fallback = title_filename_fallback
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def _cover_link(self, rel):
|
|
35
35
|
if self.fake:
|
|
36
36
|
return None
|
|
37
|
-
cover_pk = getattr(self.obj, "cover_pk", None)
|
|
38
|
-
|
|
39
|
-
kwargs = {"
|
|
37
|
+
# cover_pk = getattr(self.obj, "cover_pk", None)
|
|
38
|
+
try:
|
|
39
|
+
kwargs = {"group": self.obj.group, "pks": self.obj.ids}
|
|
40
|
+
ts = floor(datetime.timestamp(self.obj.updated_at))
|
|
41
|
+
query_params = {
|
|
42
|
+
"customCovers": True,
|
|
43
|
+
"dynamicCovers": False,
|
|
44
|
+
"ts": ts,
|
|
45
|
+
}
|
|
40
46
|
href = reverse("opds:bin:cover", kwargs=kwargs)
|
|
47
|
+
href = update_href_query_params(href, query_params)
|
|
41
48
|
mime_type = "image/webp"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if cover_name:
|
|
46
|
-
cover_path += cover_name + ".svg"
|
|
47
|
-
mime_type = "image/svg+xml"
|
|
48
|
-
else:
|
|
49
|
-
cover_path += MISSING_COVER_FN
|
|
50
|
-
mime_type = "image/webp"
|
|
51
|
-
href = staticfiles_storage.url(cover_path)
|
|
52
|
-
return OPDS1Link(Rel.THUMBNAIL, href, mime_type)
|
|
53
|
-
|
|
54
|
-
def _image_link(self):
|
|
55
|
-
if self.fake:
|
|
56
|
-
return None
|
|
57
|
-
cover_pk = getattr(self.obj, "cover_pk", None)
|
|
58
|
-
if cover_pk:
|
|
59
|
-
kwargs = {"pk": cover_pk, "page": 0}
|
|
60
|
-
href = reverse("opds:bin:page", kwargs=kwargs)
|
|
61
|
-
mime_type = "image/jpeg"
|
|
62
|
-
else:
|
|
63
|
-
fn = f"img/{MISSING_COVER_FN}"
|
|
64
|
-
href = staticfiles_storage.url(fn)
|
|
65
|
-
mime_type = "image/webp"
|
|
66
|
-
return OPDS1Link(Rel.IMAGE, href, mime_type)
|
|
49
|
+
return OPDS1Link(rel, href, mime_type)
|
|
50
|
+
except Exception:
|
|
51
|
+
LOG.exception("create thumb")
|
|
67
52
|
|
|
68
53
|
def _nav_href(self, metadata=False):
|
|
69
54
|
try:
|
|
@@ -149,9 +134,9 @@ class OPDS1EntryLinksMixin:
|
|
|
149
134
|
"""Create all entry links."""
|
|
150
135
|
result = []
|
|
151
136
|
try:
|
|
152
|
-
if thumb := self.
|
|
137
|
+
if thumb := self._cover_link(Rel.THUMBNAIL):
|
|
153
138
|
result += [thumb]
|
|
154
|
-
if image := self.
|
|
139
|
+
if image := self._cover_link(Rel.IMAGE):
|
|
155
140
|
result += [image]
|
|
156
141
|
|
|
157
142
|
if self.obj.group == "c" and not self.fake:
|
codex/views/opds/v1/facets.py
CHANGED
|
@@ -12,6 +12,7 @@ from codex.views.opds.util import update_href_query_params
|
|
|
12
12
|
from codex.views.opds.v1.data import OPDS1Link
|
|
13
13
|
from codex.views.opds.v1.entry.data import OPDS1EntryData, OPDS1EntryObject
|
|
14
14
|
from codex.views.opds.v1.entry.entry import OPDS1Entry
|
|
15
|
+
from codex.views.util import pop_name
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@dataclass
|
|
@@ -89,7 +90,7 @@ class OPDS1FacetsView(BrowserView):
|
|
|
89
90
|
obj = MappingProxyType({})
|
|
90
91
|
|
|
91
92
|
def _facet(self, kwargs, facet_group, facet_title, new_query_params):
|
|
92
|
-
kwargs
|
|
93
|
+
kwargs = pop_name(kwargs)
|
|
93
94
|
href = reverse("opds:v1:feed", kwargs=kwargs)
|
|
94
95
|
facet_active = False
|
|
95
96
|
for key, val in new_query_params.items():
|
codex/views/opds/v1/feed.py
CHANGED
|
@@ -41,13 +41,13 @@ class OpdsNs:
|
|
|
41
41
|
ACQUISITION = "http://opds-spec.org/2010/acquisition"
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
class
|
|
44
|
+
class UserAgentNames:
|
|
45
45
|
"""Control whether to hack in facets with nav links."""
|
|
46
46
|
|
|
47
|
-
CLIENT_REORDERS = ("Chunky"
|
|
48
|
-
FACET_SUPPORT = ("yar"
|
|
49
|
-
SIMPLE_DOWNLOAD_MIME_TYPES = ("PocketBook"
|
|
50
|
-
# Other known valid
|
|
47
|
+
CLIENT_REORDERS = frozenset({"Chunky"})
|
|
48
|
+
FACET_SUPPORT = frozenset({"yar"}) # kybooks
|
|
49
|
+
SIMPLE_DOWNLOAD_MIME_TYPES = frozenset({"PocketBook Reader"})
|
|
50
|
+
# Other known valid names:
|
|
51
51
|
# "Panels", "Chunky"
|
|
52
52
|
|
|
53
53
|
|
|
@@ -168,14 +168,17 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
168
168
|
|
|
169
169
|
def _ensure_page_counts(self):
|
|
170
170
|
"""Ensure page counts on books with just in time comicbox."""
|
|
171
|
+
if self.admin_flags.get("import_metadata"):
|
|
172
|
+
return
|
|
171
173
|
books_qs: QuerySet = self.obj["books"] # type: ignore
|
|
172
174
|
import_pks = set()
|
|
173
175
|
new_books = []
|
|
174
176
|
for book in books_qs:
|
|
175
|
-
if
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
if book.page_count is not None:
|
|
178
|
+
continue
|
|
179
|
+
with Comicbox(book.path) as cb:
|
|
180
|
+
book.file_type = cb.get_file_type()
|
|
181
|
+
book.page_count = cb.get_page_count()
|
|
179
182
|
new_books.append(book)
|
|
180
183
|
import_pks.add(book.pk)
|
|
181
184
|
if new_books:
|
|
@@ -187,7 +190,24 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
187
190
|
|
|
188
191
|
def get_object(self): # type: ignore
|
|
189
192
|
"""Get the browser page and serialize it for this subclass."""
|
|
190
|
-
|
|
193
|
+
group_qs, book_qs, num_pages, total_count, zero_pad, mtime = (
|
|
194
|
+
super()._get_group_and_books()
|
|
195
|
+
)
|
|
196
|
+
book_qs = book_qs.select_related("series", "volume", "language")
|
|
197
|
+
|
|
198
|
+
title = self.get_browser_page_title()
|
|
199
|
+
self.obj = MappingProxyType(
|
|
200
|
+
{
|
|
201
|
+
"title": title,
|
|
202
|
+
"groups": group_qs,
|
|
203
|
+
"books": book_qs,
|
|
204
|
+
"zero_pad": zero_pad,
|
|
205
|
+
"num_pages": num_pages,
|
|
206
|
+
"total_count": total_count,
|
|
207
|
+
"mtime": mtime,
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
|
|
191
211
|
self.is_aq_feed = self.model_group in ("c", "f")
|
|
192
212
|
|
|
193
213
|
self._ensure_page_counts()
|
|
@@ -197,22 +217,21 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
197
217
|
|
|
198
218
|
def _set_user_agent_variables(self):
|
|
199
219
|
"""Set User Agent variables."""
|
|
200
|
-
# defaults in FacetsMixin
|
|
201
220
|
user_agent = self.request.headers.get("User-Agent")
|
|
202
221
|
if not user_agent:
|
|
203
222
|
return
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
223
|
+
user_agent_parts = user_agent.split("/", 1)
|
|
224
|
+
if user_agent_parts:
|
|
225
|
+
user_agent_name = user_agent_parts[0]
|
|
226
|
+
else:
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
if user_agent_name in UserAgentNames.FACET_SUPPORT:
|
|
230
|
+
self.use_facets = True
|
|
231
|
+
if user_agent_name in UserAgentNames.CLIENT_REORDERS:
|
|
232
|
+
self.skip_order_facets = True
|
|
233
|
+
if user_agent_name in UserAgentNames.SIMPLE_DOWNLOAD_MIME_TYPES:
|
|
234
|
+
self.mime_type_map = MimeType.SIMPLE_FILE_TYPE_MAP
|
|
216
235
|
|
|
217
236
|
def set_opds_request_type(self):
|
|
218
237
|
"""Set the opds request type variables."""
|
codex/views/opds/v1/links.py
CHANGED
|
@@ -15,6 +15,7 @@ from codex.views.opds.v1.data import OPDS1Link
|
|
|
15
15
|
from codex.views.opds.v1.entry.data import OPDS1EntryData, OPDS1EntryObject
|
|
16
16
|
from codex.views.opds.v1.entry.entry import OPDS1Entry
|
|
17
17
|
from codex.views.opds.v1.facets import OPDS1FacetsView
|
|
18
|
+
from codex.views.util import pop_name
|
|
18
19
|
|
|
19
20
|
LOG = get_logger(__name__)
|
|
20
21
|
|
|
@@ -129,7 +130,7 @@ class OPDS1LinksView(OPDS1FacetsView):
|
|
|
129
130
|
"""Create a link."""
|
|
130
131
|
if query_params is None:
|
|
131
132
|
query_params = self.request.GET
|
|
132
|
-
kwargs
|
|
133
|
+
kwargs = pop_name(kwargs)
|
|
133
134
|
href = reverse("opds:v1:feed", kwargs=kwargs)
|
|
134
135
|
href = update_href_query_params(href, query_params)
|
|
135
136
|
return OPDS1Link(rel, href, mime_type)
|
codex/views/opds/v2/feed.py
CHANGED
|
@@ -175,32 +175,34 @@ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
|
|
|
175
175
|
|
|
176
176
|
def get_object(self):
|
|
177
177
|
"""Get the browser page and serialize it for this subclass."""
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
group_qs, book_qs, num_pages, total_count, zero_pad, mtime = (
|
|
179
|
+
self._get_group_and_books()
|
|
180
|
+
)
|
|
181
|
+
title = self.get_browser_page_title()
|
|
180
182
|
# convert browser_page into opds page
|
|
181
183
|
|
|
182
184
|
# instance vars
|
|
183
|
-
self.is_aq_feed =
|
|
184
|
-
self.num_pages =
|
|
185
|
+
self.is_aq_feed = self.model_group in ("c", "f")
|
|
186
|
+
self.num_pages = num_pages
|
|
185
187
|
|
|
186
188
|
# opds page
|
|
187
|
-
title = self._title(
|
|
188
|
-
number_of_items =
|
|
189
|
+
title = self._title(title)
|
|
190
|
+
number_of_items = total_count
|
|
189
191
|
current_page = self.kwargs.get("page")
|
|
190
192
|
up_route = self.get_last_route()
|
|
191
193
|
links = self.get_links(up_route)
|
|
192
194
|
facets = self._get_facets()
|
|
193
195
|
|
|
194
196
|
# opds groups
|
|
195
|
-
page_groups =
|
|
196
|
-
page_books =
|
|
197
|
-
zero_pad = browser_page["zero_pad"]
|
|
197
|
+
page_groups = group_qs
|
|
198
|
+
page_books = book_qs
|
|
198
199
|
groups = self._get_groups(page_groups, page_books, title, zero_pad)
|
|
199
200
|
|
|
200
201
|
return MappingProxyType(
|
|
201
202
|
{
|
|
202
203
|
"metadata": {
|
|
203
204
|
"title": title,
|
|
205
|
+
"modified": mtime,
|
|
204
206
|
"number_of_items": number_of_items,
|
|
205
207
|
"items_per_page": MAX_OBJ_PER_PAGE,
|
|
206
208
|
"current_page": current_page,
|
codex/views/opds/v2/links.py
CHANGED
|
@@ -10,6 +10,7 @@ from codex.views.browser.browser import BrowserView
|
|
|
10
10
|
from codex.views.const import FALSY
|
|
11
11
|
from codex.views.opds.const import MimeType, Rel
|
|
12
12
|
from codex.views.opds.util import update_href_query_params
|
|
13
|
+
from codex.views.util import pop_name
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
@dataclass
|
|
@@ -69,7 +70,7 @@ class OPDS2LinksView(BrowserView):
|
|
|
69
70
|
if "page" in kwargs and not self._href_page_validate(kwargs, data):
|
|
70
71
|
return None
|
|
71
72
|
|
|
72
|
-
kwargs
|
|
73
|
+
kwargs = pop_name(kwargs)
|
|
73
74
|
href = reverse(url_name, kwargs=kwargs)
|
|
74
75
|
return self._href_update_query_params(href, data)
|
|
75
76
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Publication Methods for OPDS v2.0 feed."""
|
|
2
2
|
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from math import floor
|
|
3
5
|
from types import MappingProxyType
|
|
4
6
|
from urllib.parse import quote_plus
|
|
5
7
|
|
|
6
|
-
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
7
|
-
|
|
8
8
|
from codex.librarian.covers.create import CoverCreateMixin
|
|
9
9
|
from codex.models import Comic
|
|
10
10
|
from codex.views.opds.const import AUTHOR_ROLES, MimeType, Rel
|
|
@@ -32,39 +32,16 @@ class OPDS2PublicationView(OPDS2TopLinksView):
|
|
|
32
32
|
|
|
33
33
|
is_opds_metadata = False
|
|
34
34
|
|
|
35
|
-
def _get_big_image_link(self, obj, cover_pk):
|
|
36
|
-
if cover_pk:
|
|
37
|
-
mime_type = MimeType.JPEG
|
|
38
|
-
url_name = "opds:bin:page"
|
|
39
|
-
href = None
|
|
40
|
-
min_page = 0
|
|
41
|
-
max_page = obj.max_page or 1
|
|
42
|
-
else:
|
|
43
|
-
mime_type = MimeType.WEBP
|
|
44
|
-
href = staticfiles_storage.url("img/missing_cover.webp")
|
|
45
|
-
url_name = None
|
|
46
|
-
min_page = None
|
|
47
|
-
max_page = None
|
|
48
|
-
|
|
49
|
-
kwargs = {"pk": obj.pk, "page": 0}
|
|
50
|
-
href_data = HrefData(
|
|
51
|
-
kwargs,
|
|
52
|
-
absolute_query_params=True,
|
|
53
|
-
url_name=url_name,
|
|
54
|
-
min_page=min_page,
|
|
55
|
-
max_page=max_page,
|
|
56
|
-
)
|
|
57
|
-
link_data = LinkData(Rel.IMAGE, href_data, href=href, mime_type=mime_type)
|
|
58
|
-
return self.link(link_data)
|
|
59
|
-
|
|
60
35
|
def _images(self, obj):
|
|
61
36
|
# Images
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
37
|
+
kwargs = {"group": obj.group, "pks": obj.ids}
|
|
38
|
+
ts = floor(datetime.timestamp(obj.updated_at))
|
|
39
|
+
query_params = {
|
|
40
|
+
"customCovers": True,
|
|
41
|
+
"dynamicCovers": False,
|
|
42
|
+
"ts": ts,
|
|
43
|
+
}
|
|
44
|
+
href_data = HrefData(kwargs, query_params, True, "opds:bin:cover")
|
|
68
45
|
link_data = LinkData(
|
|
69
46
|
Rel.THUMBNAIL,
|
|
70
47
|
href_data,
|
|
@@ -75,7 +52,9 @@ class OPDS2PublicationView(OPDS2TopLinksView):
|
|
|
75
52
|
|
|
76
53
|
cover_link = self.link(link_data)
|
|
77
54
|
|
|
78
|
-
|
|
55
|
+
link_data.rel = Rel.IMAGE
|
|
56
|
+
|
|
57
|
+
image_link = self.link(link_data)
|
|
79
58
|
|
|
80
59
|
return [
|
|
81
60
|
cover_link,
|
codex/views/public.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from rest_framework.generics import GenericAPIView
|
|
4
4
|
from rest_framework.mixins import RetrieveModelMixin
|
|
5
|
-
from rest_framework.parsers import JSONParser
|
|
6
5
|
|
|
7
6
|
from codex.logger.logging import get_logger
|
|
8
7
|
from codex.models import AdminFlag
|
|
@@ -21,7 +20,6 @@ _ADMIN_FLAG_KEYS = frozenset(
|
|
|
21
20
|
class AdminFlagsView(GenericAPIView, RetrieveModelMixin):
|
|
22
21
|
"""Get admin flags relevant to auth."""
|
|
23
22
|
|
|
24
|
-
parser_classes = (JSONParser,)
|
|
25
23
|
serializer_class = AuthAdminFlagsSerializer
|
|
26
24
|
queryset = AdminFlag.objects.filter(key__in=_ADMIN_FLAG_KEYS).only("key", "on")
|
|
27
25
|
|