codex 1.6.0a6__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/exceptions.py +1 -0
- codex/integrity.py +4 -4
- codex/librarian/importer/{aggregate_metadata.py → aggregate.py} +56 -86
- codex/librarian/importer/cache.py +180 -0
- 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 -395
- 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 +11 -1
- codex/librarian/librariand.py +8 -2
- 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/permissions.py +1 -1
- codex/search/query.py +25 -2
- codex/search/writing.py +1 -1
- codex/serializers/admin.py +1 -2
- codex/serializers/browser/filters.py +8 -101
- codex/serializers/browser/mixins.py +2 -3
- codex/serializers/browser/page.py +2 -2
- codex/serializers/browser/settings.py +4 -3
- codex/serializers/choices.py +10 -1
- codex/serializers/fields.py +123 -0
- codex/serializers/mtime.py +25 -0
- codex/serializers/opds/v2.py +7 -11
- codex/serializers/opds/v2_unused.py +5 -3
- codex/serializers/reader.py +5 -2
- codex/serializers/route.py +7 -3
- codex/settings/settings.py +2 -2
- codex/static_root/assets/VCheckbox-DI-DgMb4.8eeb1bd3e18b.js +1 -0
- 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-DI-DgMb4.js +1 -0
- 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-m4IbdjEe.f0c4a814ded8.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-m4IbdjEe.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-D1_0lCri.6ef85a29c2dc.js +1 -0
- 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-D1_0lCri.js +1 -0
- 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-opmV0YZz.d77216e97599.js +1 -0
- 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-opmV0YZz.js +1 -0
- 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-DX2dhUf5.ecc2ebfa62ec.js +1 -0
- 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-DX2dhUf5.js +1 -0
- 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-BGa6_q5a.d1a3ad7266bd.js +1 -0
- 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-BGa6_q5a.js +1 -0
- 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-C8DP9rgp.ef80e8e3dfc1.js +1 -0
- 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-C8DP9rgp.js +1 -0
- 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-BRYhkLH8.3534ec94b2f1.js +1 -0
- 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-BRYhkLH8.js +1 -0
- 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-C0gAriFq.fa6e4b60d4ef.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-C0gAriFq.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-CFhKCj49.b2458002a937.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-CFhKCj49.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-Y0bL21AB.42d4f0d68163.js +1 -0
- 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-Y0bL21AB.js +1 -0
- 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-DNngWUEM.4e992e3fe86e.js +30 -0
- 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-DNngWUEM.js +30 -0
- 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/{admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css → admin-drawer-panel-o87BwD38.ce39427c8201.css} +1 -1
- codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.br +0 -0
- codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.gz → admin-drawer-panel-o87BwD38.ce39427c8201.css.gz} +0 -0
- codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.css → admin-drawer-panel-o87BwD38.css} +1 -1
- codex/static_root/assets/admin-drawer-panel-o87BwD38.css.br +0 -0
- codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.css.gz → admin-drawer-panel-o87BwD38.css.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-JYM9aAR9.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-JYM9aAR9.f71d378f4fa6.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-ZAZ6P1Rc.515858dd21e8.js → confirm-dialog-DkU0twE6.a20006aa5ba6.js} +1 -1
- codex/static_root/assets/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-ZAZ6P1Rc.js → confirm-dialog-DkU0twE6.js} +1 -1
- codex/static_root/assets/confirm-dialog-DkU0twE6.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DkU0twE6.js.gz +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +1 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +1 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +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-DjxN78cK.35673f6e49b1.js +1 -0
- 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-DjxN78cK.js +1 -0
- 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-Ci_XcDT0.a5ea640a08d6.js → flag-tab-D4ZNSbMR.e763a9937701.js} +1 -1
- 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-Ci_XcDT0.js → flag-tab-D4ZNSbMR.js} +1 -1
- 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-DFma2R-E.879c37b15bbd.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-DFma2R-E.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-V8DUGoHN.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-V8DUGoHN.809c548ff347.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/{main-sK7HC5h4.css → main-Ztp855PW.css} +1 -1
- codex/static_root/assets/main-Ztp855PW.css.br +0 -0
- codex/static_root/assets/{main-sK7HC5h4.css.gz → main-Ztp855PW.css.gz} +0 -0
- codex/static_root/assets/{main-sK7HC5h4.6f04ddfc5f1a.css → main-Ztp855PW.d46d718fac45.css} +1 -1
- codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
- codex/static_root/assets/{main-sK7HC5h4.6f04ddfc5f1a.css.gz → main-Ztp855PW.d46d718fac45.css.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-GkMk4o5L.ef24c859a7a4.js +305 -0
- 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-GkMk4o5L.js +305 -0
- 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-CZ8VvR4y.d4232f3c2518.js +1 -0
- 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-CZ8VvR4y.js +1 -0
- 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-DyS0A2pN.17a98928589f.js +2 -0
- 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-DyS0A2pN.js +2 -0
- 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-R3Ekobfm.70fa09a67518.js +1 -0
- 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-R3Ekobfm.js +1 -0
- 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-BbXjAe5l.d48912d34a68.js +1 -0
- 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-BbXjAe5l.js +1 -0
- 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-DxITIl2d.d79d7f062930.js +1 -0
- 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-DxITIl2d.js +1 -0
- 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.a0aaaa6c7ef7.json +1 -0
- 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.16d52597ef50.json +629 -0
- codex/static_root/manifest.16d52597ef50.json.br +0 -0
- codex/static_root/manifest.16d52597ef50.json.gz +0 -0
- codex/static_root/manifest.json +275 -288
- 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/auth.py +2 -1
- codex/urls/api/browser.py +13 -4
- codex/urls/api/reader.py +3 -9
- codex/urls/api/v3.py +2 -0
- codex/urls/opds/binary.py +1 -1
- codex/util.py +10 -0
- codex/views/admin/api_key.py +2 -6
- codex/views/admin/auth.py +28 -0
- codex/views/admin/flag.py +2 -7
- codex/views/admin/group.py +2 -6
- codex/views/admin/library.py +7 -20
- codex/views/admin/stats.py +7 -66
- codex/views/admin/tasks.py +24 -11
- codex/views/admin/user.py +3 -9
- codex/views/auth.py +30 -53
- codex/views/bookmark.py +7 -11
- codex/views/browser/{browser_annotations.py → annotations.py} +90 -181
- codex/views/browser/base.py +23 -88
- codex/views/browser/breadcrumbs.py +202 -0
- codex/views/browser/browser.py +64 -342
- codex/views/browser/choices.py +22 -54
- codex/views/browser/cover.py +177 -0
- codex/views/browser/filters/annotations.py +24 -23
- codex/views/browser/filters/bookmark.py +9 -13
- codex/views/browser/filters/field.py +5 -5
- codex/views/browser/filters/group.py +18 -10
- codex/views/browser/filters/search.py +30 -18
- codex/views/browser/metadata.py +22 -31
- codex/views/browser/{browser_order_by.py → order_by.py} +2 -6
- codex/views/browser/paginate.py +106 -0
- codex/views/browser/{session.py → settings.py} +4 -3
- codex/views/browser/title.py +48 -0
- codex/views/browser/validate.py +158 -0
- codex/views/const.py +109 -0
- codex/views/download.py +5 -8
- codex/views/frontend.py +4 -2
- codex/views/mtime.py +72 -0
- codex/views/opds/auth.py +17 -0
- codex/views/opds/authentication_v1.py +46 -45
- codex/views/opds/binary.py +5 -10
- codex/views/opds/const.py +13 -0
- codex/views/opds/urls.py +6 -4
- codex/views/opds/util.py +4 -12
- codex/views/opds/v1/entry/links.py +18 -33
- codex/views/opds/v1/facets.py +7 -7
- codex/views/opds/v1/feed.py +52 -40
- codex/views/opds/v1/links.py +7 -5
- codex/views/opds/v1/opensearch_v1.py +2 -6
- codex/views/opds/v2/feed.py +22 -23
- codex/views/opds/v2/links.py +4 -2
- codex/views/opds/v2/publications.py +34 -54
- codex/views/opds/v2/top_links.py +2 -2
- codex/views/public.py +36 -0
- codex/views/reader/arcs.py +101 -0
- codex/views/reader/books.py +199 -0
- codex/views/reader/page.py +3 -7
- codex/views/reader/reader.py +56 -301
- codex/views/reader/{session.py → settings.py} +4 -2
- codex/views/session.py +77 -103
- codex/views/settings.py +34 -0
- codex/views/template.py +3 -3
- codex/views/util.py +63 -0
- codex/views/version.py +2 -2
- {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/METADATA +19 -14
- {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/RECORD +373 -362
- codex/librarian/importer/covers.py +0 -47
- 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-BS5fdM4M.a5d446e4aeb3.js +0 -1
- codex/static_root/assets/VCheckbox-BS5fdM4M.a5d446e4aeb3.js.br +0 -0
- codex/static_root/assets/VCheckbox-BS5fdM4M.a5d446e4aeb3.js.gz +0 -0
- codex/static_root/assets/VCheckbox-BS5fdM4M.js +0 -1
- codex/static_root/assets/VCheckbox-BS5fdM4M.js.br +0 -0
- codex/static_root/assets/VCheckbox-BS5fdM4M.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-m4IbdjEe.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-m4IbdjEe.js.gz +0 -0
- codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js +0 -1
- codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js.br +0 -0
- codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js.gz +0 -0
- codex/static_root/assets/VCombobox-jg7SI5mn.js +0 -1
- codex/static_root/assets/VCombobox-jg7SI5mn.js.br +0 -0
- codex/static_root/assets/VCombobox-jg7SI5mn.js.gz +0 -0
- codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js +0 -1
- codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js.br +0 -0
- codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js.gz +0 -0
- codex/static_root/assets/VDialog-DiixpkQL.js +0 -1
- codex/static_root/assets/VDialog-DiixpkQL.js.br +0 -0
- codex/static_root/assets/VDialog-DiixpkQL.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js +0 -1
- codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Db5mPFue.js +0 -1
- codex/static_root/assets/VExpansionPanels-Db5mPFue.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Db5mPFue.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js +0 -1
- codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DULWiMSI.js +0 -1
- codex/static_root/assets/VRadioGroup-DULWiMSI.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DULWiMSI.js.gz +0 -0
- codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js +0 -1
- codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js.br +0 -0
- codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js.gz +0 -0
- codex/static_root/assets/VSelect-BnoCfCLY.js +0 -1
- codex/static_root/assets/VSelect-BnoCfCLY.js.br +0 -0
- codex/static_root/assets/VSelect-BnoCfCLY.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-BSL0qeL9.5b5835d286df.js +0 -1
- codex/static_root/assets/VSelectionControl-BSL0qeL9.5b5835d286df.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BSL0qeL9.5b5835d286df.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-BSL0qeL9.js +0 -1
- codex/static_root/assets/VSelectionControl-BSL0qeL9.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BSL0qeL9.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js +0 -1
- codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-BQtz8h9p.js +0 -1
- codex/static_root/assets/VSlideGroup-BQtz8h9p.js.br +0 -0
- codex/static_root/assets/VSlideGroup-BQtz8h9p.js.gz +0 -0
- codex/static_root/assets/VTable-C0gAriFq.fa6e4b60d4ef.js.br +0 -0
- codex/static_root/assets/VTable-C0gAriFq.fa6e4b60d4ef.js.gz +0 -0
- codex/static_root/assets/VTable-C0gAriFq.js.br +0 -0
- codex/static_root/assets/VTable-C0gAriFq.js.gz +0 -0
- codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js +0 -1
- codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js.br +0 -0
- codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js.gz +0 -0
- codex/static_root/assets/VTextField-Da_Hs7ls.js +0 -1
- codex/static_root/assets/VTextField-Da_Hs7ls.js.br +0 -0
- codex/static_root/assets/VTextField-Da_Hs7ls.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CFhKCj49.b2458002a937.js.br +0 -0
- codex/static_root/assets/VWindowItem-CFhKCj49.b2458002a937.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CFhKCj49.js.br +0 -0
- codex/static_root/assets/VWindowItem-CFhKCj49.js.gz +0 -0
- codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js +0 -1
- codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js.br +0 -0
- codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js.gz +0 -0
- codex/static_root/assets/admin-BpGFsvOH.js +0 -1
- codex/static_root/assets/admin-BpGFsvOH.js.br +0 -0
- codex/static_root/assets/admin-BpGFsvOH.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js +0 -30
- codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js +0 -30
- codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js.gz +0 -0
- codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js +0 -1
- codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js.br +0 -0
- codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js.gz +0 -0
- codex/static_root/assets/browser-CRPvEdqv.js +0 -1
- codex/static_root/assets/browser-CRPvEdqv.js.br +0 -0
- codex/static_root/assets/browser-CRPvEdqv.js.gz +0 -0
- codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css +0 -1
- codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css.br +0 -0
- codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css.gz +0 -0
- codex/static_root/assets/browser-Zqc9cR6T.css +0 -1
- codex/static_root/assets/browser-Zqc9cR6T.css.br +0 -0
- codex/static_root/assets/browser-Zqc9cR6T.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-JYM9aAR9.f71d378f4fa6.js.br +0 -0
- codex/static_root/assets/change-password-dialog-JYM9aAR9.f71d378f4fa6.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-JYM9aAR9.js.br +0 -0
- codex/static_root/assets/change-password-dialog-JYM9aAR9.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.515858dd21e8.js.br +0 -0
- codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.515858dd21e8.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.js.br +0 -0
- codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.js.gz +0 -0
- codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js +0 -1
- codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js.br +0 -0
- codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js.gz +0 -0
- codex/static_root/assets/datetime-column-BT1MSP82.js +0 -1
- codex/static_root/assets/datetime-column-BT1MSP82.js.br +0 -0
- codex/static_root/assets/datetime-column-BT1MSP82.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-frSNsexx.eab1570bd32d.js +0 -1
- codex/static_root/assets/filter-frSNsexx.eab1570bd32d.js.br +0 -0
- codex/static_root/assets/filter-frSNsexx.eab1570bd32d.js.gz +0 -0
- codex/static_root/assets/filter-frSNsexx.js +0 -1
- codex/static_root/assets/filter-frSNsexx.js.br +0 -0
- codex/static_root/assets/filter-frSNsexx.js.gz +0 -0
- codex/static_root/assets/flag-tab-Ci_XcDT0.a5ea640a08d6.js.br +0 -0
- codex/static_root/assets/flag-tab-Ci_XcDT0.a5ea640a08d6.js.gz +0 -0
- codex/static_root/assets/flag-tab-Ci_XcDT0.js.br +0 -0
- codex/static_root/assets/flag-tab-Ci_XcDT0.js.gz +0 -0
- codex/static_root/assets/group-tab-DFma2R-E.879c37b15bbd.js.br +0 -0
- codex/static_root/assets/group-tab-DFma2R-E.879c37b15bbd.js.gz +0 -0
- codex/static_root/assets/group-tab-DFma2R-E.js.br +0 -0
- codex/static_root/assets/group-tab-DFma2R-E.js.gz +0 -0
- codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js +0 -1
- codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js.br +0 -0
- codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js.gz +0 -0
- codex/static_root/assets/http-error-B3fQGxpt.js +0 -1
- codex/static_root/assets/http-error-B3fQGxpt.js.br +0 -0
- codex/static_root/assets/http-error-B3fQGxpt.js.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +0 -1
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.js +0 -1
- codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
- codex/static_root/assets/library-tab-V8DUGoHN.809c548ff347.js.br +0 -0
- codex/static_root/assets/library-tab-V8DUGoHN.809c548ff347.js.gz +0 -0
- codex/static_root/assets/library-tab-V8DUGoHN.js.br +0 -0
- codex/static_root/assets/library-tab-V8DUGoHN.js.gz +0 -0
- codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js +0 -32
- codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js.br +0 -0
- codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js.gz +0 -0
- codex/static_root/assets/main-Cavvqkyp.js +0 -32
- codex/static_root/assets/main-Cavvqkyp.js.br +0 -0
- codex/static_root/assets/main-Cavvqkyp.js.gz +0 -0
- codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
- codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css +0 -1
- codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-ClZui9Zf.css +0 -1
- codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js +0 -1
- codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js +0 -1
- codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js.gz +0 -0
- codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js +0 -35
- codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js.br +0 -0
- codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js.gz +0 -0
- codex/static_root/assets/pdf-doc-4PKLN-Z3.js +0 -35
- codex/static_root/assets/pdf-doc-4PKLN-Z3.js.br +0 -0
- codex/static_root/assets/pdf-doc-4PKLN-Z3.js.gz +0 -0
- codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js +0 -2
- codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js.br +0 -0
- codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js.gz +0 -0
- codex/static_root/assets/reader-C39uGGxn.js +0 -2
- codex/static_root/assets/reader-C39uGGxn.js.br +0 -0
- codex/static_root/assets/reader-C39uGGxn.js.gz +0 -0
- codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css +0 -1
- codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css.br +0 -0
- codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css.gz +0 -0
- codex/static_root/assets/reader-N2Ei_y2K.css +0 -1
- codex/static_root/assets/reader-N2Ei_y2K.css.br +0 -0
- codex/static_root/assets/reader-N2Ei_y2K.css.gz +0 -0
- codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js +0 -1
- codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js.br +0 -0
- codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js.gz +0 -0
- codex/static_root/assets/relation-chips-CAC5ytqH.js +0 -1
- codex/static_root/assets/relation-chips-CAC5ytqH.js.br +0 -0
- codex/static_root/assets/relation-chips-CAC5ytqH.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js +0 -2
- codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js.br +0 -0
- codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CT73ieiV.js +0 -2
- codex/static_root/assets/settings-drawer-CT73ieiV.js.br +0 -0
- codex/static_root/assets/settings-drawer-CT73ieiV.js.gz +0 -0
- codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js +0 -1
- codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js.br +0 -0
- codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js.gz +0 -0
- codex/static_root/assets/stats-tab-C6qAnV6-.js +0 -1
- codex/static_root/assets/stats-tab-C6qAnV6-.js.br +0 -0
- codex/static_root/assets/stats-tab-C6qAnV6-.js.gz +0 -0
- codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js +0 -1
- codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js.br +0 -0
- codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js.gz +0 -0
- codex/static_root/assets/task-tab-JcG2vvgt.js +0 -1
- codex/static_root/assets/task-tab-JcG2vvgt.js.br +0 -0
- codex/static_root/assets/task-tab-JcG2vvgt.js.gz +0 -0
- codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +0 -1
- codex/static_root/assets/to-case-kUm8NPpW.js +0 -1
- codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js +0 -1
- codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js.br +0 -0
- codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js.gz +0 -0
- codex/static_root/assets/unauthorized-28bmiwAU.js +0 -1
- codex/static_root/assets/unauthorized-28bmiwAU.js.br +0 -0
- codex/static_root/assets/unauthorized-28bmiwAU.js.gz +0 -0
- codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js +0 -1
- codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js.br +0 -0
- codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js.gz +0 -0
- codex/static_root/assets/user-tab-BOq4BMMc.js +0 -1
- codex/static_root/assets/user-tab-BOq4BMMc.js.br +0 -0
- codex/static_root/assets/user-tab-BOq4BMMc.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.23fedac2b9ba.json +0 -1
- codex/static_root/js/choices.23fedac2b9ba.json.br +0 -0
- codex/static_root/js/choices.23fedac2b9ba.json.gz +0 -0
- codex/static_root/manifest.ad9da9714e46.json +0 -642
- codex/static_root/manifest.ad9da9714e46.json.br +0 -0
- codex/static_root/manifest.ad9da9714e46.json.gz +0 -0
- codex/views/browser/browser_breadcrumbs.py +0 -175
- codex/views/browser/const.py +0 -3
- codex/views/cover.py +0 -78
- {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/LICENSE +0 -0
- {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/WHEEL +0 -0
- {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/entry_points.txt +0 -0
|
@@ -9,57 +9,58 @@ from rest_framework.response import Response
|
|
|
9
9
|
|
|
10
10
|
from codex.serializers.opds.authentication import OPDSAuthentication1Serializer
|
|
11
11
|
|
|
12
|
+
_LOGO_SIZE = 512
|
|
13
|
+
_DOC = MappingProxyType(
|
|
14
|
+
{
|
|
15
|
+
"id": reverse_lazy("opds:authentication:v1"),
|
|
16
|
+
"title": "Codex",
|
|
17
|
+
"description": "Codex OPDS Syndication",
|
|
18
|
+
"links": [
|
|
19
|
+
{
|
|
20
|
+
"rel": "logo",
|
|
21
|
+
"href": staticfiles_storage.url("img/logo.svg"),
|
|
22
|
+
"type": "image/svg+xml",
|
|
23
|
+
"width": _LOGO_SIZE,
|
|
24
|
+
"height": _LOGO_SIZE,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"rel": "help",
|
|
28
|
+
"href": "https://github.com/ajslater/codex",
|
|
29
|
+
"type": "text/html",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"rel": "register",
|
|
33
|
+
"href": reverse_lazy("app:start"),
|
|
34
|
+
"type": "text/html",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
"authentication": [
|
|
38
|
+
{
|
|
39
|
+
"type": "http://opds-spec.org/auth/basic",
|
|
40
|
+
"labels": {"login": "Username", "password": "Password"},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
# XXX Out of spec type
|
|
44
|
+
"type": "cookie",
|
|
45
|
+
"links": [
|
|
46
|
+
{
|
|
47
|
+
"rel": "authenticate",
|
|
48
|
+
"href": reverse_lazy("app:start"),
|
|
49
|
+
"type": "text/html",
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
12
57
|
|
|
13
58
|
class OPDSAuthentication1View(GenericAPIView):
|
|
14
59
|
"""Authentication document."""
|
|
15
60
|
|
|
16
61
|
serializer_class = OPDSAuthentication1Serializer
|
|
17
62
|
|
|
18
|
-
DOC = MappingProxyType(
|
|
19
|
-
{
|
|
20
|
-
"id": reverse_lazy("opds:authentication:v1"),
|
|
21
|
-
"title": "Codex",
|
|
22
|
-
"description": "Codex OPDS Syndication",
|
|
23
|
-
"links": [
|
|
24
|
-
{
|
|
25
|
-
"rel": "logo",
|
|
26
|
-
"href": staticfiles_storage.url("img/logo.svg"),
|
|
27
|
-
"type": "image/svg+xml",
|
|
28
|
-
"width": 512,
|
|
29
|
-
"height": 512,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"rel": "help",
|
|
33
|
-
"href": "https://github.com/ajslater/codex",
|
|
34
|
-
"type": "text/html",
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"rel": "register",
|
|
38
|
-
"href": reverse_lazy("app:start"),
|
|
39
|
-
"type": "text/html",
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
"authentication": [
|
|
43
|
-
{
|
|
44
|
-
"type": "http://opds-spec.org/auth/basic",
|
|
45
|
-
"labels": {"login": "Username", "password": "Password"},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
# XXX Out of spec type
|
|
49
|
-
"type": "cookie",
|
|
50
|
-
"links": [
|
|
51
|
-
{
|
|
52
|
-
"rel": "authenticate",
|
|
53
|
-
"href": reverse_lazy("app:start"),
|
|
54
|
-
"type": "text/html",
|
|
55
|
-
}
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
|
|
62
63
|
def get(self, *args, **kwargs):
|
|
63
64
|
"""Fill in the authentication dict."""
|
|
64
|
-
serializer = self.get_serializer(
|
|
65
|
+
serializer = self.get_serializer(_DOC)
|
|
65
66
|
return Response(serializer.data)
|
codex/views/opds/binary.py
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
"""Binary views with Basic Authentication added."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from codex.views.cover import CoverView
|
|
3
|
+
from codex.views.browser.cover import CoverView
|
|
6
4
|
from codex.views.download import DownloadView
|
|
5
|
+
from codex.views.opds.auth import OPDSAuthMixin
|
|
7
6
|
from codex.views.reader.page import ReaderPageView
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
class
|
|
11
|
-
"""Add Basic Auth."""
|
|
12
|
-
|
|
13
|
-
authentication_classes = (BasicAuthentication, SessionAuthentication)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class OPDSCoverView(OPDSAuthMixin, CoverView):
|
|
9
|
+
class OPDSCoverView(CoverView):
|
|
17
10
|
"""Cover View with Basic Auth."""
|
|
18
11
|
|
|
12
|
+
# Panels sometimes doesn't send auth with cover requests.
|
|
13
|
+
|
|
19
14
|
|
|
20
15
|
class OPDSDownloadView(OPDSAuthMixin, DownloadView):
|
|
21
16
|
"""Download View with Basic Auth."""
|
codex/views/opds/const.py
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
from collections.abc import Mapping
|
|
4
4
|
from types import MappingProxyType
|
|
5
5
|
|
|
6
|
+
from codex.models import (
|
|
7
|
+
Character,
|
|
8
|
+
Genre,
|
|
9
|
+
Location,
|
|
10
|
+
SeriesGroup,
|
|
11
|
+
StoryArc,
|
|
12
|
+
Tag,
|
|
13
|
+
Team,
|
|
14
|
+
)
|
|
15
|
+
|
|
6
16
|
BLANK_TITLE = "Unknown"
|
|
7
17
|
AUTHOR_ROLES = {"Writer"}
|
|
8
18
|
|
|
@@ -71,3 +81,6 @@ class MimeType:
|
|
|
71
81
|
}
|
|
72
82
|
)
|
|
73
83
|
OCTET = "application/octet-stream"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
OPDS_M2M_MODELS = (Character, Genre, Location, SeriesGroup, StoryArc, Tag, Team)
|
codex/views/opds/urls.py
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"""OPDS URLs."""
|
|
2
2
|
|
|
3
3
|
from django.urls import reverse
|
|
4
|
-
from rest_framework.generics import GenericAPIView
|
|
5
4
|
from rest_framework.response import Response
|
|
6
5
|
|
|
7
6
|
from codex.serializers.choices import DEFAULTS
|
|
8
7
|
from codex.serializers.opds.urls import OPDSURLsSerializer
|
|
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
|
|
|
12
13
|
|
|
13
|
-
class OPDSURLsView(
|
|
14
|
+
class OPDSURLsView(AuthGenericAPIView):
|
|
14
15
|
"""OPDS URLs."""
|
|
15
16
|
|
|
16
17
|
serializer_class = OPDSURLsSerializer
|
|
@@ -19,7 +20,8 @@ class OPDSURLsView(GenericAPIView):
|
|
|
19
20
|
"""Resolve the urls."""
|
|
20
21
|
obj = {}
|
|
21
22
|
route = DEFAULTS["breadcrumbs"][0]
|
|
22
|
-
|
|
23
|
+
route = pop_name(route)
|
|
24
|
+
for version in _OPDS_VERSIONS:
|
|
23
25
|
key = f"v{version}"
|
|
24
26
|
name = f"opds:v{version}:feed"
|
|
25
27
|
value = reverse(name, kwargs=route)
|
codex/views/opds/util.py
CHANGED
|
@@ -6,19 +6,12 @@ from django.urls import reverse
|
|
|
6
6
|
from django.utils.http import urlencode
|
|
7
7
|
|
|
8
8
|
from codex.models import (
|
|
9
|
-
Character,
|
|
10
9
|
Contributor,
|
|
11
10
|
ContributorPerson,
|
|
12
|
-
Genre,
|
|
13
|
-
Location,
|
|
14
|
-
SeriesGroup,
|
|
15
|
-
StoryArc,
|
|
16
|
-
Tag,
|
|
17
|
-
Team,
|
|
18
11
|
)
|
|
19
12
|
from codex.serializers.choices import DEFAULTS
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
from codex.views.auth import GroupACLMixin
|
|
14
|
+
from codex.views.opds.const import OPDS_M2M_MODELS
|
|
22
15
|
|
|
23
16
|
|
|
24
17
|
def update_href_query_params(href, old_query_params, new_query_params=None):
|
|
@@ -65,9 +58,7 @@ def get_m2m_objects(pks) -> dict:
|
|
|
65
58
|
cats = {}
|
|
66
59
|
for model in OPDS_M2M_MODELS:
|
|
67
60
|
table = model.__name__.lower()
|
|
68
|
-
rel =
|
|
69
|
-
if model == StoryArc:
|
|
70
|
-
rel = "storyarcnumber__" + rel
|
|
61
|
+
rel = GroupACLMixin.get_rel_prefix(model)
|
|
71
62
|
comic_filter = {rel + "__in": pks}
|
|
72
63
|
qs = model.objects.filter(**comic_filter).order_by("name").only("name")
|
|
73
64
|
cats[table] = qs
|
|
@@ -81,6 +72,7 @@ def full_redirect_view(url_name):
|
|
|
81
72
|
def func(request):
|
|
82
73
|
"""Redirect to view, forwarding query strings and auth."""
|
|
83
74
|
kwargs = DEFAULTS["breadcrumbs"][0]
|
|
75
|
+
kwargs.pop("name", None)
|
|
84
76
|
url = reverse(url_name, kwargs=kwargs)
|
|
85
77
|
|
|
86
78
|
# Forward the query string.
|
|
@@ -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
|
|
@@ -77,7 +78,7 @@ DEFAULT_FACETS = {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
|
|
80
|
-
class
|
|
81
|
+
class OPDS1FacetsView(BrowserView):
|
|
81
82
|
"""OPDS 1 Facets methods."""
|
|
82
83
|
|
|
83
84
|
OPDS = 1
|
|
@@ -89,15 +90,14 @@ class FacetsMixin(BrowserView):
|
|
|
89
90
|
obj = MappingProxyType({})
|
|
90
91
|
|
|
91
92
|
def _facet(self, kwargs, facet_group, facet_title, new_query_params):
|
|
93
|
+
kwargs = pop_name(kwargs)
|
|
92
94
|
href = reverse("opds:v1:feed", kwargs=kwargs)
|
|
93
95
|
facet_active = False
|
|
94
96
|
for key, val in new_query_params.items():
|
|
95
|
-
if self.request.
|
|
97
|
+
if self.request.GET.get(key) == val:
|
|
96
98
|
facet_active = True
|
|
97
99
|
break
|
|
98
|
-
href = update_href_query_params(
|
|
99
|
-
href, self.request.query_params, new_query_params
|
|
100
|
-
)
|
|
100
|
+
href = update_href_query_params(href, self.request.GET, new_query_params)
|
|
101
101
|
|
|
102
102
|
title = " ".join(filter(None, (facet_group.title_prefix, facet_title))).strip()
|
|
103
103
|
return OPDS1Link(
|
|
@@ -118,7 +118,7 @@ class FacetsMixin(BrowserView):
|
|
|
118
118
|
ids=item.get("pks"),
|
|
119
119
|
name=name,
|
|
120
120
|
)
|
|
121
|
-
qps = {**self.request.
|
|
121
|
+
qps = {**self.request.GET}
|
|
122
122
|
qps.update(query_params)
|
|
123
123
|
zero_pad = self.obj["zero_pad"]
|
|
124
124
|
data = OPDS1EntryData(
|
|
@@ -131,7 +131,7 @@ class FacetsMixin(BrowserView):
|
|
|
131
131
|
default_val = DEFAULT_FACETS.get(facet_group.query_param)
|
|
132
132
|
if facet.value == default_val:
|
|
133
133
|
compare += [None]
|
|
134
|
-
return self.request.
|
|
134
|
+
return self.request.GET.get(facet_group.query_param) in compare
|
|
135
135
|
|
|
136
136
|
@staticmethod
|
|
137
137
|
def _did_special_group_change(group, facet_group):
|
codex/views/opds/v1/feed.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"""OPDS v1 feed."""
|
|
2
2
|
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
3
|
from types import MappingProxyType
|
|
5
4
|
from typing import TYPE_CHECKING, Any
|
|
6
5
|
|
|
7
6
|
from comicbox.box import Comicbox
|
|
8
7
|
from drf_spectacular.utils import extend_schema
|
|
9
|
-
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
|
10
8
|
from rest_framework.response import Response
|
|
11
9
|
from rest_framework.throttling import ScopedRateThrottle
|
|
12
10
|
|
|
@@ -17,17 +15,16 @@ from codex.serializers.opds.v1 import (
|
|
|
17
15
|
OPDS1TemplateSerializer,
|
|
18
16
|
)
|
|
19
17
|
from codex.views.browser.browser import BrowserView
|
|
20
|
-
from codex.views.
|
|
21
|
-
from codex.views.
|
|
18
|
+
from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
|
|
19
|
+
from codex.views.opds.auth import OPDSTemplateView
|
|
22
20
|
from codex.views.opds.const import BLANK_TITLE, MimeType
|
|
23
21
|
from codex.views.opds.v1.entry.data import OPDS1EntryData
|
|
24
22
|
from codex.views.opds.v1.entry.entry import OPDS1Entry
|
|
25
23
|
from codex.views.opds.v1.links import (
|
|
26
|
-
|
|
24
|
+
OPDS1LinksView,
|
|
27
25
|
RootTopLinks,
|
|
28
26
|
TopLinks,
|
|
29
27
|
)
|
|
30
|
-
from codex.views.template import CodexXMLTemplateView
|
|
31
28
|
|
|
32
29
|
if TYPE_CHECKING:
|
|
33
30
|
from collections.abc import Mapping
|
|
@@ -44,20 +41,19 @@ class OpdsNs:
|
|
|
44
41
|
ACQUISITION = "http://opds-spec.org/2010/acquisition"
|
|
45
42
|
|
|
46
43
|
|
|
47
|
-
class
|
|
44
|
+
class UserAgentNames:
|
|
48
45
|
"""Control whether to hack in facets with nav links."""
|
|
49
46
|
|
|
50
|
-
CLIENT_REORDERS = ("Chunky"
|
|
51
|
-
FACET_SUPPORT = ("yar"
|
|
52
|
-
SIMPLE_DOWNLOAD_MIME_TYPES = ("PocketBook"
|
|
53
|
-
# 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:
|
|
54
51
|
# "Panels", "Chunky"
|
|
55
52
|
|
|
56
53
|
|
|
57
|
-
class OPDS1FeedView(
|
|
54
|
+
class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
58
55
|
"""OPDS 1 Feed."""
|
|
59
56
|
|
|
60
|
-
authentication_classes = (SessionAuthentication, BasicAuthentication)
|
|
61
57
|
template_name = "opds_v1/index.xml"
|
|
62
58
|
serializer_class = OPDS1TemplateSerializer
|
|
63
59
|
throttle_classes = (ScopedRateThrottle,)
|
|
@@ -90,7 +86,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
90
86
|
"""Create the feed title."""
|
|
91
87
|
result = ""
|
|
92
88
|
try:
|
|
93
|
-
browser_title: Mapping[str, Any] = self.obj.get("
|
|
89
|
+
browser_title: Mapping[str, Any] = self.obj.get("title") # type: ignore
|
|
94
90
|
if browser_title:
|
|
95
91
|
parent_name = browser_title.get("parent_name", "All")
|
|
96
92
|
pks = self.kwargs["pks"]
|
|
@@ -107,10 +103,9 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
107
103
|
|
|
108
104
|
@property
|
|
109
105
|
def updated(self):
|
|
110
|
-
"""
|
|
106
|
+
"""Use mtime for updated."""
|
|
111
107
|
try:
|
|
112
|
-
|
|
113
|
-
return datetime.fromtimestamp(ts, tz=timezone.utc) # type: ignore
|
|
108
|
+
return self.obj.get("mtime", "")
|
|
114
109
|
except Exception:
|
|
115
110
|
LOG.exception("Getting OPDS v1 updated")
|
|
116
111
|
|
|
@@ -145,7 +140,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
145
140
|
entries += [
|
|
146
141
|
OPDS1Entry(
|
|
147
142
|
obj,
|
|
148
|
-
self.request.
|
|
143
|
+
self.request.GET,
|
|
149
144
|
data,
|
|
150
145
|
title_filename_fallback=fallback,
|
|
151
146
|
)
|
|
@@ -165,9 +160,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
165
160
|
entries += self.facets(entries=True, root=at_root)
|
|
166
161
|
|
|
167
162
|
entries += self._get_entries_section("groups", False)
|
|
168
|
-
metadata = (
|
|
169
|
-
self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
|
|
170
|
-
)
|
|
163
|
+
metadata = self.request.GET.get("opdsMetadata", "").lower() not in FALSY
|
|
171
164
|
entries += self._get_entries_section("books", metadata)
|
|
172
165
|
except Exception:
|
|
173
166
|
LOG.exception("Getting OPDS v1 entries")
|
|
@@ -175,14 +168,17 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
175
168
|
|
|
176
169
|
def _ensure_page_counts(self):
|
|
177
170
|
"""Ensure page counts on books with just in time comicbox."""
|
|
171
|
+
if self.admin_flags.get("import_metadata"):
|
|
172
|
+
return
|
|
178
173
|
books_qs: QuerySet = self.obj["books"] # type: ignore
|
|
179
174
|
import_pks = set()
|
|
180
175
|
new_books = []
|
|
181
176
|
for book in books_qs:
|
|
182
|
-
if
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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()
|
|
186
182
|
new_books.append(book)
|
|
187
183
|
import_pks.add(book.pk)
|
|
188
184
|
if new_books:
|
|
@@ -194,7 +190,24 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
194
190
|
|
|
195
191
|
def get_object(self): # type: ignore
|
|
196
192
|
"""Get the browser page and serialize it for this subclass."""
|
|
197
|
-
|
|
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
|
+
|
|
198
211
|
self.is_aq_feed = self.model_group in ("c", "f")
|
|
199
212
|
|
|
200
213
|
self._ensure_page_counts()
|
|
@@ -204,22 +217,21 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
204
217
|
|
|
205
218
|
def _set_user_agent_variables(self):
|
|
206
219
|
"""Set User Agent variables."""
|
|
207
|
-
# defaults in FacetsMixin
|
|
208
220
|
user_agent = self.request.headers.get("User-Agent")
|
|
209
221
|
if not user_agent:
|
|
210
222
|
return
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
|
223
235
|
|
|
224
236
|
def set_opds_request_type(self):
|
|
225
237
|
"""Set the opds request type variables."""
|
|
@@ -232,7 +244,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
232
244
|
self.acquisition_groups = frozenset({*self.valid_nav_groups[-2:]} | {"c"})
|
|
233
245
|
self.is_opds_1_acquisition = group in self.acquisition_groups
|
|
234
246
|
self.is_opds_metadata = (
|
|
235
|
-
self.request.
|
|
247
|
+
self.request.GET.get("opdsMetadata", "").lower() not in FALSY
|
|
236
248
|
)
|
|
237
249
|
|
|
238
250
|
def init_request(self):
|
codex/views/opds/v1/links.py
CHANGED
|
@@ -14,7 +14,8 @@ from codex.views.opds.util import update_href_query_params
|
|
|
14
14
|
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
|
-
from codex.views.opds.v1.facets import
|
|
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
|
|
|
@@ -101,13 +102,13 @@ class RootTopLinks:
|
|
|
101
102
|
ALL = (NEW, FEATURED, LAST_READ)
|
|
102
103
|
|
|
103
104
|
|
|
104
|
-
class
|
|
105
|
+
class OPDS1LinksView(OPDS1FacetsView):
|
|
105
106
|
"""OPDS 1 Links methods."""
|
|
106
107
|
|
|
107
108
|
# overwritten in get_object()
|
|
108
109
|
DEFAULT_ROUTE = MappingProxyType(
|
|
109
110
|
{
|
|
110
|
-
**
|
|
111
|
+
**OPDS1FacetsView.DEFAULT_ROUTE,
|
|
111
112
|
"name": "opds:v1:feed",
|
|
112
113
|
}
|
|
113
114
|
)
|
|
@@ -120,7 +121,7 @@ class LinksMixin(FacetsMixin):
|
|
|
120
121
|
return False
|
|
121
122
|
|
|
122
123
|
for key, value in top_link.query_params.items():
|
|
123
|
-
if str(self.request.
|
|
124
|
+
if str(self.request.GET.get(key)) != str(value):
|
|
124
125
|
return False
|
|
125
126
|
|
|
126
127
|
return True
|
|
@@ -128,7 +129,8 @@ class LinksMixin(FacetsMixin):
|
|
|
128
129
|
def _link(self, kwargs, rel, query_params=None, mime_type=MimeType.NAV):
|
|
129
130
|
"""Create a link."""
|
|
130
131
|
if query_params is None:
|
|
131
|
-
query_params = self.request.
|
|
132
|
+
query_params = self.request.GET
|
|
133
|
+
kwargs = pop_name(kwargs)
|
|
132
134
|
href = reverse("opds:v1:feed", kwargs=kwargs)
|
|
133
135
|
href = update_href_query_params(href, query_params)
|
|
134
136
|
return OPDS1Link(rel, href, mime_type)
|
|
@@ -2,19 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from drf_spectacular.types import OpenApiTypes
|
|
4
4
|
from drf_spectacular.utils import extend_schema
|
|
5
|
-
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
|
6
5
|
from rest_framework.throttling import ScopedRateThrottle
|
|
7
6
|
|
|
8
|
-
from codex.views.auth import
|
|
9
|
-
from codex.views.template import CodexXMLTemplateView
|
|
7
|
+
from codex.views.opds.auth import OPDSTemplateView
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
@extend_schema(responses={("200", "application/xml"): OpenApiTypes.BYTE})
|
|
13
|
-
class OpenSearch1View(
|
|
11
|
+
class OpenSearch1View(OPDSTemplateView):
|
|
14
12
|
"""OpenSearchView."""
|
|
15
13
|
|
|
16
|
-
authentication_classes = (SessionAuthentication, BasicAuthentication)
|
|
17
|
-
permission_classes = (IsAuthenticatedOrEnabledNonUsers,)
|
|
18
14
|
template_name = "opds_v1/opensearch_v1.xml"
|
|
19
15
|
content_type = "application/xml"
|
|
20
16
|
throttle_classes = (ScopedRateThrottle,)
|