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/exceptions.py
CHANGED
|
@@ -30,6 +30,7 @@ class SeeOtherRedirectError(APIException):
|
|
|
30
30
|
route: dict = detail.get("route", {}) # type: ignore
|
|
31
31
|
params: dict = route.get("params", DEFAULTS["breadcrumbs"][0])
|
|
32
32
|
# Save route in server format for redirect reverse
|
|
33
|
+
params.pop("name", None)
|
|
33
34
|
self.route_kwargs = params
|
|
34
35
|
# Serialize route for detail
|
|
35
36
|
serializer = RouteSerializer(params)
|
codex/librarian/covers/purge.py
CHANGED
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
from codex.librarian.covers.path import CoverPathMixin
|
|
8
8
|
from codex.librarian.covers.status import CoverStatusTypes
|
|
9
9
|
from codex.librarian.notifier.tasks import LIBRARY_CHANGED_TASK
|
|
10
|
-
from codex.models import Comic
|
|
10
|
+
from codex.models import Comic
|
|
11
11
|
from codex.models.paths import CustomCover
|
|
12
12
|
from codex.status import Status
|
|
13
13
|
|
|
@@ -69,7 +69,6 @@ class CoverPurgeMixin(CoverPathMixin):
|
|
|
69
69
|
self.log.info("Removed entire custom cover cache.")
|
|
70
70
|
except Exception as exc:
|
|
71
71
|
self.log.warning(exc)
|
|
72
|
-
Timestamp.touch(Timestamp.TimestampChoices.COVERS)
|
|
73
72
|
librarian_queue.put(LIBRARY_CHANGED_TASK)
|
|
74
73
|
|
|
75
74
|
def _cleanup_orphan_covers(self, cover_class, cover_root, name):
|
|
@@ -28,6 +28,7 @@ from codex.librarian.importer.status import ImportStatusTypes, status_notify
|
|
|
28
28
|
from codex.models import Imprint, Publisher, Series, Volume
|
|
29
29
|
from codex.models.admin import AdminFlag
|
|
30
30
|
from codex.status import Status
|
|
31
|
+
from codex.util import max_none
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class AggregateMetadataMixin(CleanMetadataMixin):
|
|
@@ -182,22 +183,13 @@ class AggregateMetadataMixin(CleanMetadataMixin):
|
|
|
182
183
|
all_fks[field] = set()
|
|
183
184
|
all_fks[field].add(name)
|
|
184
185
|
|
|
185
|
-
@staticmethod
|
|
186
|
-
def _none_max(a, b):
|
|
187
|
-
"""None aware math.max."""
|
|
188
|
-
if a is not None and b is not None:
|
|
189
|
-
return max(a, b)
|
|
190
|
-
if a is None:
|
|
191
|
-
return b
|
|
192
|
-
return a
|
|
193
|
-
|
|
194
186
|
@classmethod
|
|
195
187
|
def _set_max_group_count(cls, common_args, group_class, index, count_key):
|
|
196
188
|
"""Assign the maximum group count number."""
|
|
197
189
|
all_fks, group_tree, group_md = common_args
|
|
198
190
|
group_name = group_tree[0:index]
|
|
199
191
|
try:
|
|
200
|
-
count =
|
|
192
|
+
count = max_none(
|
|
201
193
|
all_fks[GROUP_TREES][Series].get(group_name),
|
|
202
194
|
group_md.get(count_key),
|
|
203
195
|
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Update Groups timestamp for cover cache busting."""
|
|
2
|
+
|
|
3
|
+
from django.db.models.functions import Now
|
|
4
|
+
from django.db.models.query import Q
|
|
5
|
+
|
|
6
|
+
from codex.models import StoryArc
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CacheMixin:
|
|
10
|
+
"""Update Groups timestamp for cover cache busting."""
|
|
11
|
+
|
|
12
|
+
def update_groups_with_changed_comics(self, start_time, deleted_comic_groups):
|
|
13
|
+
"""Update timestamps for each group for cover cache busting."""
|
|
14
|
+
total_count = 0
|
|
15
|
+
now = Now()
|
|
16
|
+
|
|
17
|
+
log_list = []
|
|
18
|
+
for model, pks in deleted_comic_groups.items():
|
|
19
|
+
rel = "story_arc_number__" if model == StoryArc else ""
|
|
20
|
+
updated_at_rel = rel + "comic__updated_at__gt"
|
|
21
|
+
updated_filter = {updated_at_rel: start_time}
|
|
22
|
+
model_qs = (
|
|
23
|
+
model.objects.filter(Q(**updated_filter) | Q(pk__in=pks))
|
|
24
|
+
.distinct()
|
|
25
|
+
.only("updated_at")
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
updated = []
|
|
29
|
+
for obj in model_qs:
|
|
30
|
+
obj.updated_at = now
|
|
31
|
+
updated.append(obj)
|
|
32
|
+
|
|
33
|
+
count = len(updated)
|
|
34
|
+
if count:
|
|
35
|
+
model.objects.bulk_update(updated, ["updated_at"])
|
|
36
|
+
log_list.append(f"{count} {model.__name__}s.")
|
|
37
|
+
total_count += count
|
|
38
|
+
if total_count:
|
|
39
|
+
groups_log = ", ".join(log_list)
|
|
40
|
+
self.log.debug( # type: ignore
|
|
41
|
+
f"Updated {groups_log} timestamps for browser cache busting."
|
|
42
|
+
)
|
|
43
|
+
self.log.info( # type: ignore
|
|
44
|
+
f"Updated {total_count} group timestamps for browser cache busting."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return total_count
|
|
@@ -219,3 +219,11 @@ COMIC_M2M_FIELD_NAMES = frozenset(
|
|
|
219
219
|
for field in Comic._meta.get_fields()
|
|
220
220
|
if field.many_to_many and field.name != "folders"
|
|
221
221
|
)
|
|
222
|
+
COMIC_GROUP_FIELD_NAMES = (
|
|
223
|
+
"publisher",
|
|
224
|
+
"imprint",
|
|
225
|
+
"series",
|
|
226
|
+
"volume",
|
|
227
|
+
"story_arc_numbers",
|
|
228
|
+
"folders",
|
|
229
|
+
)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Clean up the database after moves or imports."""
|
|
2
2
|
|
|
3
3
|
from codex.librarian.covers.tasks import CoverRemoveTask
|
|
4
|
+
from codex.librarian.importer.const import COMIC_GROUP_FIELD_NAMES
|
|
4
5
|
from codex.librarian.importer.status import ImportStatusTypes, status_notify
|
|
5
|
-
from codex.models import Comic, Folder
|
|
6
|
+
from codex.models import Comic, Folder, StoryArc
|
|
6
7
|
from codex.models.paths import CustomCover
|
|
8
|
+
from codex.settings.settings import MAX_CHUNK_SIZE
|
|
7
9
|
from codex.threads import QueuedThread
|
|
8
10
|
|
|
9
11
|
|
|
@@ -37,14 +39,47 @@ class DeletedMixin(QueuedThread):
|
|
|
37
39
|
)
|
|
38
40
|
return count
|
|
39
41
|
|
|
42
|
+
@staticmethod
|
|
43
|
+
def _populate_deleted_comic_groups(delete_qs, deleted_comic_groups):
|
|
44
|
+
"""Populate changed groups for cover timestamp updater."""
|
|
45
|
+
comics_deleted_qs = delete_qs.only(*COMIC_GROUP_FIELD_NAMES).prefetch_related(
|
|
46
|
+
"story_arc_numbers__story_arc"
|
|
47
|
+
)
|
|
48
|
+
for comic in comics_deleted_qs.iterator(chunk_size=MAX_CHUNK_SIZE):
|
|
49
|
+
for field_name in COMIC_GROUP_FIELD_NAMES:
|
|
50
|
+
if field_name == "story_arc_numbers":
|
|
51
|
+
for san in comic.story_arc_numbers.select_related("story_arc").only(
|
|
52
|
+
"story_arc"
|
|
53
|
+
):
|
|
54
|
+
deleted_comic_groups[StoryArc].add(san.story_arc.pk)
|
|
55
|
+
elif field_name == "folders":
|
|
56
|
+
for folder in comic.folders.only("pk"):
|
|
57
|
+
deleted_comic_groups[Folder].add(folder.pk)
|
|
58
|
+
else:
|
|
59
|
+
related_model = comic._meta.get_field(field_name).related_model
|
|
60
|
+
related_id = getattr(comic, field_name).pk
|
|
61
|
+
deleted_comic_groups[related_model].add(related_id)
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _init_deleted_comic_groups(deleted_comic_groups):
|
|
65
|
+
"""Init deleted_comic_groups, used later even if no deletes."""
|
|
66
|
+
for field_name in COMIC_GROUP_FIELD_NAMES:
|
|
67
|
+
related_model = Comic._meta.get_field(field_name).related_model
|
|
68
|
+
deleted_comic_groups[related_model] = set()
|
|
69
|
+
|
|
40
70
|
@status_notify(status_type=ImportStatusTypes.FILES_DELETED, updates=False)
|
|
41
|
-
def _bulk_comics_deleted(
|
|
71
|
+
def _bulk_comics_deleted(
|
|
72
|
+
self, delete_comic_paths, library, deleted_comic_groups, **kwargs
|
|
73
|
+
):
|
|
42
74
|
"""Bulk delete comics found missing from the filesystem."""
|
|
43
75
|
if not delete_comic_paths:
|
|
44
76
|
return 0
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
77
|
+
delete_qs = Comic.objects.filter(library=library, path__in=delete_comic_paths)
|
|
78
|
+
|
|
79
|
+
self._populate_deleted_comic_groups(delete_qs, deleted_comic_groups)
|
|
80
|
+
|
|
81
|
+
delete_comic_pks = frozenset(delete_qs.values_list("pk", flat=True))
|
|
82
|
+
delete_qs.delete()
|
|
48
83
|
|
|
49
84
|
self._remove_covers(delete_comic_pks)
|
|
50
85
|
|
|
@@ -73,12 +108,15 @@ class DeletedMixin(QueuedThread):
|
|
|
73
108
|
|
|
74
109
|
return count
|
|
75
110
|
|
|
76
|
-
def delete(self, library, task):
|
|
111
|
+
def delete(self, library, task, deleted_comic_groups):
|
|
77
112
|
"""Delete files and folders."""
|
|
78
113
|
count = self._bulk_folders_deleted(task.dirs_deleted, library)
|
|
79
114
|
task.dirs_deleted = None
|
|
80
115
|
|
|
81
|
-
|
|
116
|
+
self._init_deleted_comic_groups(deleted_comic_groups)
|
|
117
|
+
count += self._bulk_comics_deleted(
|
|
118
|
+
task.files_deleted, library, deleted_comic_groups
|
|
119
|
+
)
|
|
82
120
|
task.files_deleted = None
|
|
83
121
|
|
|
84
122
|
count += self._bulk_covers_deleted(task.covers_deleted, library)
|
|
@@ -5,9 +5,11 @@ from pathlib import Path
|
|
|
5
5
|
from time import sleep, time
|
|
6
6
|
|
|
7
7
|
from django.core.cache import cache
|
|
8
|
+
from django.utils.timezone import now
|
|
8
9
|
from humanize import naturaldelta
|
|
9
10
|
|
|
10
11
|
from codex.librarian.importer.aggregate_metadata import AggregateMetadataMixin
|
|
12
|
+
from codex.librarian.importer.cache import CacheMixin
|
|
11
13
|
from codex.librarian.importer.const import FIS, FKS, M2M_MDS, MDS
|
|
12
14
|
from codex.librarian.importer.deleted import DeletedMixin
|
|
13
15
|
from codex.librarian.importer.failed_imports import FailedImportsMixin
|
|
@@ -35,6 +37,7 @@ class ComicImporterThread(
|
|
|
35
37
|
UpdateComicsMixin,
|
|
36
38
|
FailedImportsMixin,
|
|
37
39
|
MovedMixin,
|
|
40
|
+
CacheMixin,
|
|
38
41
|
):
|
|
39
42
|
"""A worker to handle all bulk database updates."""
|
|
40
43
|
|
|
@@ -322,6 +325,7 @@ class ComicImporterThread(
|
|
|
322
325
|
library = Library.objects.get(pk=task.library_id)
|
|
323
326
|
try:
|
|
324
327
|
self._init_apply(library, task)
|
|
328
|
+
import_start_time = now()
|
|
325
329
|
|
|
326
330
|
changed: int = 0
|
|
327
331
|
link_cover_pks: set[int] = set()
|
|
@@ -379,7 +383,13 @@ class ComicImporterThread(
|
|
|
379
383
|
library, fis, bool(task.files_deleted)
|
|
380
384
|
)
|
|
381
385
|
|
|
382
|
-
|
|
386
|
+
deleted_comic_groups = {}
|
|
387
|
+
changed += self.delete(library, task, deleted_comic_groups)
|
|
388
|
+
|
|
389
|
+
self.update_groups_with_changed_comics(
|
|
390
|
+
import_start_time, deleted_comic_groups
|
|
391
|
+
)
|
|
392
|
+
|
|
383
393
|
if changed:
|
|
384
394
|
cache.clear()
|
|
385
395
|
finally:
|
|
@@ -11,7 +11,6 @@ from codex.librarian.importer.link_comics import LinkComicsMixin
|
|
|
11
11
|
from codex.librarian.importer.status import ImportStatusTypes, status_notify
|
|
12
12
|
from codex.models import (
|
|
13
13
|
Comic,
|
|
14
|
-
Timestamp,
|
|
15
14
|
)
|
|
16
15
|
|
|
17
16
|
|
|
@@ -74,7 +73,6 @@ class UpdateComicsMixin(LinkComicsMixin):
|
|
|
74
73
|
self.log.debug(f"Bulk updating {len(update_comics)} comics.")
|
|
75
74
|
try:
|
|
76
75
|
Comic.objects.bulk_update(update_comics, BULK_UPDATE_COMIC_FIELDS)
|
|
77
|
-
Timestamp.touch(Timestamp.TimestampChoices.COVERS)
|
|
78
76
|
count = len(update_comics)
|
|
79
77
|
|
|
80
78
|
self._remove_covers(comic_pks, False) # type: ignore
|
|
@@ -76,7 +76,7 @@ class Janitor(CleanupMixin, UpdateMixin, VacuumMixin, UpdateFailedImportsMixin):
|
|
|
76
76
|
except Exception:
|
|
77
77
|
self.log.exception(f"In {self.__class__.__name__}")
|
|
78
78
|
|
|
79
|
-
def run(self, task):
|
|
79
|
+
def run(self, task): # noqa: PLR0912,C901
|
|
80
80
|
"""Run Janitor tasks as the librarian process directly."""
|
|
81
81
|
try:
|
|
82
82
|
match task:
|
codex/librarian/librariand.py
CHANGED
|
@@ -77,7 +77,7 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
|
|
|
77
77
|
self.queue.put(task)
|
|
78
78
|
self.search_indexer_abort_event = Manager().Event()
|
|
79
79
|
|
|
80
|
-
def _process_task(self, task):
|
|
80
|
+
def _process_task(self, task): # noqa: PLR0912,C901
|
|
81
81
|
"""Process an individual task popped off the queue."""
|
|
82
82
|
match task:
|
|
83
83
|
case CoverTask():
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Generated by Django 5.0.6 on 2024-05-25 21:19."""
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
"""Migrate db."""
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("codex", "0028_custom_covers"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AlterField(
|
|
15
|
+
model_name="adminflag",
|
|
16
|
+
name="key",
|
|
17
|
+
field=models.CharField(
|
|
18
|
+
choices=[
|
|
19
|
+
("FV", "Folder View"),
|
|
20
|
+
("RG", "Registration"),
|
|
21
|
+
("NU", "Non Users"),
|
|
22
|
+
("AU", "Auto Update"),
|
|
23
|
+
("SO", "Search Index Optimize"),
|
|
24
|
+
("IM", "Import Metadata"),
|
|
25
|
+
],
|
|
26
|
+
db_index=True,
|
|
27
|
+
max_length=2,
|
|
28
|
+
),
|
|
29
|
+
),
|
|
30
|
+
migrations.AlterField(
|
|
31
|
+
model_name="timestamp",
|
|
32
|
+
name="key",
|
|
33
|
+
field=models.CharField(
|
|
34
|
+
choices=[
|
|
35
|
+
("JA", "Janitor"),
|
|
36
|
+
("VR", "Codex Version"),
|
|
37
|
+
("SI", "Search Index UUID"),
|
|
38
|
+
("AP", "API Key"),
|
|
39
|
+
],
|
|
40
|
+
db_index=True,
|
|
41
|
+
max_length=2,
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
]
|
codex/models/admin.py
CHANGED
codex/permissions.py
CHANGED
|
@@ -8,7 +8,7 @@ from codex.models import Timestamp
|
|
|
8
8
|
class HasAPIKeyOrIsAdminUser(BasePermission):
|
|
9
9
|
"""Does the request have the current api key."""
|
|
10
10
|
|
|
11
|
-
def has_permission(self, request, view):
|
|
11
|
+
def has_permission(self, request, view): # type: ignore
|
|
12
12
|
"""Test the request api key against the database."""
|
|
13
13
|
data = request.GET if request.method == "GET" else request.POST
|
|
14
14
|
api_key = data.get("apiKey")
|
codex/serializers/admin.py
CHANGED
|
@@ -173,8 +173,7 @@ class AdminFolderSerializer(Serializer):
|
|
|
173
173
|
def validate_show_hidden(self, show_hidden):
|
|
174
174
|
"""Snakecase the showHidden field."""
|
|
175
175
|
return (
|
|
176
|
-
show_hidden == "true"
|
|
177
|
-
or self.initial_data.get("showHidden", False) == "true"
|
|
176
|
+
show_hidden == "true" or self.initial_data.get("showHidden") == "true" # type: ignore
|
|
178
177
|
)
|
|
179
178
|
|
|
180
179
|
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
"""Browser Settings Filter Serializers."""
|
|
2
2
|
|
|
3
|
-
from abc import ABC
|
|
4
|
-
|
|
5
|
-
from django.core.exceptions import ValidationError
|
|
6
|
-
from django.utils.translation import gettext_lazy as _
|
|
7
|
-
from rest_framework.fields import Field
|
|
8
3
|
from rest_framework.serializers import (
|
|
9
4
|
BooleanField,
|
|
10
|
-
CharField,
|
|
11
5
|
ChoiceField,
|
|
12
|
-
FloatField,
|
|
13
|
-
IntegerField,
|
|
14
|
-
ListField,
|
|
15
6
|
Serializer,
|
|
16
7
|
)
|
|
17
8
|
|
|
18
|
-
from codex.serializers.choices import CHOICES
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
from codex.serializers.choices import CHOICES
|
|
10
|
+
from codex.serializers.fields import (
|
|
11
|
+
BooleanListField,
|
|
12
|
+
CharListField,
|
|
13
|
+
DecadeListField,
|
|
14
|
+
FloatListField,
|
|
15
|
+
IntListField,
|
|
16
|
+
)
|
|
21
17
|
|
|
22
18
|
|
|
23
19
|
class BrowserFilterChoicesSerializer(Serializer):
|
|
@@ -47,95 +43,6 @@ class BrowserFilterChoicesSerializer(Serializer):
|
|
|
47
43
|
year = BooleanField(read_only=True)
|
|
48
44
|
|
|
49
45
|
|
|
50
|
-
def validate_decades(decades):
|
|
51
|
-
"""Validate decades."""
|
|
52
|
-
if not decades:
|
|
53
|
-
return
|
|
54
|
-
# * We don't need a whole db call just to be perfectly accurate
|
|
55
|
-
# * -1s are decoded back into None before validation
|
|
56
|
-
exists = False
|
|
57
|
-
for decade in decades:
|
|
58
|
-
if decade is None or decade % 10 == 0:
|
|
59
|
-
exists = True
|
|
60
|
-
break
|
|
61
|
-
if not exists:
|
|
62
|
-
raise ValidationError(_(f"Invalid decade in: {decades}"))
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def validate_int_null(values):
|
|
66
|
-
"""Use a special code for null.
|
|
67
|
-
|
|
68
|
-
Because if a vuetify component has a null key it changes it to the
|
|
69
|
-
array index.
|
|
70
|
-
"""
|
|
71
|
-
for index, value in enumerate(values):
|
|
72
|
-
if value == VUETIFY_NULL_CODE:
|
|
73
|
-
values[index] = None
|
|
74
|
-
return values
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def validate_str_null(values):
|
|
78
|
-
"""Use a special code for null.
|
|
79
|
-
|
|
80
|
-
Because if a vuetify component has a null key it changes it to the
|
|
81
|
-
array index. This is the version for CharFields.
|
|
82
|
-
"""
|
|
83
|
-
for index, value in enumerate(values):
|
|
84
|
-
if value == VUETIFY_NULL_CODE_STR:
|
|
85
|
-
values[index] = None
|
|
86
|
-
return values
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class FilterListField(ListField, ABC):
|
|
90
|
-
"""Filter List field with custom arguments."""
|
|
91
|
-
|
|
92
|
-
CHILD_CLASS = Field
|
|
93
|
-
VALIDATORS = ()
|
|
94
|
-
|
|
95
|
-
def __init__(self, *args, **kwargs):
|
|
96
|
-
"""Apply the subclass's arguments."""
|
|
97
|
-
super().__init__(
|
|
98
|
-
*args,
|
|
99
|
-
child=self.CHILD_CLASS(allow_null=True, **kwargs),
|
|
100
|
-
required=False,
|
|
101
|
-
validators=self.VALIDATORS,
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
class FloatListField(FilterListField):
|
|
106
|
-
"""Decimal List Field with validation."""
|
|
107
|
-
|
|
108
|
-
CHILD_CLASS = FloatField # type: ignore
|
|
109
|
-
VALIDATORS = (validate_int_null,)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class IntListField(FilterListField):
|
|
113
|
-
"""Integer List Field with validation."""
|
|
114
|
-
|
|
115
|
-
CHILD_CLASS = IntegerField # type: ignore
|
|
116
|
-
VALIDATORS = (validate_int_null,)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
class DecadeListField(IntListField):
|
|
120
|
-
"""Integer List Field with validation for decades."""
|
|
121
|
-
|
|
122
|
-
VALIDATORS = (validate_int_null, validate_decades)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class CharListField(FilterListField):
|
|
126
|
-
"""Char List Field with validation."""
|
|
127
|
-
|
|
128
|
-
CHILD_CLASS = CharField # type: ignore
|
|
129
|
-
VALIDATORS = (validate_str_null,)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
class BooleanListField(FilterListField):
|
|
133
|
-
"""Bool List Field with validation."""
|
|
134
|
-
|
|
135
|
-
CHILD_CLASS = BooleanField # type: ignore
|
|
136
|
-
VALIDATORS = (validate_int_null,)
|
|
137
|
-
|
|
138
|
-
|
|
139
46
|
class BrowserSettingsFilterSerializer(Serializer):
|
|
140
47
|
"""Filter values for settings."""
|
|
141
48
|
|
|
@@ -10,6 +10,15 @@ from rest_framework.serializers import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
from codex.serializers.browser.filters import IntListField
|
|
13
|
+
from codex.serializers.fields import TimestampField
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BrowserCover(Serializer):
|
|
17
|
+
"""Browser Cover."""
|
|
18
|
+
|
|
19
|
+
custom = BooleanField(read_only=True, default=False)
|
|
20
|
+
pk = IntegerField(read_only=True, default=0)
|
|
21
|
+
mtime = TimestampField(read_only=True, default=0)
|
|
13
22
|
|
|
14
23
|
|
|
15
24
|
class BrowserAggregateSerializerMixin(Serializer):
|
|
@@ -20,9 +29,8 @@ class BrowserAggregateSerializerMixin(Serializer):
|
|
|
20
29
|
|
|
21
30
|
# Aggregate Annotations
|
|
22
31
|
child_count = IntegerField(read_only=True)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
cover_custom = BooleanField(read_only=True)
|
|
32
|
+
mtime = TimestampField(read_only=True)
|
|
33
|
+
cover = BrowserCover(read_only=True)
|
|
26
34
|
|
|
27
35
|
# Bookmark annotations
|
|
28
36
|
page = IntegerField(read_only=True)
|
|
@@ -31,3 +39,12 @@ class BrowserAggregateSerializerMixin(Serializer):
|
|
|
31
39
|
progress = DecimalField(
|
|
32
40
|
max_digits=5, decimal_places=2, read_only=True, coerce_to_string=False
|
|
33
41
|
)
|
|
42
|
+
|
|
43
|
+
def to_representation(self, instance):
|
|
44
|
+
"""Nest Cover Object."""
|
|
45
|
+
instance.cover = {
|
|
46
|
+
"custom": instance.cover_custom,
|
|
47
|
+
"pk": instance.cover_pk,
|
|
48
|
+
"mtime": instance.cover_mtime,
|
|
49
|
+
}
|
|
50
|
+
return super().to_representation(instance)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from rest_framework.serializers import (
|
|
4
4
|
BooleanField,
|
|
5
5
|
CharField,
|
|
6
|
-
DateTimeField,
|
|
7
6
|
DecimalField,
|
|
8
7
|
IntegerField,
|
|
9
8
|
ListSerializer,
|
|
@@ -13,6 +12,7 @@ from rest_framework.serializers import (
|
|
|
13
12
|
from codex.serializers.browser.mixins import (
|
|
14
13
|
BrowserAggregateSerializerMixin,
|
|
15
14
|
)
|
|
15
|
+
from codex.serializers.fields import TimestampField
|
|
16
16
|
from codex.serializers.route import RouteSerializer
|
|
17
17
|
|
|
18
18
|
|
|
@@ -34,7 +34,6 @@ class BrowserCardSerializer(BrowserAggregateSerializerMixin):
|
|
|
34
34
|
order_value = CharField(read_only=True)
|
|
35
35
|
page_count = IntegerField(read_only=True)
|
|
36
36
|
reading_direction = CharField(read_only=True)
|
|
37
|
-
mtime = DateTimeField(format="%s", read_only=True)
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
class BrowserAdminFlagsSerializer(Serializer):
|
|
@@ -57,10 +56,10 @@ class BrowserPageSerializer(Serializer):
|
|
|
57
56
|
admin_flags = BrowserAdminFlagsSerializer(read_only=True)
|
|
58
57
|
breadcrumbs = ListSerializer(child=RouteSerializer())
|
|
59
58
|
title = BrowserTitleSerializer(read_only=True)
|
|
60
|
-
covers_timestamp = IntegerField(read_only=True)
|
|
61
59
|
zero_pad = IntegerField(read_only=True)
|
|
62
60
|
libraries_exist = BooleanField(read_only=True)
|
|
63
61
|
model_group = CharField(read_only=True)
|
|
64
62
|
num_pages = IntegerField(read_only=True)
|
|
65
63
|
groups = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
|
|
66
64
|
books = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
|
|
65
|
+
mtime = TimestampField(read_only=True)
|
|
@@ -10,6 +10,7 @@ from rest_framework.serializers import (
|
|
|
10
10
|
|
|
11
11
|
from codex.serializers.browser.filters import BrowserSettingsFilterSerializer
|
|
12
12
|
from codex.serializers.choices import CHOICES, VUETIFY_NULL_CODE
|
|
13
|
+
from codex.serializers.fields import TimestampField
|
|
13
14
|
|
|
14
15
|
VUETIFY_NULL_CODE_STR = str(VUETIFY_NULL_CODE)
|
|
15
16
|
|
|
@@ -44,3 +45,4 @@ class BrowserSettingsSerializer(Serializer):
|
|
|
44
45
|
top_group = ChoiceField(choices=tuple(CHOICES["topGroup"].keys()), required=False)
|
|
45
46
|
opds_metadata = BooleanField(required=False)
|
|
46
47
|
limit = IntegerField(required=False)
|
|
48
|
+
mtime = TimestampField(read_only=True)
|