codex 1.6.0a5__py3-none-any.whl → 1.6.0a7__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/librarian/covers/purge.py +1 -2
- codex/librarian/importer/aggregate_metadata.py +2 -10
- codex/librarian/importer/cache.py +47 -0
- codex/librarian/importer/const.py +8 -0
- codex/librarian/importer/deleted.py +45 -7
- codex/librarian/importer/importerd.py +11 -1
- codex/librarian/importer/update_comics.py +0 -2
- codex/librarian/janitor/janitor.py +1 -1
- codex/librarian/librariand.py +1 -1
- codex/migrations/0029_choices_adminflag_and_timestamp.py +44 -0
- codex/models/admin.py +0 -1
- codex/permissions.py +1 -1
- codex/serializers/admin.py +1 -2
- codex/serializers/browser/filters.py +8 -101
- codex/serializers/browser/mixins.py +20 -3
- codex/serializers/browser/page.py +2 -3
- codex/serializers/browser/settings.py +2 -0
- 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/static_root/assets/VCheckbox-BOUtyxuo.c690f0cdbe48.js +1 -0
- 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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -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 +1 -0
- 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 +1 -0
- codex/static_root/assets/VSelect-DgfIKRnC.js.br +0 -0
- codex/static_root/assets/VSelect-DgfIKRnC.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CGu0oz0K.3030b7a270d6.js +1 -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 +1 -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 +1 -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 +1 -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-FKMTcdkF.6c1eed84d25e.js → VTable-C7pKI7gU.087912f706f5.js} +1 -1
- 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-FKMTcdkF.js → VTable-C7pKI7gU.js} +1 -1
- 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-Dftcik85.js → VTextField-Bs8oq9mk.7999fef12a03.js} +1 -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-Dftcik85.cb2f1dda87e1.js → VTextField-Bs8oq9mk.js} +1 -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-CYCwnt3s.28025dc36c6a.js → VWindowItem-iyZ1XOkP.409f7d292253.js} +1 -1
- 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-CYCwnt3s.js → VWindowItem-iyZ1XOkP.js} +1 -1
- 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 +1 -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 +1 -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 +30 -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 +30 -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/{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-DbyAjCNh.577a228a966b.js +1 -0
- 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 +1 -0
- 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 +1 -0
- 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 +1 -0
- 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-BIBG6Bk8.3583021c124a.js → change-password-dialog-DZ9dP8_F.930f4cae3cb0.js} +1 -1
- 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-BIBG6Bk8.js → change-password-dialog-DZ9dP8_F.js} +1 -1
- 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-DARLmXRf.53fc6938fb8c.js → confirm-dialog-RP7JNStF.a752a7c1e697.js} +1 -1
- codex/static_root/assets/confirm-dialog-RP7JNStF.a752a7c1e697.js.br +0 -0
- codex/static_root/assets/confirm-dialog-RP7JNStF.a752a7c1e697.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-DARLmXRf.js → confirm-dialog-RP7JNStF.js} +1 -1
- codex/static_root/assets/confirm-dialog-RP7JNStF.js.br +0 -0
- codex/static_root/assets/confirm-dialog-RP7JNStF.js.gz +0 -0
- codex/static_root/assets/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-LpLgWpkf.16ce3324970f.js → datetime-column-Cu3WYyPj.54b4c3817b74.js} +1 -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-LpLgWpkf.js → datetime-column-Cu3WYyPj.js} +1 -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/filter-DAdGUt-1.3261cbcd50b5.js +1 -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 +1 -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 +1 -0
- 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 +1 -0
- 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-MK1lcjUU.98406d5babdc.js → group-tab-DEwh4jfB.a95097b5e320.js} +1 -1
- 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-MK1lcjUU.js → group-tab-DEwh4jfB.js} +1 -1
- 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 +1 -0
- 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 +1 -0
- 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-DYeEBYlT.a2a2ef088d1c.js → library-tab-DrXvD9B2.f765f9d3bae4.js} +1 -1
- 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-DYeEBYlT.js → library-tab-DrXvD9B2.js} +1 -1
- 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 +32 -0
- 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 +32 -0
- codex/static_root/assets/main-Dy9Uqpmh.js.br +0 -0
- codex/static_root/assets/main-Dy9Uqpmh.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-DbS_HOT1.81222ab51ef4.js +1 -0
- 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 +1 -0
- 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-ClZui9Zf.697d7ae11c21.css → pagination-toolbar-r6nbtWxg.4088ab042c0c.css} +1 -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-ClZui9Zf.css → pagination-toolbar-r6nbtWxg.css} +1 -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 +305 -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 +305 -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 +2 -0
- 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 +2 -0
- 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-CF3xt6JG.19fad7f62450.css → reader-OXJ1KVaW.a463b19d0508.css} +1 -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-CF3xt6JG.css → reader-OXJ1KVaW.css} +1 -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 +1 -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 +1 -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 +2 -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 +2 -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/{settings-drawer-BJOfMtqA.6471a50fc05b.css → settings-drawer-nZ9HvKSZ.01744e131806.css} +1 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
- codex/static_root/assets/{settings-drawer-BJOfMtqA.css → settings-drawer-nZ9HvKSZ.css} +1 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
- codex/static_root/assets/stats-tab-CQsK1n8O.544bf50ffb22.js +1 -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 +1 -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 +1 -0
- codex/static_root/assets/task-tab-C60TLP6e.63aa929a4ed6.js.br +0 -0
- codex/static_root/assets/task-tab-C60TLP6e.63aa929a4ed6.js.gz +0 -0
- codex/static_root/assets/task-tab-C60TLP6e.js +1 -0
- codex/static_root/assets/task-tab-C60TLP6e.js.br +0 -0
- codex/static_root/assets/task-tab-C60TLP6e.js.gz +0 -0
- codex/static_root/assets/to-case-CR9beRR0.21bb805fdab4.js +1 -0
- codex/static_root/assets/to-case-CR9beRR0.js +1 -0
- codex/static_root/assets/unauthorized-Bs7NSqea.c55650fac055.js +1 -0
- 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 +1 -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 +1 -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 +1 -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.079a01e0be1a.json +1 -0
- codex/static_root/js/choices.079a01e0be1a.json.br +0 -0
- codex/static_root/js/choices.079a01e0be1a.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 +629 -0
- codex/static_root/manifest.dad79c8475ae.json.br +0 -0
- codex/static_root/manifest.dad79c8475ae.json.gz +0 -0
- codex/static_root/manifest.json +265 -278
- 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 +2 -2
- codex/urls/api/reader.py +2 -2
- codex/urls/api/v3.py +2 -0
- codex/util.py +10 -0
- codex/views/admin/api_key.py +2 -6
- codex/views/admin/auth.py +30 -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 +12 -11
- codex/views/admin/user.py +3 -9
- codex/views/auth.py +31 -52
- codex/views/bookmark.py +7 -11
- codex/views/browser/{browser_annotations.py → annotations.py} +119 -78
- codex/views/browser/base.py +24 -72
- codex/views/browser/breadcrumbs.py +200 -0
- codex/views/browser/browser.py +64 -344
- codex/views/browser/choices.py +25 -33
- codex/views/browser/filters/annotations.py +2 -2
- codex/views/browser/filters/bookmark.py +9 -13
- codex/views/browser/filters/field.py +9 -6
- codex/views/browser/filters/group.py +8 -5
- codex/views/browser/filters/search.py +11 -8
- codex/views/browser/metadata.py +3 -7
- 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 +97 -0
- codex/views/cover.py +4 -6
- codex/views/download.py +5 -8
- codex/views/frontend.py +4 -2
- codex/views/mtime.py +70 -0
- codex/views/opds/auth.py +17 -0
- codex/views/opds/authentication_v1.py +46 -45
- codex/views/opds/binary.py +4 -9
- codex/views/opds/const.py +13 -0
- codex/views/opds/urls.py +5 -4
- codex/views/opds/util.py +2 -8
- codex/views/opds/v1/facets.py +6 -7
- codex/views/opds/v1/feed.py +10 -17
- codex/views/opds/v1/links.py +6 -5
- codex/views/opds/v1/opensearch_v1.py +2 -6
- codex/views/opds/v2/feed.py +12 -15
- codex/views/opds/v2/links.py +3 -2
- codex/views/opds/v2/publications.py +21 -20
- codex/views/opds/v2/top_links.py +2 -2
- codex/views/public.py +38 -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 +53 -298
- codex/views/reader/{session.py → settings.py} +4 -2
- codex/views/session.py +74 -102
- codex/views/settings.py +34 -0
- codex/views/template.py +3 -3
- codex/views/util.py +31 -0
- codex/views/utils.py +26 -0
- codex/views/version.py +2 -2
- {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/METADATA +12 -8
- {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/RECORD +344 -330
- codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js +0 -1
- codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js.br +0 -0
- codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js.gz +0 -0
- codex/static_root/assets/VCheckbox-BF_99tgB.js +0 -1
- codex/static_root/assets/VCheckbox-BF_99tgB.js.br +0 -0
- codex/static_root/assets/VCheckbox-BF_99tgB.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js +0 -1
- codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js +0 -1
- codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js.gz +0 -0
- codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js +0 -1
- codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js.br +0 -0
- codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js.gz +0 -0
- codex/static_root/assets/VCombobox-CTRWnOuM.js +0 -1
- codex/static_root/assets/VCombobox-CTRWnOuM.js.br +0 -0
- codex/static_root/assets/VCombobox-CTRWnOuM.js.gz +0 -0
- codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js +0 -1
- codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js.br +0 -0
- codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js.gz +0 -0
- codex/static_root/assets/VDialog-pzwzInEh.js +0 -1
- codex/static_root/assets/VDialog-pzwzInEh.js.br +0 -0
- codex/static_root/assets/VDialog-pzwzInEh.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js +0 -1
- codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CKSq1GpG.js +0 -1
- codex/static_root/assets/VExpansionPanels-CKSq1GpG.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CKSq1GpG.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js +0 -1
- codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-CW5GobGU.js +0 -1
- codex/static_root/assets/VRadioGroup-CW5GobGU.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CW5GobGU.js.gz +0 -0
- codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js +0 -1
- codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js.br +0 -0
- codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js.gz +0 -0
- codex/static_root/assets/VSelect-ChOlLRyH.js +0 -1
- codex/static_root/assets/VSelect-ChOlLRyH.js.br +0 -0
- codex/static_root/assets/VSelect-ChOlLRyH.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js +0 -1
- codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DKOhc2zB.js +0 -1
- codex/static_root/assets/VSelectionControl-DKOhc2zB.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DKOhc2zB.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js +0 -1
- codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js.br +0 -0
- codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-zFz5_byd.js +0 -1
- codex/static_root/assets/VSlideGroup-zFz5_byd.js.br +0 -0
- codex/static_root/assets/VSlideGroup-zFz5_byd.js.gz +0 -0
- codex/static_root/assets/VTable-FKMTcdkF.6c1eed84d25e.js.br +0 -0
- codex/static_root/assets/VTable-FKMTcdkF.6c1eed84d25e.js.gz +0 -0
- codex/static_root/assets/VTable-FKMTcdkF.js.br +0 -0
- codex/static_root/assets/VTable-FKMTcdkF.js.gz +0 -0
- codex/static_root/assets/VTextField-Dftcik85.cb2f1dda87e1.js.br +0 -0
- codex/static_root/assets/VTextField-Dftcik85.cb2f1dda87e1.js.gz +0 -0
- codex/static_root/assets/VTextField-Dftcik85.js.br +0 -0
- codex/static_root/assets/VTextField-Dftcik85.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CYCwnt3s.28025dc36c6a.js.br +0 -0
- codex/static_root/assets/VWindowItem-CYCwnt3s.28025dc36c6a.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CYCwnt3s.js.br +0 -0
- codex/static_root/assets/VWindowItem-CYCwnt3s.js.gz +0 -0
- codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js +0 -1
- codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js.br +0 -0
- codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js.gz +0 -0
- codex/static_root/assets/admin-DH_DrB0F.js +0 -1
- codex/static_root/assets/admin-DH_DrB0F.js.br +0 -0
- codex/static_root/assets/admin-DH_DrB0F.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js +0 -30
- codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js +0 -30
- codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-B2rmpAVA.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/browser-CpppBvim.a1b5d4fe97b1.css +0 -1
- codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css.br +0 -0
- codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css.gz +0 -0
- codex/static_root/assets/browser-CpppBvim.css +0 -1
- codex/static_root/assets/browser-CpppBvim.css.br +0 -0
- codex/static_root/assets/browser-CpppBvim.css.gz +0 -0
- codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js +0 -1
- codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js.br +0 -0
- codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js.gz +0 -0
- codex/static_root/assets/browser-lKU7rqLd.js +0 -1
- codex/static_root/assets/browser-lKU7rqLd.js.br +0 -0
- codex/static_root/assets/browser-lKU7rqLd.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BIBG6Bk8.3583021c124a.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BIBG6Bk8.3583021c124a.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BIBG6Bk8.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BIBG6Bk8.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DARLmXRf.53fc6938fb8c.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DARLmXRf.53fc6938fb8c.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DARLmXRf.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DARLmXRf.js.gz +0 -0
- codex/static_root/assets/datetime-column-LpLgWpkf.16ce3324970f.js.br +0 -0
- codex/static_root/assets/datetime-column-LpLgWpkf.16ce3324970f.js.gz +0 -0
- codex/static_root/assets/datetime-column-LpLgWpkf.js.br +0 -0
- codex/static_root/assets/datetime-column-LpLgWpkf.js.gz +0 -0
- codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js +0 -1
- codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js.br +0 -0
- codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js.gz +0 -0
- codex/static_root/assets/filter-jRB1Ob6r.js +0 -1
- codex/static_root/assets/filter-jRB1Ob6r.js.br +0 -0
- codex/static_root/assets/filter-jRB1Ob6r.js.gz +0 -0
- codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js +0 -1
- codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js.br +0 -0
- codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js.gz +0 -0
- codex/static_root/assets/flag-tab-mYS2wg5e.js +0 -1
- codex/static_root/assets/flag-tab-mYS2wg5e.js.br +0 -0
- codex/static_root/assets/flag-tab-mYS2wg5e.js.gz +0 -0
- codex/static_root/assets/group-tab-MK1lcjUU.98406d5babdc.js.br +0 -0
- codex/static_root/assets/group-tab-MK1lcjUU.98406d5babdc.js.gz +0 -0
- codex/static_root/assets/group-tab-MK1lcjUU.js.br +0 -0
- codex/static_root/assets/group-tab-MK1lcjUU.js.gz +0 -0
- codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js +0 -1
- codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js.br +0 -0
- codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js.gz +0 -0
- codex/static_root/assets/http-error-HFeYnF0n.js +0 -1
- codex/static_root/assets/http-error-HFeYnF0n.js.br +0 -0
- codex/static_root/assets/http-error-HFeYnF0n.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-DYeEBYlT.a2a2ef088d1c.js.br +0 -0
- codex/static_root/assets/library-tab-DYeEBYlT.a2a2ef088d1c.js.gz +0 -0
- codex/static_root/assets/library-tab-DYeEBYlT.js.br +0 -0
- codex/static_root/assets/library-tab-DYeEBYlT.js.gz +0 -0
- codex/static_root/assets/main-gSW7TFDY.329223b148d4.js +0 -32
- codex/static_root/assets/main-gSW7TFDY.329223b148d4.js.br +0 -0
- codex/static_root/assets/main-gSW7TFDY.329223b148d4.js.gz +0 -0
- codex/static_root/assets/main-gSW7TFDY.js +0 -32
- codex/static_root/assets/main-gSW7TFDY.js.br +0 -0
- codex/static_root/assets/main-gSW7TFDY.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.br +0 -0
- codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.gz +0 -0
- 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-Dlm70Bhs.8f23f8bd9a05.js +0 -1
- codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js +0 -1
- codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js +0 -35
- codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js.br +0 -0
- codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BTdS9hUz.js +0 -35
- codex/static_root/assets/pdf-doc-BTdS9hUz.js.br +0 -0
- codex/static_root/assets/pdf-doc-BTdS9hUz.js.gz +0 -0
- codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js +0 -2
- codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js.br +0 -0
- codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js.gz +0 -0
- codex/static_root/assets/reader-BtSTIETq.js +0 -2
- codex/static_root/assets/reader-BtSTIETq.js.br +0 -0
- codex/static_root/assets/reader-BtSTIETq.js.gz +0 -0
- codex/static_root/assets/reader-CF3xt6JG.19fad7f62450.css.br +0 -0
- codex/static_root/assets/reader-CF3xt6JG.19fad7f62450.css.gz +0 -0
- codex/static_root/assets/reader-CF3xt6JG.css.br +0 -0
- codex/static_root/assets/reader-CF3xt6JG.css.gz +0 -0
- codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js +0 -1
- codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js.br +0 -0
- codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js.gz +0 -0
- codex/static_root/assets/relation-chips-C4Ob7ySi.js +0 -1
- codex/static_root/assets/relation-chips-C4Ob7ySi.js.br +0 -0
- codex/static_root/assets/relation-chips-C4Ob7ySi.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BJOfMtqA.6471a50fc05b.css.br +0 -0
- codex/static_root/assets/settings-drawer-BJOfMtqA.6471a50fc05b.css.gz +0 -0
- codex/static_root/assets/settings-drawer-BJOfMtqA.css.br +0 -0
- codex/static_root/assets/settings-drawer-BJOfMtqA.css.gz +0 -0
- codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js +0 -2
- codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js.br +0 -0
- codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js.gz +0 -0
- codex/static_root/assets/settings-drawer-xSctoxZn.js +0 -2
- codex/static_root/assets/settings-drawer-xSctoxZn.js.br +0 -0
- codex/static_root/assets/settings-drawer-xSctoxZn.js.gz +0 -0
- codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js +0 -1
- codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js.br +0 -0
- codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js.gz +0 -0
- codex/static_root/assets/stats-tab-C_uC_U7o.js +0 -1
- codex/static_root/assets/stats-tab-C_uC_U7o.js.br +0 -0
- codex/static_root/assets/stats-tab-C_uC_U7o.js.gz +0 -0
- codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js +0 -1
- codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js.br +0 -0
- codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js.gz +0 -0
- codex/static_root/assets/task-tab-VIpy1Ula.js +0 -1
- codex/static_root/assets/task-tab-VIpy1Ula.js.br +0 -0
- codex/static_root/assets/task-tab-VIpy1Ula.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-kEuFL5br.475729603aeb.js +0 -1
- codex/static_root/assets/unauthorized-kEuFL5br.475729603aeb.js.br +0 -0
- codex/static_root/assets/unauthorized-kEuFL5br.475729603aeb.js.gz +0 -0
- codex/static_root/assets/unauthorized-kEuFL5br.js +0 -1
- codex/static_root/assets/unauthorized-kEuFL5br.js.br +0 -0
- codex/static_root/assets/unauthorized-kEuFL5br.js.gz +0 -0
- codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js +0 -1
- codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js.br +0 -0
- codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js.gz +0 -0
- codex/static_root/assets/user-tab-CZNFTTEI.js +0 -1
- codex/static_root/assets/user-tab-CZNFTTEI.js.br +0 -0
- codex/static_root/assets/user-tab-CZNFTTEI.js.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.6ef112683dae.json +0 -642
- codex/static_root/manifest.6ef112683dae.json.br +0 -0
- codex/static_root/manifest.6ef112683dae.json.gz +0 -0
- codex/views/browser/browser_breadcrumbs.py +0 -175
- codex/views/browser/const.py +0 -3
- {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/LICENSE +0 -0
- {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/WHEEL +0 -0
- {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/entry_points.txt +0 -0
codex/views/download.py
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
"""Download a comic archive."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import ClassVar
|
|
5
4
|
|
|
6
5
|
from django.http import FileResponse, Http404
|
|
7
6
|
from drf_spectacular.types import OpenApiTypes
|
|
8
7
|
from drf_spectacular.utils import extend_schema
|
|
9
|
-
from rest_framework.views import APIView
|
|
10
8
|
|
|
11
9
|
from codex.models import Comic
|
|
12
|
-
from codex.views.auth import
|
|
10
|
+
from codex.views.auth import AuthFilterAPIView
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
class DownloadView(
|
|
13
|
+
class DownloadView(AuthFilterAPIView):
|
|
16
14
|
"""Return the comic archive file as an attachment."""
|
|
17
15
|
|
|
18
|
-
permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
|
|
19
16
|
content_type = "application/vnd.comicbook+zip"
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
_AS_ATTACHMENT = True
|
|
22
19
|
|
|
23
20
|
@extend_schema(responses={(200, content_type): OpenApiTypes.BINARY})
|
|
24
21
|
def get(self, *_args, **kwargs):
|
|
@@ -48,7 +45,7 @@ class DownloadView(APIView, GroupACLMixin):
|
|
|
48
45
|
filename = comic.get_filename()
|
|
49
46
|
return FileResponse(
|
|
50
47
|
comic_file,
|
|
51
|
-
as_attachment=self.
|
|
48
|
+
as_attachment=self._AS_ATTACHMENT,
|
|
52
49
|
content_type=content_type,
|
|
53
50
|
filename=filename,
|
|
54
51
|
)
|
|
@@ -57,4 +54,4 @@ class DownloadView(APIView, GroupACLMixin):
|
|
|
57
54
|
class FileView(DownloadView):
|
|
58
55
|
"""View a comic in the browser."""
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
_AS_ATTACHMENT = False
|
codex/views/frontend.py
CHANGED
|
@@ -6,12 +6,14 @@ from rest_framework.renderers import TemplateHTMLRenderer
|
|
|
6
6
|
from rest_framework.response import Response
|
|
7
7
|
|
|
8
8
|
from codex.serializers.route import RouteSerializer
|
|
9
|
-
from codex.views.session import
|
|
9
|
+
from codex.views.session import SessionView
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class IndexView(
|
|
12
|
+
class IndexView(SessionView):
|
|
13
13
|
"""The main app."""
|
|
14
14
|
|
|
15
|
+
SESSION_KEY = SessionView.BROWSER_SESSION_KEY
|
|
16
|
+
|
|
15
17
|
renderer_classes: ClassVar[list] = [TemplateHTMLRenderer] # type: ignore
|
|
16
18
|
template_name = "index.html"
|
|
17
19
|
|
codex/views/mtime.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Get the mtimes for the submitted groups."""
|
|
2
|
+
|
|
3
|
+
from django.db.models import Max
|
|
4
|
+
from rest_framework.response import Response
|
|
5
|
+
|
|
6
|
+
from codex.models.groups import Publisher
|
|
7
|
+
from codex.serializers.mtime import GroupsMtimeSerializer, MtimeSerializer
|
|
8
|
+
from codex.util import max_none
|
|
9
|
+
from codex.views.auth import AuthGenericAPIView
|
|
10
|
+
from codex.views.browser.filters.bookmark import BookmarkFilterMixin
|
|
11
|
+
from codex.views.const import GROUP_MODEL_MAP
|
|
12
|
+
from codex.views.utils import reparse_json_query_params
|
|
13
|
+
|
|
14
|
+
_REPARSE_JSON_FIELDS = frozenset({"groups"})
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MtimeView(AuthGenericAPIView, BookmarkFilterMixin):
|
|
18
|
+
"""Get the mtimes for the submitted groups."""
|
|
19
|
+
|
|
20
|
+
serializer_class = GroupsMtimeSerializer
|
|
21
|
+
response_serializer_class = MtimeSerializer
|
|
22
|
+
|
|
23
|
+
def _get_group_mtime(self, group, pks, use_bookmark_filter):
|
|
24
|
+
"""Get one group's mtimes."""
|
|
25
|
+
model = GROUP_MODEL_MAP[group]
|
|
26
|
+
if not model:
|
|
27
|
+
model = Publisher
|
|
28
|
+
|
|
29
|
+
qs = model.objects
|
|
30
|
+
|
|
31
|
+
if pks and 0 not in pks:
|
|
32
|
+
qs = qs.filter(pk__in=pks)
|
|
33
|
+
updated_at_max = qs.aggregate(max=Max("updated_at"))["max"]
|
|
34
|
+
|
|
35
|
+
if use_bookmark_filter:
|
|
36
|
+
bm_rel = self.get_bm_rel(model)
|
|
37
|
+
bm_filter = self.get_my_bookmark_filter(bm_rel)
|
|
38
|
+
qs = qs.filter(bm_filter)
|
|
39
|
+
# TODO can these be combined into one max?
|
|
40
|
+
bookmark_updated_at_max = qs.aggregate(max=(Max(f"{bm_rel}__updated_at")))[
|
|
41
|
+
"max"
|
|
42
|
+
]
|
|
43
|
+
updated_at_max = max_none(updated_at_max, bookmark_updated_at_max)
|
|
44
|
+
|
|
45
|
+
return updated_at_max
|
|
46
|
+
|
|
47
|
+
def get_max_groups_mtime(self, groups, use_bookmark_filter):
|
|
48
|
+
"""Get max mtime for all groups."""
|
|
49
|
+
max_mtime = None
|
|
50
|
+
|
|
51
|
+
for item in groups:
|
|
52
|
+
group = item["group"]
|
|
53
|
+
pks = tuple(int(pk) for pk in item["pks"].split(","))
|
|
54
|
+
mtime = self._get_group_mtime(group, pks, use_bookmark_filter)
|
|
55
|
+
max_mtime = max_none(max_mtime, mtime)
|
|
56
|
+
return max_mtime
|
|
57
|
+
|
|
58
|
+
def get(self, *args, **kwargs):
|
|
59
|
+
"""Get the mtimes for the submitted groups."""
|
|
60
|
+
# Parse Request
|
|
61
|
+
params = reparse_json_query_params(self.request.GET, _REPARSE_JSON_FIELDS)
|
|
62
|
+
groups = params.get("groups", "")
|
|
63
|
+
use_bookmark_filter = self.request.GET.get("use_bookmark_filter", False)
|
|
64
|
+
|
|
65
|
+
max_mtime = self.get_max_groups_mtime(groups, use_bookmark_filter)
|
|
66
|
+
|
|
67
|
+
# Serialize Response
|
|
68
|
+
result = {"max_mtime": max_mtime}
|
|
69
|
+
serializer = self.response_serializer_class(result)
|
|
70
|
+
return Response(serializer.data)
|
codex/views/opds/auth.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""OPDS Authentican mixin."""
|
|
2
|
+
|
|
3
|
+
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
|
4
|
+
|
|
5
|
+
from codex.views.auth import IsAuthenticatedOrEnabledNonUsers
|
|
6
|
+
from codex.views.template import CodexXMLTemplateView
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OPDSAuthMixin:
|
|
10
|
+
"""Add Basic Auth."""
|
|
11
|
+
|
|
12
|
+
authentication_classes = (SessionAuthentication, BasicAuthentication)
|
|
13
|
+
permission_classes = (IsAuthenticatedOrEnabledNonUsers,)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OPDSTemplateView(OPDSAuthMixin, CodexXMLTemplateView):
|
|
17
|
+
"""XML Template view with OPDSAuth."""
|
|
@@ -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 rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
|
4
|
-
|
|
5
3
|
from codex.views.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,16 @@
|
|
|
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
9
|
|
|
10
|
-
|
|
10
|
+
_OPDS_VERSIONS = (1, 2)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class OPDSURLsView(
|
|
13
|
+
class OPDSURLsView(AuthGenericAPIView):
|
|
14
14
|
"""OPDS URLs."""
|
|
15
15
|
|
|
16
16
|
serializer_class = OPDSURLsSerializer
|
|
@@ -19,7 +19,8 @@ class OPDSURLsView(GenericAPIView):
|
|
|
19
19
|
"""Resolve the urls."""
|
|
20
20
|
obj = {}
|
|
21
21
|
route = DEFAULTS["breadcrumbs"][0]
|
|
22
|
-
|
|
22
|
+
route.pop("name", None)
|
|
23
|
+
for version in _OPDS_VERSIONS:
|
|
23
24
|
key = f"v{version}"
|
|
24
25
|
name = f"opds:v{version}:feed"
|
|
25
26
|
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
11
|
StoryArc,
|
|
16
|
-
Tag,
|
|
17
|
-
Team,
|
|
18
12
|
)
|
|
19
13
|
from codex.serializers.choices import DEFAULTS
|
|
20
|
-
|
|
21
|
-
OPDS_M2M_MODELS = (Character, Genre, Location, SeriesGroup, StoryArc, Tag, Team)
|
|
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):
|
|
@@ -81,6 +74,7 @@ def full_redirect_view(url_name):
|
|
|
81
74
|
def func(request):
|
|
82
75
|
"""Redirect to view, forwarding query strings and auth."""
|
|
83
76
|
kwargs = DEFAULTS["breadcrumbs"][0]
|
|
77
|
+
kwargs.pop("name", None)
|
|
84
78
|
url = reverse(url_name, kwargs=kwargs)
|
|
85
79
|
|
|
86
80
|
# Forward the query string.
|
codex/views/opds/v1/facets.py
CHANGED
|
@@ -77,7 +77,7 @@ DEFAULT_FACETS = {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
class
|
|
80
|
+
class OPDS1FacetsView(BrowserView):
|
|
81
81
|
"""OPDS 1 Facets methods."""
|
|
82
82
|
|
|
83
83
|
OPDS = 1
|
|
@@ -89,15 +89,14 @@ class FacetsMixin(BrowserView):
|
|
|
89
89
|
obj = MappingProxyType({})
|
|
90
90
|
|
|
91
91
|
def _facet(self, kwargs, facet_group, facet_title, new_query_params):
|
|
92
|
+
kwargs.pop("name", None)
|
|
92
93
|
href = reverse("opds:v1:feed", kwargs=kwargs)
|
|
93
94
|
facet_active = False
|
|
94
95
|
for key, val in new_query_params.items():
|
|
95
|
-
if self.request.
|
|
96
|
+
if self.request.GET.get(key) == val:
|
|
96
97
|
facet_active = True
|
|
97
98
|
break
|
|
98
|
-
href = update_href_query_params(
|
|
99
|
-
href, self.request.query_params, new_query_params
|
|
100
|
-
)
|
|
99
|
+
href = update_href_query_params(href, self.request.GET, new_query_params)
|
|
101
100
|
|
|
102
101
|
title = " ".join(filter(None, (facet_group.title_prefix, facet_title))).strip()
|
|
103
102
|
return OPDS1Link(
|
|
@@ -118,7 +117,7 @@ class FacetsMixin(BrowserView):
|
|
|
118
117
|
ids=item.get("pks"),
|
|
119
118
|
name=name,
|
|
120
119
|
)
|
|
121
|
-
qps = {**self.request.
|
|
120
|
+
qps = {**self.request.GET}
|
|
122
121
|
qps.update(query_params)
|
|
123
122
|
zero_pad = self.obj["zero_pad"]
|
|
124
123
|
data = OPDS1EntryData(
|
|
@@ -131,7 +130,7 @@ class FacetsMixin(BrowserView):
|
|
|
131
130
|
default_val = DEFAULT_FACETS.get(facet_group.query_param)
|
|
132
131
|
if facet.value == default_val:
|
|
133
132
|
compare += [None]
|
|
134
|
-
return self.request.
|
|
133
|
+
return self.request.GET.get(facet_group.query_param) in compare
|
|
135
134
|
|
|
136
135
|
@staticmethod
|
|
137
136
|
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
|
|
@@ -54,10 +51,9 @@ class UserAgentPrefixes:
|
|
|
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")
|
|
@@ -232,7 +225,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
|
|
|
232
225
|
self.acquisition_groups = frozenset({*self.valid_nav_groups[-2:]} | {"c"})
|
|
233
226
|
self.is_opds_1_acquisition = group in self.acquisition_groups
|
|
234
227
|
self.is_opds_metadata = (
|
|
235
|
-
self.request.
|
|
228
|
+
self.request.GET.get("opdsMetadata", "").lower() not in FALSY
|
|
236
229
|
)
|
|
237
230
|
|
|
238
231
|
def init_request(self):
|
codex/views/opds/v1/links.py
CHANGED
|
@@ -14,7 +14,7 @@ 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
18
|
|
|
19
19
|
LOG = get_logger(__name__)
|
|
20
20
|
|
|
@@ -101,13 +101,13 @@ class RootTopLinks:
|
|
|
101
101
|
ALL = (NEW, FEATURED, LAST_READ)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
class
|
|
104
|
+
class OPDS1LinksView(OPDS1FacetsView):
|
|
105
105
|
"""OPDS 1 Links methods."""
|
|
106
106
|
|
|
107
107
|
# overwritten in get_object()
|
|
108
108
|
DEFAULT_ROUTE = MappingProxyType(
|
|
109
109
|
{
|
|
110
|
-
**
|
|
110
|
+
**OPDS1FacetsView.DEFAULT_ROUTE,
|
|
111
111
|
"name": "opds:v1:feed",
|
|
112
112
|
}
|
|
113
113
|
)
|
|
@@ -120,7 +120,7 @@ class LinksMixin(FacetsMixin):
|
|
|
120
120
|
return False
|
|
121
121
|
|
|
122
122
|
for key, value in top_link.query_params.items():
|
|
123
|
-
if str(self.request.
|
|
123
|
+
if str(self.request.GET.get(key)) != str(value):
|
|
124
124
|
return False
|
|
125
125
|
|
|
126
126
|
return True
|
|
@@ -128,7 +128,8 @@ class LinksMixin(FacetsMixin):
|
|
|
128
128
|
def _link(self, kwargs, rel, query_params=None, mime_type=MimeType.NAV):
|
|
129
129
|
"""Create a link."""
|
|
130
130
|
if query_params is None:
|
|
131
|
-
query_params = self.request.
|
|
131
|
+
query_params = self.request.GET
|
|
132
|
+
kwargs.pop("name", None)
|
|
132
133
|
href = reverse("opds:v1:feed", kwargs=kwargs)
|
|
133
134
|
href = update_href_query_params(href, query_params)
|
|
134
135
|
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,)
|
codex/views/opds/v2/feed.py
CHANGED
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
from types import MappingProxyType
|
|
4
4
|
|
|
5
5
|
from drf_spectacular.utils import extend_schema
|
|
6
|
-
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
|
7
6
|
from rest_framework.response import Response
|
|
8
7
|
|
|
9
8
|
from codex.logger.logging import get_logger
|
|
10
9
|
from codex.models import AdminFlag
|
|
11
10
|
from codex.serializers.opds.v2 import OPDS2FeedSerializer
|
|
12
11
|
from codex.views.browser.browser import BrowserView
|
|
13
|
-
from codex.views.
|
|
14
|
-
from codex.views.
|
|
12
|
+
from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
|
|
13
|
+
from codex.views.opds.auth import OPDSAuthMixin
|
|
15
14
|
from codex.views.opds.const import BLANK_TITLE
|
|
16
15
|
from codex.views.opds.v2.const import (
|
|
17
16
|
FACETS,
|
|
@@ -22,22 +21,20 @@ from codex.views.opds.v2.const import (
|
|
|
22
21
|
NavigationGroup,
|
|
23
22
|
)
|
|
24
23
|
from codex.views.opds.v2.links import HrefData, LinkData
|
|
25
|
-
from codex.views.opds.v2.publications import
|
|
26
|
-
from codex.views.opds.v2.top_links import TopLinksMixin
|
|
24
|
+
from codex.views.opds.v2.publications import OPDS2PublicationView
|
|
27
25
|
|
|
28
26
|
LOG = get_logger(__name__)
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
class OPDS2FeedView(
|
|
29
|
+
class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
|
|
32
30
|
"""OPDS 2.0 Feed."""
|
|
33
31
|
|
|
34
32
|
DEFAULT_ROUTE = MappingProxyType(
|
|
35
|
-
{**
|
|
33
|
+
{**OPDS2PublicationView.DEFAULT_ROUTE, "name": "opds:v2:feed"}
|
|
36
34
|
)
|
|
37
35
|
TARGET = "opds2"
|
|
38
36
|
throttle_scope = "opds"
|
|
39
37
|
|
|
40
|
-
authentication_classes = (SessionAuthentication, BasicAuthentication)
|
|
41
38
|
serializer_class = OPDS2FeedSerializer
|
|
42
39
|
|
|
43
40
|
def _title(self, browser_title):
|
|
@@ -55,11 +52,11 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
|
|
|
55
52
|
result = BLANK_TITLE
|
|
56
53
|
return result
|
|
57
54
|
|
|
58
|
-
def _detect_user_agent(self):
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
# def _detect_user_agent(self):
|
|
56
|
+
# """Hacks for individual clients."""
|
|
57
|
+
# user_agent = self.request.headers.get("User-Agent")
|
|
58
|
+
# if not user_agent:
|
|
59
|
+
# return
|
|
63
60
|
|
|
64
61
|
@staticmethod
|
|
65
62
|
def _is_allowed(link_spec):
|
|
@@ -187,7 +184,7 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
|
|
|
187
184
|
self.num_pages = browser_page["num_pages"]
|
|
188
185
|
|
|
189
186
|
# opds page
|
|
190
|
-
title = self._title(browser_page.get("
|
|
187
|
+
title = self._title(browser_page.get("title"))
|
|
191
188
|
number_of_items = browser_page["total_count"]
|
|
192
189
|
current_page = self.kwargs.get("page")
|
|
193
190
|
up_route = self.get_last_route()
|
|
@@ -224,7 +221,7 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
|
|
|
224
221
|
acquisition_groups = frozenset(self.valid_nav_groups[-2:])
|
|
225
222
|
self.is_opds_2_acquisition = group in acquisition_groups
|
|
226
223
|
self.is_opds_metadata = (
|
|
227
|
-
self.request.
|
|
224
|
+
self.request.GET.get("opdsMetadata", "").lower() not in FALSY
|
|
228
225
|
)
|
|
229
226
|
|
|
230
227
|
def init_request(self):
|
codex/views/opds/v2/links.py
CHANGED
|
@@ -39,7 +39,7 @@ class LinkData:
|
|
|
39
39
|
num_items: int | None = None
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
class
|
|
42
|
+
class OPDS2LinksView(BrowserView):
|
|
43
43
|
"""Links methods for OPDS 2.0 Feed."""
|
|
44
44
|
|
|
45
45
|
num_pages = 0 # For pyright. Overwritten every run.
|
|
@@ -58,7 +58,7 @@ class LinksMixin(BrowserView):
|
|
|
58
58
|
elif hasattr(self, "request"):
|
|
59
59
|
# if request link and not init static links
|
|
60
60
|
href = update_href_query_params(
|
|
61
|
-
href, self.request.
|
|
61
|
+
href, self.request.GET, new_query_params=data.query_params
|
|
62
62
|
)
|
|
63
63
|
return href
|
|
64
64
|
|
|
@@ -69,6 +69,7 @@ class LinksMixin(BrowserView):
|
|
|
69
69
|
if "page" in kwargs and not self._href_page_validate(kwargs, data):
|
|
70
70
|
return None
|
|
71
71
|
|
|
72
|
+
kwargs.pop("name", None)
|
|
72
73
|
href = reverse(url_name, kwargs=kwargs)
|
|
73
74
|
return self._href_update_query_params(href, data)
|
|
74
75
|
|