codex 1.6.0a7__py3-none-any.whl → 1.6.0a8__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 +4 -4
- 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 +102 -17
- codex/librarian/importer/create_covers.py +98 -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 +41 -62
- 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 +72 -96
- 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/settings.py +2 -3
- codex/serializers/choices.py +10 -1
- codex/settings/settings.py +2 -2
- codex/static_root/assets/{VCheckbox-BOUtyxuo.c690f0cdbe48.js → VCheckbox-DI-DgMb4.8eeb1bd3e18b.js} +1 -1
- codex/static_root/assets/VCheckbox-DI-DgMb4.8eeb1bd3e18b.js.br +0 -0
- codex/static_root/assets/VCheckbox-DI-DgMb4.8eeb1bd3e18b.js.gz +0 -0
- codex/static_root/assets/{VCheckbox-BOUtyxuo.js → VCheckbox-DI-DgMb4.js} +1 -1
- codex/static_root/assets/VCheckbox-DI-DgMb4.js.br +0 -0
- codex/static_root/assets/VCheckbox-DI-DgMb4.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-B1-m5pEh.e2b8cbdb92c9.js → VCheckboxBtn-B8Z4NA-t.3d8921f98ad9.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.3d8921f98ad9.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.3d8921f98ad9.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-B1-m5pEh.js → VCheckboxBtn-B8Z4NA-t.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.js.gz +0 -0
- codex/static_root/assets/{VCombobox-DjkDXe33.22617ea193b1.js → VCombobox-D1_0lCri.6ef85a29c2dc.js} +1 -1
- codex/static_root/assets/VCombobox-D1_0lCri.6ef85a29c2dc.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_0lCri.6ef85a29c2dc.js.gz +0 -0
- codex/static_root/assets/{VCombobox-DjkDXe33.js → VCombobox-D1_0lCri.js} +1 -1
- codex/static_root/assets/VCombobox-D1_0lCri.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_0lCri.js.gz +0 -0
- codex/static_root/assets/{VDialog-X0zn9AGX.0d89c749b9a6.js → VDialog-opmV0YZz.d77216e97599.js} +1 -1
- codex/static_root/assets/VDialog-opmV0YZz.d77216e97599.js.br +0 -0
- codex/static_root/assets/VDialog-opmV0YZz.d77216e97599.js.gz +0 -0
- codex/static_root/assets/{VDialog-X0zn9AGX.js → VDialog-opmV0YZz.js} +1 -1
- codex/static_root/assets/VDialog-opmV0YZz.js.br +0 -0
- codex/static_root/assets/VDialog-opmV0YZz.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-CRevojaF.43d65c777c58.js → VExpansionPanels-DX2dhUf5.ecc2ebfa62ec.js} +1 -1
- codex/static_root/assets/VExpansionPanels-DX2dhUf5.ecc2ebfa62ec.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DX2dhUf5.ecc2ebfa62ec.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-CRevojaF.js → VExpansionPanels-DX2dhUf5.js} +1 -1
- codex/static_root/assets/VExpansionPanels-DX2dhUf5.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DX2dhUf5.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-8nrrxjDv.2b4dfb984a8c.js → VRadioGroup-BGa6_q5a.d1a3ad7266bd.js} +1 -1
- codex/static_root/assets/VRadioGroup-BGa6_q5a.d1a3ad7266bd.js.br +0 -0
- codex/static_root/assets/VRadioGroup-BGa6_q5a.d1a3ad7266bd.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-8nrrxjDv.js → VRadioGroup-BGa6_q5a.js} +1 -1
- codex/static_root/assets/VRadioGroup-BGa6_q5a.js.br +0 -0
- codex/static_root/assets/VRadioGroup-BGa6_q5a.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-sIYW_-ud.7d40da0ec3ff.js +1 -0
- codex/static_root/assets/VSelect-sIYW_-ud.7d40da0ec3ff.js.br +0 -0
- codex/static_root/assets/VSelect-sIYW_-ud.7d40da0ec3ff.js.gz +0 -0
- codex/static_root/assets/VSelect-sIYW_-ud.js +1 -0
- codex/static_root/assets/VSelect-sIYW_-ud.js.br +0 -0
- codex/static_root/assets/VSelect-sIYW_-ud.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CGu0oz0K.3030b7a270d6.js → VSelectionControl-C8DP9rgp.ef80e8e3dfc1.js} +1 -1
- codex/static_root/assets/VSelectionControl-C8DP9rgp.ef80e8e3dfc1.js.br +0 -0
- codex/static_root/assets/VSelectionControl-C8DP9rgp.ef80e8e3dfc1.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CGu0oz0K.js → VSelectionControl-C8DP9rgp.js} +1 -1
- codex/static_root/assets/VSelectionControl-C8DP9rgp.js.br +0 -0
- codex/static_root/assets/VSelectionControl-C8DP9rgp.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-D_oNvCOd.js → VSlideGroup-BRYhkLH8.3534ec94b2f1.js} +1 -1
- codex/static_root/assets/VSlideGroup-BRYhkLH8.3534ec94b2f1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-BRYhkLH8.3534ec94b2f1.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-D_oNvCOd.71a7101cdddd.js → VSlideGroup-BRYhkLH8.js} +1 -1
- codex/static_root/assets/VSlideGroup-BRYhkLH8.js.br +0 -0
- codex/static_root/assets/VSlideGroup-BRYhkLH8.js.gz +0 -0
- codex/static_root/assets/{VTable-C7pKI7gU.087912f706f5.js → VTable-D1esKtlw.52165930e3b9.js} +1 -1
- codex/static_root/assets/VTable-D1esKtlw.52165930e3b9.js.br +0 -0
- codex/static_root/assets/VTable-D1esKtlw.52165930e3b9.js.gz +0 -0
- codex/static_root/assets/{VTable-C7pKI7gU.js → VTable-D1esKtlw.js} +1 -1
- codex/static_root/assets/VTable-D1esKtlw.js.br +0 -0
- codex/static_root/assets/VTable-D1esKtlw.js.gz +0 -0
- codex/static_root/assets/VTextField-BnGscKcY.54032adf3a4b.js +1 -0
- codex/static_root/assets/VTextField-BnGscKcY.54032adf3a4b.js.br +0 -0
- codex/static_root/assets/VTextField-BnGscKcY.54032adf3a4b.js.gz +0 -0
- codex/static_root/assets/VTextField-BnGscKcY.js +1 -0
- codex/static_root/assets/VTextField-BnGscKcY.js.br +0 -0
- codex/static_root/assets/VTextField-BnGscKcY.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-iyZ1XOkP.409f7d292253.js → VWindowItem-B0KokcAk.ea94224aecb9.js} +1 -1
- codex/static_root/assets/VWindowItem-B0KokcAk.ea94224aecb9.js.br +0 -0
- codex/static_root/assets/VWindowItem-B0KokcAk.ea94224aecb9.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-iyZ1XOkP.js → VWindowItem-B0KokcAk.js} +1 -1
- codex/static_root/assets/VWindowItem-B0KokcAk.js.br +0 -0
- codex/static_root/assets/VWindowItem-B0KokcAk.js.gz +0 -0
- codex/static_root/assets/{admin-B0pCBjma.2407da79bd20.js → admin-Y0bL21AB.42d4f0d68163.js} +1 -1
- codex/static_root/assets/admin-Y0bL21AB.42d4f0d68163.js.br +0 -0
- codex/static_root/assets/admin-Y0bL21AB.42d4f0d68163.js.gz +0 -0
- codex/static_root/assets/{admin-B0pCBjma.js → admin-Y0bL21AB.js} +1 -1
- codex/static_root/assets/admin-Y0bL21AB.js.br +0 -0
- codex/static_root/assets/admin-Y0bL21AB.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BxoNUHQr.5e2e1a4a255d.js → admin-drawer-panel-DNngWUEM.4e992e3fe86e.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-DNngWUEM.4e992e3fe86e.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-DNngWUEM.4e992e3fe86e.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BxoNUHQr.js → admin-drawer-panel-DNngWUEM.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-DNngWUEM.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-DNngWUEM.js.gz +0 -0
- codex/static_root/assets/browser-CMAtBCLx.17a277ce43a9.css +1 -0
- codex/static_root/assets/browser-CMAtBCLx.17a277ce43a9.css.br +0 -0
- codex/static_root/assets/browser-CMAtBCLx.17a277ce43a9.css.gz +0 -0
- codex/static_root/assets/browser-CMAtBCLx.css +1 -0
- codex/static_root/assets/browser-CMAtBCLx.css.br +0 -0
- codex/static_root/assets/browser-CMAtBCLx.css.gz +0 -0
- codex/static_root/assets/browser-DoJk8uvb.2c97a3858932.js +1 -0
- codex/static_root/assets/browser-DoJk8uvb.2c97a3858932.js.br +0 -0
- codex/static_root/assets/browser-DoJk8uvb.2c97a3858932.js.gz +0 -0
- codex/static_root/assets/browser-DoJk8uvb.js +1 -0
- codex/static_root/assets/browser-DoJk8uvb.js.br +0 -0
- codex/static_root/assets/browser-DoJk8uvb.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-DZ9dP8_F.js → change-password-dialog-CH4xbjJa.b749aa6ea07e.js} +1 -1
- codex/static_root/assets/change-password-dialog-CH4xbjJa.b749aa6ea07e.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CH4xbjJa.b749aa6ea07e.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-DZ9dP8_F.930f4cae3cb0.js → change-password-dialog-CH4xbjJa.js} +1 -1
- codex/static_root/assets/change-password-dialog-CH4xbjJa.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CH4xbjJa.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-RP7JNStF.a752a7c1e697.js → confirm-dialog-DkU0twE6.a20006aa5ba6.js} +1 -1
- codex/static_root/assets/{confirm-dialog-RP7JNStF.a752a7c1e697.js.br → confirm-dialog-DkU0twE6.a20006aa5ba6.js.br} +0 -0
- codex/static_root/assets/confirm-dialog-DkU0twE6.a20006aa5ba6.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-RP7JNStF.js → confirm-dialog-DkU0twE6.js} +1 -1
- codex/static_root/assets/{confirm-dialog-RP7JNStF.js.br → confirm-dialog-DkU0twE6.js.br} +0 -0
- codex/static_root/assets/confirm-dialog-DkU0twE6.js.gz +0 -0
- codex/static_root/assets/datetime-column-BfW_j_3Q.6a614321a435.js +1 -0
- codex/static_root/assets/datetime-column-BfW_j_3Q.6a614321a435.js.br +0 -0
- codex/static_root/assets/datetime-column-BfW_j_3Q.6a614321a435.js.gz +0 -0
- codex/static_root/assets/datetime-column-BfW_j_3Q.js +1 -0
- codex/static_root/assets/datetime-column-BfW_j_3Q.js.br +0 -0
- codex/static_root/assets/datetime-column-BfW_j_3Q.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-DjxN78cK.35673f6e49b1.js} +1 -1
- codex/static_root/assets/filter-DjxN78cK.35673f6e49b1.js.br +0 -0
- codex/static_root/assets/filter-DjxN78cK.35673f6e49b1.js.gz +0 -0
- codex/static_root/assets/{filter-DAdGUt-1.js → filter-DjxN78cK.js} +1 -1
- codex/static_root/assets/filter-DjxN78cK.js.br +0 -0
- codex/static_root/assets/filter-DjxN78cK.js.gz +0 -0
- codex/static_root/assets/flag-tab-D4ZNSbMR.e763a9937701.js +1 -0
- codex/static_root/assets/flag-tab-D4ZNSbMR.e763a9937701.js.br +0 -0
- codex/static_root/assets/flag-tab-D4ZNSbMR.e763a9937701.js.gz +0 -0
- codex/static_root/assets/flag-tab-D4ZNSbMR.js +1 -0
- codex/static_root/assets/flag-tab-D4ZNSbMR.js.br +0 -0
- codex/static_root/assets/flag-tab-D4ZNSbMR.js.gz +0 -0
- codex/static_root/assets/{group-tab-DEwh4jfB.a95097b5e320.js → group-tab-DucpNoBs.a6f37ae940c0.js} +1 -1
- codex/static_root/assets/group-tab-DucpNoBs.a6f37ae940c0.js.br +0 -0
- codex/static_root/assets/group-tab-DucpNoBs.a6f37ae940c0.js.gz +0 -0
- codex/static_root/assets/{group-tab-DEwh4jfB.js → group-tab-DucpNoBs.js} +1 -1
- codex/static_root/assets/group-tab-DucpNoBs.js.br +0 -0
- codex/static_root/assets/group-tab-DucpNoBs.js.gz +0 -0
- codex/static_root/assets/http-error-Cj0hTq-c.062a88ddd4dc.js +1 -0
- codex/static_root/assets/http-error-Cj0hTq-c.062a88ddd4dc.js.br +0 -0
- codex/static_root/assets/http-error-Cj0hTq-c.062a88ddd4dc.js.gz +0 -0
- codex/static_root/assets/http-error-Cj0hTq-c.js +1 -0
- codex/static_root/assets/http-error-Cj0hTq-c.js.br +0 -0
- codex/static_root/assets/http-error-Cj0hTq-c.js.gz +0 -0
- codex/static_root/assets/{library-tab-DrXvD9B2.js → library-tab-CzksdS3Q.566acdb45dec.js} +1 -1
- codex/static_root/assets/library-tab-CzksdS3Q.566acdb45dec.js.br +0 -0
- codex/static_root/assets/library-tab-CzksdS3Q.566acdb45dec.js.gz +0 -0
- codex/static_root/assets/{library-tab-DrXvD9B2.f765f9d3bae4.js → library-tab-CzksdS3Q.js} +1 -1
- codex/static_root/assets/library-tab-CzksdS3Q.js.br +0 -0
- codex/static_root/assets/library-tab-CzksdS3Q.js.gz +0 -0
- codex/static_root/assets/main-DX-k2724.f73d08e9ad64.js +32 -0
- codex/static_root/assets/main-DX-k2724.f73d08e9ad64.js.br +0 -0
- codex/static_root/assets/main-DX-k2724.f73d08e9ad64.js.gz +0 -0
- codex/static_root/assets/main-DX-k2724.js +32 -0
- codex/static_root/assets/main-DX-k2724.js.br +0 -0
- codex/static_root/assets/main-DX-k2724.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CQcR0a3T.7a2d33ed8c39.js +1 -0
- codex/static_root/assets/pagination-toolbar-CQcR0a3T.7a2d33ed8c39.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CQcR0a3T.7a2d33ed8c39.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CQcR0a3T.js +1 -0
- codex/static_root/assets/pagination-toolbar-CQcR0a3T.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CQcR0a3T.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/{pdf-doc-obrBwxa6.ec9bd13a5786.js → pdf-doc-GkMk4o5L.ef24c859a7a4.js} +1 -1
- codex/static_root/assets/pdf-doc-GkMk4o5L.ef24c859a7a4.js.br +0 -0
- codex/static_root/assets/pdf-doc-GkMk4o5L.ef24c859a7a4.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-obrBwxa6.js → pdf-doc-GkMk4o5L.js} +1 -1
- codex/static_root/assets/pdf-doc-GkMk4o5L.js.br +0 -0
- codex/static_root/assets/pdf-doc-GkMk4o5L.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-Cl6mmQB_.7b9e7591954f.js +2 -0
- codex/static_root/assets/reader-Cl6mmQB_.7b9e7591954f.js.br +0 -0
- codex/static_root/assets/reader-Cl6mmQB_.7b9e7591954f.js.gz +0 -0
- codex/static_root/assets/reader-Cl6mmQB_.js +2 -0
- codex/static_root/assets/reader-Cl6mmQB_.js.br +0 -0
- codex/static_root/assets/reader-Cl6mmQB_.js.gz +0 -0
- codex/static_root/assets/{relation-chips-CZJcLjc8.1d7426dab654.js → relation-chips-CZ8VvR4y.d4232f3c2518.js} +1 -1
- codex/static_root/assets/relation-chips-CZ8VvR4y.d4232f3c2518.js.br +0 -0
- codex/static_root/assets/relation-chips-CZ8VvR4y.d4232f3c2518.js.gz +0 -0
- codex/static_root/assets/{relation-chips-CZJcLjc8.js → relation-chips-CZ8VvR4y.js} +1 -1
- codex/static_root/assets/relation-chips-CZ8VvR4y.js.br +0 -0
- codex/static_root/assets/relation-chips-CZ8VvR4y.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-7iTnAdjf.337eecf2fdbb.js → settings-drawer-DyS0A2pN.17a98928589f.js} +2 -2
- codex/static_root/assets/settings-drawer-DyS0A2pN.17a98928589f.js.br +0 -0
- codex/static_root/assets/settings-drawer-DyS0A2pN.17a98928589f.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-7iTnAdjf.js → settings-drawer-DyS0A2pN.js} +2 -2
- codex/static_root/assets/settings-drawer-DyS0A2pN.js.br +0 -0
- codex/static_root/assets/settings-drawer-DyS0A2pN.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CQsK1n8O.544bf50ffb22.js → stats-tab-R3Ekobfm.70fa09a67518.js} +1 -1
- codex/static_root/assets/stats-tab-R3Ekobfm.70fa09a67518.js.br +0 -0
- codex/static_root/assets/stats-tab-R3Ekobfm.70fa09a67518.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CQsK1n8O.js → stats-tab-R3Ekobfm.js} +1 -1
- codex/static_root/assets/stats-tab-R3Ekobfm.js.br +0 -0
- codex/static_root/assets/stats-tab-R3Ekobfm.js.gz +0 -0
- codex/static_root/assets/task-tab-BhLDK_7Q.28ff51b3ed01.js +1 -0
- codex/static_root/assets/task-tab-BhLDK_7Q.28ff51b3ed01.js.br +0 -0
- codex/static_root/assets/task-tab-BhLDK_7Q.28ff51b3ed01.js.gz +0 -0
- codex/static_root/assets/task-tab-BhLDK_7Q.js +1 -0
- codex/static_root/assets/task-tab-BhLDK_7Q.js.br +0 -0
- codex/static_root/assets/task-tab-BhLDK_7Q.js.gz +0 -0
- codex/static_root/assets/to-case-BR2frjFx.87de5d7a0503.js +1 -0
- codex/static_root/assets/to-case-BR2frjFx.js +1 -0
- codex/static_root/assets/{unauthorized-Bs7NSqea.c55650fac055.js → unauthorized-BbXjAe5l.d48912d34a68.js} +1 -1
- codex/static_root/assets/unauthorized-BbXjAe5l.d48912d34a68.js.br +0 -0
- codex/static_root/assets/unauthorized-BbXjAe5l.d48912d34a68.js.gz +0 -0
- codex/static_root/assets/{unauthorized-Bs7NSqea.js → unauthorized-BbXjAe5l.js} +1 -1
- codex/static_root/assets/unauthorized-BbXjAe5l.js.br +0 -0
- codex/static_root/assets/unauthorized-BbXjAe5l.js.gz +0 -0
- codex/static_root/assets/{user-tab-ChuH7Atm.7e80cf1fb78e.js → user-tab-DxITIl2d.d79d7f062930.js} +1 -1
- codex/static_root/assets/user-tab-DxITIl2d.d79d7f062930.js.br +0 -0
- codex/static_root/assets/user-tab-DxITIl2d.d79d7f062930.js.gz +0 -0
- codex/static_root/assets/{user-tab-ChuH7Atm.js → user-tab-DxITIl2d.js} +1 -1
- codex/static_root/assets/user-tab-DxITIl2d.js.br +0 -0
- codex/static_root/assets/user-tab-DxITIl2d.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.16d52597ef50.json} +239 -239
- codex/static_root/manifest.16d52597ef50.json.br +0 -0
- codex/static_root/manifest.16d52597ef50.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/opds/binary.py +1 -1
- codex/views/admin/auth.py +0 -2
- codex/views/admin/tasks.py +13 -1
- codex/views/auth.py +2 -4
- codex/views/browser/annotations.py +27 -148
- codex/views/browser/base.py +1 -18
- codex/views/browser/breadcrumbs.py +16 -14
- codex/views/browser/browser.py +5 -10
- codex/views/browser/choices.py +15 -39
- codex/views/browser/cover.py +177 -0
- codex/views/browser/filters/annotations.py +23 -22
- codex/views/browser/filters/field.py +1 -2
- codex/views/browser/filters/group.py +13 -8
- codex/views/browser/filters/search.py +19 -10
- codex/views/browser/metadata.py +20 -25
- codex/views/const.py +12 -0
- codex/views/mtime.py +14 -12
- 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 +1 -1
- codex/views/reader/reader.py +2 -2
- codex/views/session.py +5 -3
- codex/views/util.py +32 -0
- {codex-1.6.0a7.dist-info → codex-1.6.0a8.dist-info}/METADATA +8 -7
- {codex-1.6.0a7.dist-info → codex-1.6.0a8.dist-info}/RECORD +317 -318
- 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/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 +0 -1
- 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 +0 -1
- 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.gz +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 +0 -1
- 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 +0 -1
- 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/utils.py +0 -26
- {codex-1.6.0a7.dist-info → codex-1.6.0a8.dist-info}/LICENSE +0 -0
- {codex-1.6.0a7.dist-info → codex-1.6.0a8.dist-info}/WHEEL +0 -0
- {codex-1.6.0a7.dist-info → codex-1.6.0a8.dist-info}/entry_points.txt +0 -0
codex/views/mtime.py
CHANGED
|
@@ -9,7 +9,7 @@ from codex.util import max_none
|
|
|
9
9
|
from codex.views.auth import AuthGenericAPIView
|
|
10
10
|
from codex.views.browser.filters.bookmark import BookmarkFilterMixin
|
|
11
11
|
from codex.views.const import GROUP_MODEL_MAP
|
|
12
|
-
from codex.views.
|
|
12
|
+
from codex.views.util import reparse_json_query_params
|
|
13
13
|
|
|
14
14
|
_REPARSE_JSON_FIELDS = frozenset({"groups"})
|
|
15
15
|
|
|
@@ -20,23 +20,27 @@ class MtimeView(AuthGenericAPIView, BookmarkFilterMixin):
|
|
|
20
20
|
serializer_class = GroupsMtimeSerializer
|
|
21
21
|
response_serializer_class = MtimeSerializer
|
|
22
22
|
|
|
23
|
-
def
|
|
23
|
+
def _parse_params(self):
|
|
24
|
+
params = reparse_json_query_params(self.request.GET, _REPARSE_JSON_FIELDS)
|
|
25
|
+
self.groups = params.get("groups", "")
|
|
26
|
+
self.use_bookmark_filter = self.request.GET.get("use_bookmark_filter", False)
|
|
27
|
+
|
|
28
|
+
def _get_group_mtime(self, group, pks):
|
|
24
29
|
"""Get one group's mtimes."""
|
|
25
30
|
model = GROUP_MODEL_MAP[group]
|
|
26
31
|
if not model:
|
|
27
32
|
model = Publisher
|
|
28
33
|
|
|
29
|
-
qs = model.objects
|
|
34
|
+
qs = model.objects.all()
|
|
30
35
|
|
|
31
36
|
if pks and 0 not in pks:
|
|
32
37
|
qs = qs.filter(pk__in=pks)
|
|
33
38
|
updated_at_max = qs.aggregate(max=Max("updated_at"))["max"]
|
|
34
39
|
|
|
35
|
-
if use_bookmark_filter:
|
|
40
|
+
if self.use_bookmark_filter:
|
|
36
41
|
bm_rel = self.get_bm_rel(model)
|
|
37
42
|
bm_filter = self.get_my_bookmark_filter(bm_rel)
|
|
38
43
|
qs = qs.filter(bm_filter)
|
|
39
|
-
# TODO can these be combined into one max?
|
|
40
44
|
bookmark_updated_at_max = qs.aggregate(max=(Max(f"{bm_rel}__updated_at")))[
|
|
41
45
|
"max"
|
|
42
46
|
]
|
|
@@ -44,25 +48,23 @@ class MtimeView(AuthGenericAPIView, BookmarkFilterMixin):
|
|
|
44
48
|
|
|
45
49
|
return updated_at_max
|
|
46
50
|
|
|
47
|
-
def get_max_groups_mtime(self
|
|
51
|
+
def get_max_groups_mtime(self):
|
|
48
52
|
"""Get max mtime for all groups."""
|
|
49
53
|
max_mtime = None
|
|
50
54
|
|
|
51
|
-
for item in groups:
|
|
55
|
+
for item in self.groups:
|
|
52
56
|
group = item["group"]
|
|
53
57
|
pks = tuple(int(pk) for pk in item["pks"].split(","))
|
|
54
|
-
mtime = self._get_group_mtime(group, pks
|
|
58
|
+
mtime = self._get_group_mtime(group, pks)
|
|
55
59
|
max_mtime = max_none(max_mtime, mtime)
|
|
56
60
|
return max_mtime
|
|
57
61
|
|
|
58
62
|
def get(self, *args, **kwargs):
|
|
59
63
|
"""Get the mtimes for the submitted groups."""
|
|
60
64
|
# Parse Request
|
|
61
|
-
|
|
62
|
-
groups = params.get("groups", "")
|
|
63
|
-
use_bookmark_filter = self.request.GET.get("use_bookmark_filter", False)
|
|
65
|
+
self._parse_params()
|
|
64
66
|
|
|
65
|
-
max_mtime = self.get_max_groups_mtime(
|
|
67
|
+
max_mtime = self.get_max_groups_mtime()
|
|
66
68
|
|
|
67
69
|
# Serialize Response
|
|
68
70
|
result = {"max_mtime": max_mtime}
|
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
|
|
codex/views/reader/books.py
CHANGED
|
@@ -75,7 +75,7 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
|
|
|
75
75
|
|
|
76
76
|
def _get_comics_list(self):
|
|
77
77
|
"""Get the reader naviation group filter."""
|
|
78
|
-
select_related = ("series",
|
|
78
|
+
select_related = ("series",)
|
|
79
79
|
prefetch_related = ()
|
|
80
80
|
|
|
81
81
|
arc: Mapping = self.params.get("arc", {}) # type: ignore
|
codex/views/reader/reader.py
CHANGED
|
@@ -15,6 +15,7 @@ from codex.logger.logging import get_logger
|
|
|
15
15
|
from codex.serializers.reader import ReaderArcSerializer, ReaderComicsSerializer
|
|
16
16
|
from codex.serializers.redirect import ReaderRedirectSerializer
|
|
17
17
|
from codex.views.reader.arcs import ReaderArcsView
|
|
18
|
+
from codex.views.util import pop_name
|
|
18
19
|
|
|
19
20
|
LOG = get_logger(__name__)
|
|
20
21
|
_VALID_ARC_GROUPS = frozenset({"f", "s", "a"})
|
|
@@ -140,8 +141,7 @@ class ReaderView(
|
|
|
140
141
|
arc["pks"] = (current.arc_pk,) # type: ignore
|
|
141
142
|
arc["index"] = current.arc_index # type:ignore
|
|
142
143
|
arc["count"] = current.arc_count # type: ignore
|
|
143
|
-
close_route = self.last_route
|
|
144
|
-
close_route.pop("name", None)
|
|
144
|
+
close_route = pop_name(self.last_route)
|
|
145
145
|
|
|
146
146
|
return {
|
|
147
147
|
"books": books,
|
codex/views/session.py
CHANGED
|
@@ -7,6 +7,7 @@ from types import MappingProxyType
|
|
|
7
7
|
from codex.logger.logging import get_logger
|
|
8
8
|
from codex.serializers.choices import DEFAULTS
|
|
9
9
|
from codex.views.auth import AuthFilterGenericAPIView
|
|
10
|
+
from codex.views.util import pop_name
|
|
10
11
|
|
|
11
12
|
LOG = get_logger(__name__)
|
|
12
13
|
|
|
@@ -62,8 +63,9 @@ class SessionView(AuthFilterGenericAPIView, ABC):
|
|
|
62
63
|
"q": DEFAULTS["q"],
|
|
63
64
|
"search_results_limit": DEFAULTS["searchResultsLimit"],
|
|
64
65
|
"show": DEFAULTS["show"],
|
|
65
|
-
"
|
|
66
|
-
"
|
|
66
|
+
"dynamic_covers": DEFAULTS["dynamicCovers"],
|
|
67
|
+
"custom_covers": DEFAULTS["customCovers"],
|
|
68
|
+
"twenty_four_hour_time": DEFAULTS["twentyFourHourTime"],
|
|
67
69
|
"top_group": DEFAULTS["topGroup"],
|
|
68
70
|
},
|
|
69
71
|
READER_SESSION_KEY: {
|
|
@@ -96,7 +98,7 @@ class SessionView(AuthFilterGenericAPIView, ABC):
|
|
|
96
98
|
breadcrumbs = DEFAULTS["breadcrumbs"]
|
|
97
99
|
last_route = breadcrumbs[-1]
|
|
98
100
|
if not name:
|
|
99
|
-
last_route
|
|
101
|
+
last_route = pop_name(last_route)
|
|
100
102
|
|
|
101
103
|
return last_route
|
|
102
104
|
|
codex/views/util.py
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
"""Utility classes by many views."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from contextlib import suppress
|
|
3
6
|
from dataclasses import asdict, dataclass
|
|
7
|
+
from json.decoder import JSONDecodeError
|
|
8
|
+
from urllib.parse import unquote_plus
|
|
9
|
+
|
|
10
|
+
from djangorestframework_camel_case.settings import api_settings
|
|
11
|
+
from djangorestframework_camel_case.util import underscoreize
|
|
4
12
|
|
|
5
13
|
|
|
6
14
|
@dataclass()
|
|
@@ -29,3 +37,27 @@ class Route(dict):
|
|
|
29
37
|
)
|
|
30
38
|
|
|
31
39
|
dict = asdict
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def reparse_json_query_params(query_params, keys) -> dict:
|
|
43
|
+
"""Reparse JSON encoded query_params."""
|
|
44
|
+
# It is an unbelievable amount of trouble to try to parse axios native bracket
|
|
45
|
+
# encoded complex objects in python
|
|
46
|
+
parsed_dict = {}
|
|
47
|
+
for key, value in query_params.items():
|
|
48
|
+
if key in keys:
|
|
49
|
+
parsed_value = unquote_plus(value)
|
|
50
|
+
with suppress(JSONDecodeError):
|
|
51
|
+
parsed_value = json.loads(parsed_value)
|
|
52
|
+
else:
|
|
53
|
+
parsed_value = value
|
|
54
|
+
|
|
55
|
+
parsed_dict[key] = parsed_value
|
|
56
|
+
return dict(underscoreize(parsed_dict, **api_settings.JSON_UNDERSCOREIZE)) # type:ignore
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def pop_name(kwargs: Mapping):
|
|
60
|
+
"""Pop name from a mapping route."""
|
|
61
|
+
kwargs = dict(kwargs)
|
|
62
|
+
kwargs.pop("name", None)
|
|
63
|
+
return kwargs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codex
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.0a8
|
|
4
4
|
Summary: A comic archive web server.
|
|
5
5
|
Home-page: https://github.com/ajslater/codex
|
|
6
6
|
License: GPL-3.0-only
|
|
@@ -29,7 +29,7 @@ Requires-Dist: ansicolors (>=1.1,<2.0)
|
|
|
29
29
|
Requires-Dist: bidict (>=0.23.1,<0.24.0)
|
|
30
30
|
Requires-Dist: case-converter (>=1.1.0,<2.0.0)
|
|
31
31
|
Requires-Dist: channels (>=4.0.0,<5.0.0)
|
|
32
|
-
Requires-Dist: comicbox[pdf] (>=1.1.
|
|
32
|
+
Requires-Dist: comicbox[pdf] (>=1.1.8,<2.0.0)
|
|
33
33
|
Requires-Dist: django (>=5.0.6,<6.0.0)
|
|
34
34
|
Requires-Dist: django-cachalot (>=2.6.2,<3.0.0)
|
|
35
35
|
Requires-Dist: django-cors-headers (>=4.0,<5.0)
|
|
@@ -348,12 +348,13 @@ index, a Django cache and comic book cover thumbnails.
|
|
|
348
348
|
|
|
349
349
|
Codex contains some experimental throttling controls. The value supplied to
|
|
350
350
|
these variables will be interpreted as the maximum number of allowed requests
|
|
351
|
-
per minute.
|
|
351
|
+
per minute. For example, the following settings would limit each described group
|
|
352
|
+
to 2 queries per second.
|
|
352
353
|
|
|
353
|
-
- `CODEX_THROTTLE_ANON=
|
|
354
|
-
- `CODEX_THROTTLE_USER=
|
|
355
|
-
- `CODEX_THROTTLE_OPDS=
|
|
356
|
-
- `CODEX_THROTTLE_OPENSEARCH=
|
|
354
|
+
- `CODEX_THROTTLE_ANON=30` Anonymous users
|
|
355
|
+
- `CODEX_THROTTLE_USER=30` Authenticated users
|
|
356
|
+
- `CODEX_THROTTLE_OPDS=30` The OPDS v1 & v2 APIs (Panels uses this for search)
|
|
357
|
+
- `CODEX_THROTTLE_OPENSEARCH=30` The OPDS v1 Opensearch API
|
|
357
358
|
|
|
358
359
|
### Reverse Proxy
|
|
359
360
|
|