codex 1.7.2__py3-none-any.whl → 1.7.3a0__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/choices.py +13 -5
- codex/librarian/bookmark/bookmarkd.py +4 -5
- codex/librarian/bookmark/tasks.py +1 -1
- codex/librarian/bookmark/update.py +15 -29
- codex/librarian/cron/crond.py +1 -1
- codex/librarian/importer/query_fks.py +2 -2
- codex/librarian/janitor/janitor.py +11 -1
- codex/librarian/janitor/log_compress.py +77 -0
- codex/librarian/janitor/status.py +1 -0
- codex/librarian/janitor/tasks.py +5 -0
- codex/librarian/librariand.py +18 -13
- codex/librarian/search/update.py +8 -4
- codex/migrations/0031_adminflag_banner.py +50 -0
- codex/models/admin.py +4 -3
- codex/serializers/admin/flags.py +1 -1
- codex/serializers/auth.py +1 -0
- codex/serializers/opds/v2/__init__.py +1 -0
- codex/serializers/opds/v2/facet.py +13 -0
- codex/serializers/opds/v2/feed.py +35 -0
- codex/serializers/opds/v2/links.py +73 -0
- codex/serializers/opds/v2/metadata.py +22 -0
- codex/serializers/opds/v2/progression.py +63 -0
- codex/serializers/opds/v2/publication.py +78 -0
- codex/serializers/opds/{v2_unused.py → v2/unused.py} +1 -1
- codex/settings/settings.py +1 -0
- codex/static_root/assets/VCheckbox-pHjSEgdV.176f3c1f4191.js +1 -0
- codex/static_root/assets/VCheckbox-pHjSEgdV.176f3c1f4191.js.br +0 -0
- codex/static_root/assets/VCheckbox-pHjSEgdV.176f3c1f4191.js.gz +0 -0
- codex/static_root/assets/VCheckbox-pHjSEgdV.js +1 -0
- codex/static_root/assets/VCheckbox-pHjSEgdV.js.br +0 -0
- codex/static_root/assets/VCheckbox-pHjSEgdV.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DBKqrZGq.467e75859b62.js +1 -0
- codex/static_root/assets/VCheckboxBtn-DBKqrZGq.467e75859b62.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DBKqrZGq.467e75859b62.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DBKqrZGq.js +1 -0
- codex/static_root/assets/VCheckboxBtn-DBKqrZGq.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DBKqrZGq.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bg7uOTbF.2fc6073b789f.js +1 -0
- codex/static_root/assets/VCombobox-Bg7uOTbF.2fc6073b789f.js.br +0 -0
- codex/static_root/assets/VCombobox-Bg7uOTbF.2fc6073b789f.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bg7uOTbF.js +1 -0
- codex/static_root/assets/VCombobox-Bg7uOTbF.js.br +0 -0
- codex/static_root/assets/VCombobox-Bg7uOTbF.js.gz +0 -0
- codex/static_root/assets/VDialog-Dyd05WHA.b07aca713774.css +1 -0
- codex/static_root/assets/VDialog-Dyd05WHA.b07aca713774.css.br +0 -0
- codex/static_root/assets/VDialog-Dyd05WHA.b07aca713774.css.gz +0 -0
- codex/static_root/assets/VDialog-Dyd05WHA.css +1 -0
- codex/static_root/assets/VDialog-Dyd05WHA.css.br +0 -0
- codex/static_root/assets/VDialog-Dyd05WHA.css.gz +0 -0
- codex/static_root/assets/VDialog-IrbBYFLM.91a058d572c4.js +1 -0
- codex/static_root/assets/VDialog-IrbBYFLM.91a058d572c4.js.br +0 -0
- codex/static_root/assets/VDialog-IrbBYFLM.91a058d572c4.js.gz +0 -0
- codex/static_root/assets/VDialog-IrbBYFLM.js +1 -0
- codex/static_root/assets/VDialog-IrbBYFLM.js.br +0 -0
- codex/static_root/assets/VDialog-IrbBYFLM.js.gz +0 -0
- codex/static_root/assets/VDivider-CuRohbVQ.3c12b040aed7.js +1 -0
- codex/static_root/assets/VDivider-CuRohbVQ.3c12b040aed7.js.br +0 -0
- codex/static_root/assets/VDivider-CuRohbVQ.3c12b040aed7.js.gz +0 -0
- codex/static_root/assets/VDivider-CuRohbVQ.js +1 -0
- codex/static_root/assets/VDivider-CuRohbVQ.js.br +0 -0
- codex/static_root/assets/VDivider-CuRohbVQ.js.gz +0 -0
- codex/static_root/assets/VDivider-DE8MJPGz.3d2e17799e5c.css +1 -0
- codex/static_root/assets/VDivider-DE8MJPGz.3d2e17799e5c.css.br +0 -0
- codex/static_root/assets/VDivider-DE8MJPGz.3d2e17799e5c.css.gz +0 -0
- codex/static_root/assets/VDivider-DE8MJPGz.css +1 -0
- codex/static_root/assets/VDivider-DE8MJPGz.css.br +0 -0
- codex/static_root/assets/VDivider-DE8MJPGz.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-b0xWLH75.bff6ea8b4055.js +1 -0
- codex/static_root/assets/VExpansionPanels-b0xWLH75.bff6ea8b4055.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-b0xWLH75.bff6ea8b4055.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-b0xWLH75.js +1 -0
- codex/static_root/assets/VExpansionPanels-b0xWLH75.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-b0xWLH75.js.gz +0 -0
- codex/static_root/assets/VForm-B4hkqnS8.afbd5cf0908a.js +1 -0
- codex/static_root/assets/VForm-B4hkqnS8.afbd5cf0908a.js.br +0 -0
- codex/static_root/assets/VForm-B4hkqnS8.afbd5cf0908a.js.gz +0 -0
- codex/static_root/assets/VForm-B4hkqnS8.js +1 -0
- codex/static_root/assets/VForm-B4hkqnS8.js.br +0 -0
- codex/static_root/assets/VForm-B4hkqnS8.js.gz +0 -0
- codex/static_root/assets/VForm-v1Gmu6E1.138c0b89b6fc.css +1 -0
- codex/static_root/assets/VForm-v1Gmu6E1.138c0b89b6fc.css.br +0 -0
- codex/static_root/assets/VForm-v1Gmu6E1.138c0b89b6fc.css.gz +0 -0
- codex/static_root/assets/VForm-v1Gmu6E1.css +1 -0
- codex/static_root/assets/VForm-v1Gmu6E1.css.br +0 -0
- codex/static_root/assets/VForm-v1Gmu6E1.css.gz +0 -0
- codex/static_root/assets/VRadioGroup-Cbayik6d.034f0f275265.js +1 -0
- codex/static_root/assets/VRadioGroup-Cbayik6d.034f0f275265.js.br +0 -0
- codex/static_root/assets/VRadioGroup-Cbayik6d.034f0f275265.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-Cbayik6d.js +1 -0
- codex/static_root/assets/VRadioGroup-Cbayik6d.js.br +0 -0
- codex/static_root/assets/VRadioGroup-Cbayik6d.js.gz +0 -0
- codex/static_root/assets/VSelect-8MFoaOUz.6309f7bf17bb.css +1 -0
- codex/static_root/assets/VSelect-8MFoaOUz.6309f7bf17bb.css.br +0 -0
- codex/static_root/assets/VSelect-8MFoaOUz.6309f7bf17bb.css.gz +0 -0
- codex/static_root/assets/VSelect-8MFoaOUz.css +1 -0
- codex/static_root/assets/VSelect-8MFoaOUz.css.br +0 -0
- codex/static_root/assets/VSelect-8MFoaOUz.css.gz +0 -0
- codex/static_root/assets/VSelect-BsuB-2P1.abe187c8b53f.js +1 -0
- codex/static_root/assets/VSelect-BsuB-2P1.abe187c8b53f.js.br +0 -0
- codex/static_root/assets/VSelect-BsuB-2P1.abe187c8b53f.js.gz +0 -0
- codex/static_root/assets/VSelect-BsuB-2P1.js +1 -0
- codex/static_root/assets/VSelect-BsuB-2P1.js.br +0 -0
- codex/static_root/assets/VSelect-BsuB-2P1.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DLxcLoTI.cc18de4e83f6.css +1 -0
- codex/static_root/assets/VSelectionControl-DLxcLoTI.cc18de4e83f6.css.br +0 -0
- codex/static_root/assets/VSelectionControl-DLxcLoTI.cc18de4e83f6.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-DLxcLoTI.css +1 -0
- codex/static_root/assets/VSelectionControl-DLxcLoTI.css.br +0 -0
- codex/static_root/assets/VSelectionControl-DLxcLoTI.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-L41n5bnX.f4674bb62425.js +1 -0
- codex/static_root/assets/VSelectionControl-L41n5bnX.f4674bb62425.js.br +0 -0
- codex/static_root/assets/VSelectionControl-L41n5bnX.f4674bb62425.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-L41n5bnX.js +1 -0
- codex/static_root/assets/VSelectionControl-L41n5bnX.js.br +0 -0
- codex/static_root/assets/VSelectionControl-L41n5bnX.js.gz +0 -0
- codex/static_root/assets/VTable-CYv4gUUt.76e4658abeb3.js +1 -0
- codex/static_root/assets/VTable-CYv4gUUt.76e4658abeb3.js.br +0 -0
- codex/static_root/assets/VTable-CYv4gUUt.76e4658abeb3.js.gz +0 -0
- codex/static_root/assets/VTable-CYv4gUUt.js +1 -0
- codex/static_root/assets/VTable-CYv4gUUt.js.br +0 -0
- codex/static_root/assets/VTable-CYv4gUUt.js.gz +0 -0
- codex/static_root/assets/VWindowItem-f8UkxXa_.1d3e9e78fc9b.js +1 -0
- codex/static_root/assets/VWindowItem-f8UkxXa_.1d3e9e78fc9b.js.br +0 -0
- codex/static_root/assets/VWindowItem-f8UkxXa_.1d3e9e78fc9b.js.gz +0 -0
- codex/static_root/assets/VWindowItem-f8UkxXa_.js +1 -0
- codex/static_root/assets/VWindowItem-f8UkxXa_.js.br +0 -0
- codex/static_root/assets/VWindowItem-f8UkxXa_.js.gz +0 -0
- codex/static_root/assets/admin-BmuWJcAB.b0b253aefd88.js +1 -0
- codex/static_root/assets/admin-BmuWJcAB.b0b253aefd88.js.br +0 -0
- codex/static_root/assets/admin-BmuWJcAB.b0b253aefd88.js.gz +0 -0
- codex/static_root/assets/admin-BmuWJcAB.js +1 -0
- codex/static_root/assets/admin-BmuWJcAB.js.br +0 -0
- codex/static_root/assets/admin-BmuWJcAB.js.gz +0 -0
- codex/static_root/assets/admin-Izb24SXM.504ea9d9515c.js +1 -0
- codex/static_root/assets/admin-Izb24SXM.504ea9d9515c.js.br +0 -0
- codex/static_root/assets/admin-Izb24SXM.504ea9d9515c.js.gz +0 -0
- codex/static_root/assets/admin-Izb24SXM.js +1 -0
- codex/static_root/assets/admin-Izb24SXM.js.br +0 -0
- codex/static_root/assets/admin-Izb24SXM.js.gz +0 -0
- codex/static_root/assets/admin-dm3bSP7O.ae1234cc0beb.css +1 -0
- codex/static_root/assets/admin-dm3bSP7O.ae1234cc0beb.css.br +0 -0
- codex/static_root/assets/admin-dm3bSP7O.ae1234cc0beb.css.gz +0 -0
- codex/static_root/assets/admin-dm3bSP7O.css +1 -0
- codex/static_root/assets/admin-dm3bSP7O.css.br +0 -0
- codex/static_root/assets/admin-dm3bSP7O.css.gz +0 -0
- codex/static_root/assets/admin-menu-C712OV-9.ecd1f2af4cf4.js +1 -0
- codex/static_root/assets/admin-menu-C712OV-9.ecd1f2af4cf4.js.br +0 -0
- codex/static_root/assets/admin-menu-C712OV-9.ecd1f2af4cf4.js.gz +0 -0
- codex/static_root/assets/admin-menu-C712OV-9.js +1 -0
- codex/static_root/assets/admin-menu-C712OV-9.js.br +0 -0
- codex/static_root/assets/admin-menu-C712OV-9.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.2536580dece1.js +1 -0
- codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.2536580dece1.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.2536580dece1.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.js +1 -0
- codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.js.gz +0 -0
- codex/static_root/assets/browser-CReWGhn_.521193ab0710.css +1 -0
- codex/static_root/assets/browser-CReWGhn_.521193ab0710.css.br +0 -0
- codex/static_root/assets/browser-CReWGhn_.521193ab0710.css.gz +0 -0
- codex/static_root/assets/browser-CReWGhn_.css +1 -0
- codex/static_root/assets/browser-CReWGhn_.css.br +0 -0
- codex/static_root/assets/browser-CReWGhn_.css.gz +0 -0
- codex/static_root/assets/browser-DiAN9Ipo.a5b62a3cbe1d.js +1 -0
- codex/static_root/assets/browser-DiAN9Ipo.a5b62a3cbe1d.js.br +0 -0
- codex/static_root/assets/browser-DiAN9Ipo.a5b62a3cbe1d.js.gz +0 -0
- codex/static_root/assets/browser-DiAN9Ipo.js +1 -0
- codex/static_root/assets/browser-DiAN9Ipo.js.br +0 -0
- codex/static_root/assets/browser-DiAN9Ipo.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BZPmAOML.73fdc23e13a4.js +1 -0
- codex/static_root/assets/change-password-dialog-BZPmAOML.73fdc23e13a4.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BZPmAOML.73fdc23e13a4.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BZPmAOML.js +1 -0
- codex/static_root/assets/change-password-dialog-BZPmAOML.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BZPmAOML.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CBLQOik_.f53acd0c622a.js +1 -0
- codex/static_root/assets/confirm-dialog-CBLQOik_.f53acd0c622a.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CBLQOik_.f53acd0c622a.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CBLQOik_.js +1 -0
- codex/static_root/assets/confirm-dialog-CBLQOik_.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CBLQOik_.js.gz +0 -0
- codex/static_root/assets/datetime-column-CU19rCbi.9edc0d2da736.js +1 -0
- codex/static_root/assets/datetime-column-CU19rCbi.9edc0d2da736.js.br +1 -0
- codex/static_root/assets/datetime-column-CU19rCbi.9edc0d2da736.js.gz +0 -0
- codex/static_root/assets/datetime-column-CU19rCbi.js +1 -0
- codex/static_root/assets/datetime-column-CU19rCbi.js.br +1 -0
- codex/static_root/assets/datetime-column-CU19rCbi.js.gz +0 -0
- codex/static_root/assets/{filter-ud5mPZpn.0f0a537959aa.js → filter-CZ7kkBLq.171b4fcba146.js} +1 -1
- codex/static_root/assets/filter-CZ7kkBLq.171b4fcba146.js.br +0 -0
- codex/static_root/assets/filter-CZ7kkBLq.171b4fcba146.js.gz +0 -0
- codex/static_root/assets/{filter-ud5mPZpn.js → filter-CZ7kkBLq.js} +1 -1
- codex/static_root/assets/filter-CZ7kkBLq.js.br +0 -0
- codex/static_root/assets/filter-CZ7kkBLq.js.gz +0 -0
- codex/static_root/assets/flag-tab-DHGfv0Q3.2b7ef394b850.css +1 -0
- codex/static_root/assets/flag-tab-DHGfv0Q3.2b7ef394b850.css.br +0 -0
- codex/static_root/assets/flag-tab-DHGfv0Q3.2b7ef394b850.css.gz +0 -0
- codex/static_root/assets/flag-tab-DHGfv0Q3.css +1 -0
- codex/static_root/assets/flag-tab-DHGfv0Q3.css.br +0 -0
- codex/static_root/assets/flag-tab-DHGfv0Q3.css.gz +0 -0
- codex/static_root/assets/flag-tab-kcBN36xe.631c48ab75fd.js +1 -0
- codex/static_root/assets/flag-tab-kcBN36xe.631c48ab75fd.js.br +0 -0
- codex/static_root/assets/flag-tab-kcBN36xe.631c48ab75fd.js.gz +0 -0
- codex/static_root/assets/flag-tab-kcBN36xe.js +1 -0
- codex/static_root/assets/flag-tab-kcBN36xe.js.br +0 -0
- codex/static_root/assets/flag-tab-kcBN36xe.js.gz +0 -0
- codex/static_root/assets/forwardRefs-DNAemYUk.06a315272ed9.js +1 -0
- codex/static_root/assets/forwardRefs-DNAemYUk.06a315272ed9.js.br +0 -0
- codex/static_root/assets/forwardRefs-DNAemYUk.06a315272ed9.js.gz +0 -0
- codex/static_root/assets/forwardRefs-DNAemYUk.js +1 -0
- codex/static_root/assets/forwardRefs-DNAemYUk.js.br +0 -0
- codex/static_root/assets/forwardRefs-DNAemYUk.js.gz +0 -0
- codex/static_root/assets/forwardRefs-zKOZsbbr.9b56b4459240.css +1 -0
- codex/static_root/assets/forwardRefs-zKOZsbbr.9b56b4459240.css.br +0 -0
- codex/static_root/assets/forwardRefs-zKOZsbbr.9b56b4459240.css.gz +0 -0
- codex/static_root/assets/forwardRefs-zKOZsbbr.css +1 -0
- codex/static_root/assets/forwardRefs-zKOZsbbr.css.br +0 -0
- codex/static_root/assets/forwardRefs-zKOZsbbr.css.gz +0 -0
- codex/static_root/assets/group-tab-D9gwAdJc.60fb0ffa734c.js +1 -0
- codex/static_root/assets/group-tab-D9gwAdJc.60fb0ffa734c.js.br +0 -0
- codex/static_root/assets/group-tab-D9gwAdJc.60fb0ffa734c.js.gz +0 -0
- codex/static_root/assets/group-tab-D9gwAdJc.js +1 -0
- codex/static_root/assets/group-tab-D9gwAdJc.js.br +0 -0
- codex/static_root/assets/group-tab-D9gwAdJc.js.gz +0 -0
- codex/static_root/assets/http-error-DEJmFobt.8c13689ae574.css +1 -0
- codex/static_root/assets/http-error-DEJmFobt.8c13689ae574.css.br +0 -0
- codex/static_root/assets/http-error-DEJmFobt.8c13689ae574.css.gz +0 -0
- codex/static_root/assets/http-error-DEJmFobt.css +1 -0
- codex/static_root/assets/http-error-DEJmFobt.css.br +0 -0
- codex/static_root/assets/http-error-DEJmFobt.css.gz +0 -0
- codex/static_root/assets/http-error-DmFqD2Xz.593d626b5f1b.js +1 -0
- codex/static_root/assets/http-error-DmFqD2Xz.593d626b5f1b.js.br +0 -0
- codex/static_root/assets/http-error-DmFqD2Xz.593d626b5f1b.js.gz +0 -0
- codex/static_root/assets/http-error-DmFqD2Xz.js +1 -0
- codex/static_root/assets/http-error-DmFqD2Xz.js.br +0 -0
- codex/static_root/assets/http-error-DmFqD2Xz.js.gz +0 -0
- codex/static_root/assets/index-hvGLIBbY.30ce8cca0c6d.js +1 -0
- codex/static_root/assets/index-hvGLIBbY.30ce8cca0c6d.js.br +0 -0
- codex/static_root/assets/index-hvGLIBbY.30ce8cca0c6d.js.gz +0 -0
- codex/static_root/assets/index-hvGLIBbY.js +1 -0
- codex/static_root/assets/index-hvGLIBbY.js.br +0 -0
- codex/static_root/assets/index-hvGLIBbY.js.gz +0 -0
- codex/static_root/assets/library-tab-CF-RAEgP.a0ad816fb994.js +1 -0
- codex/static_root/assets/library-tab-CF-RAEgP.a0ad816fb994.js.br +0 -0
- codex/static_root/assets/library-tab-CF-RAEgP.a0ad816fb994.js.gz +0 -0
- codex/static_root/assets/library-tab-CF-RAEgP.js +1 -0
- codex/static_root/assets/library-tab-CF-RAEgP.js.br +0 -0
- codex/static_root/assets/library-tab-CF-RAEgP.js.gz +0 -0
- codex/static_root/assets/{library-tab-Cat0MAK5.5911f022188e.css → library-tab-Ce6qaeCV.75baedb7e976.css} +1 -1
- codex/static_root/assets/library-tab-Ce6qaeCV.75baedb7e976.css.br +0 -0
- codex/static_root/assets/library-tab-Ce6qaeCV.75baedb7e976.css.gz +0 -0
- codex/static_root/assets/{library-tab-Cat0MAK5.css → library-tab-Ce6qaeCV.css} +1 -1
- codex/static_root/assets/library-tab-Ce6qaeCV.css.br +0 -0
- codex/static_root/assets/library-tab-Ce6qaeCV.css.gz +0 -0
- codex/static_root/assets/main-Dv8e0bMI.bd74f1ba1627.js +35 -0
- codex/static_root/assets/main-Dv8e0bMI.bd74f1ba1627.js.br +0 -0
- codex/static_root/assets/main-Dv8e0bMI.bd74f1ba1627.js.gz +0 -0
- codex/static_root/assets/main-Dv8e0bMI.js +35 -0
- codex/static_root/assets/main-Dv8e0bMI.js.br +0 -0
- codex/static_root/assets/main-Dv8e0bMI.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.04a4f5c451b4.js +1 -0
- codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.04a4f5c451b4.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.04a4f5c451b4.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.js +1 -0
- codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DSe0TkMm.css +1 -0
- codex/static_root/assets/pagination-toolbar-DSe0TkMm.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DSe0TkMm.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DSe0TkMm.dcb0eba65827.css +1 -0
- codex/static_root/assets/pagination-toolbar-DSe0TkMm.dcb0eba65827.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DSe0TkMm.dcb0eba65827.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-D_whea1o.9bbd927058f1.js +1 -0
- codex/static_root/assets/pagination-toolbar-D_whea1o.9bbd927058f1.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-D_whea1o.9bbd927058f1.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-D_whea1o.js +1 -0
- codex/static_root/assets/pagination-toolbar-D_whea1o.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-D_whea1o.js.gz +0 -0
- codex/static_root/assets/pdf-doc-dnr5fHbU.fa1d7ed9255b.js +348 -0
- codex/static_root/assets/pdf-doc-dnr5fHbU.fa1d7ed9255b.js.br +0 -0
- codex/static_root/assets/pdf-doc-dnr5fHbU.fa1d7ed9255b.js.gz +0 -0
- codex/static_root/assets/pdf-doc-dnr5fHbU.js +348 -0
- codex/static_root/assets/pdf-doc-dnr5fHbU.js.br +0 -0
- codex/static_root/assets/pdf-doc-dnr5fHbU.js.gz +0 -0
- codex/static_root/assets/{reader-Dd2KmdCN.115d4bfe9280.css → reader-B3hgta6g.2ed32f08e906.css} +1 -1
- codex/static_root/assets/reader-B3hgta6g.2ed32f08e906.css.br +0 -0
- codex/static_root/assets/reader-B3hgta6g.2ed32f08e906.css.gz +0 -0
- codex/static_root/assets/{reader-Dd2KmdCN.css → reader-B3hgta6g.css} +1 -1
- codex/static_root/assets/reader-B3hgta6g.css.br +0 -0
- codex/static_root/assets/reader-B3hgta6g.css.gz +0 -0
- codex/static_root/assets/reader-Cn-ra_Dv.f38c0b419dec.js +2 -0
- codex/static_root/assets/reader-Cn-ra_Dv.f38c0b419dec.js.br +0 -0
- codex/static_root/assets/reader-Cn-ra_Dv.f38c0b419dec.js.gz +0 -0
- codex/static_root/assets/reader-Cn-ra_Dv.js +2 -0
- codex/static_root/assets/reader-Cn-ra_Dv.js.br +0 -0
- codex/static_root/assets/reader-Cn-ra_Dv.js.gz +0 -0
- codex/static_root/assets/relation-chips-fOPxWAMU.78e5fe70c014.js +1 -0
- codex/static_root/assets/relation-chips-fOPxWAMU.78e5fe70c014.js.br +0 -0
- codex/static_root/assets/relation-chips-fOPxWAMU.78e5fe70c014.js.gz +0 -0
- codex/static_root/assets/relation-chips-fOPxWAMU.js +1 -0
- codex/static_root/assets/relation-chips-fOPxWAMU.js.br +0 -0
- codex/static_root/assets/relation-chips-fOPxWAMU.js.gz +0 -0
- codex/static_root/assets/settings-drawer-1UEq8JY1.d95cee9aefb5.js +2 -0
- codex/static_root/assets/settings-drawer-1UEq8JY1.d95cee9aefb5.js.br +0 -0
- codex/static_root/assets/settings-drawer-1UEq8JY1.d95cee9aefb5.js.gz +0 -0
- codex/static_root/assets/settings-drawer-1UEq8JY1.js +2 -0
- codex/static_root/assets/settings-drawer-1UEq8JY1.js.br +0 -0
- codex/static_root/assets/settings-drawer-1UEq8JY1.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CqJ4gXLq.75a6ea969e71.css +1 -0
- codex/static_root/assets/settings-drawer-CqJ4gXLq.75a6ea969e71.css.br +0 -0
- codex/static_root/assets/settings-drawer-CqJ4gXLq.75a6ea969e71.css.gz +0 -0
- codex/static_root/assets/settings-drawer-CqJ4gXLq.css +1 -0
- codex/static_root/assets/settings-drawer-CqJ4gXLq.css.br +0 -0
- codex/static_root/assets/settings-drawer-CqJ4gXLq.css.gz +0 -0
- codex/static_root/assets/stats-tab-0ptRCeKw.5514d3508e37.js +1 -0
- codex/static_root/assets/stats-tab-0ptRCeKw.5514d3508e37.js.br +0 -0
- codex/static_root/assets/stats-tab-0ptRCeKw.5514d3508e37.js.gz +0 -0
- codex/static_root/assets/stats-tab-0ptRCeKw.js +1 -0
- codex/static_root/assets/stats-tab-0ptRCeKw.js.br +0 -0
- codex/static_root/assets/stats-tab-0ptRCeKw.js.gz +0 -0
- codex/static_root/assets/task-tab-CG9_bi6Z.ad9f647f8e5f.js +1 -0
- codex/static_root/assets/task-tab-CG9_bi6Z.ad9f647f8e5f.js.br +0 -0
- codex/static_root/assets/task-tab-CG9_bi6Z.ad9f647f8e5f.js.gz +0 -0
- codex/static_root/assets/task-tab-CG9_bi6Z.js +1 -0
- codex/static_root/assets/task-tab-CG9_bi6Z.js.br +0 -0
- codex/static_root/assets/task-tab-CG9_bi6Z.js.gz +0 -0
- codex/static_root/assets/unauthorized-VnIXUO1w.c5536d9dae75.css +1 -0
- codex/static_root/assets/unauthorized-VnIXUO1w.c5536d9dae75.css.br +0 -0
- codex/static_root/assets/unauthorized-VnIXUO1w.c5536d9dae75.css.gz +0 -0
- codex/static_root/assets/unauthorized-VnIXUO1w.css +1 -0
- codex/static_root/assets/unauthorized-VnIXUO1w.css.br +0 -0
- codex/static_root/assets/unauthorized-VnIXUO1w.css.gz +0 -0
- codex/static_root/assets/unauthorized-vG1Lh0m_.0c4bae81ee09.js +1 -0
- codex/static_root/assets/unauthorized-vG1Lh0m_.0c4bae81ee09.js.br +0 -0
- codex/static_root/assets/unauthorized-vG1Lh0m_.0c4bae81ee09.js.gz +0 -0
- codex/static_root/assets/unauthorized-vG1Lh0m_.js +1 -0
- codex/static_root/assets/unauthorized-vG1Lh0m_.js.br +0 -0
- codex/static_root/assets/unauthorized-vG1Lh0m_.js.gz +0 -0
- codex/static_root/assets/user-tab-BOxWlpY7.1a94280a799b.js +1 -0
- codex/static_root/assets/user-tab-BOxWlpY7.1a94280a799b.js.br +0 -0
- codex/static_root/assets/user-tab-BOxWlpY7.1a94280a799b.js.gz +0 -0
- codex/static_root/assets/user-tab-BOxWlpY7.js +1 -0
- codex/static_root/assets/user-tab-BOxWlpY7.js.br +0 -0
- codex/static_root/assets/user-tab-BOxWlpY7.js.gz +0 -0
- codex/static_root/manifest.9b3479a6a4a6.json +735 -0
- codex/static_root/manifest.9b3479a6a4a6.json.br +0 -0
- codex/static_root/manifest.9b3479a6a4a6.json.gz +0 -0
- codex/static_root/manifest.json +365 -342
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/templates/index.html +1 -1
- codex/threads.py +8 -3
- codex/urls/api/admin.py +1 -0
- codex/urls/api/browser.py +1 -1
- codex/urls/opds/v2.py +6 -0
- codex/views/admin/flag.py +5 -1
- codex/views/admin/tasks.py +2 -0
- codex/views/bookmark.py +53 -41
- codex/views/browser/annotate/bookmark.py +6 -4
- codex/views/browser/annotate/card.py +1 -16
- codex/views/browser/annotate/order.py +45 -18
- codex/views/browser/bookmark.py +53 -0
- codex/views/browser/download.py +2 -0
- codex/views/browser/filters/bookmark.py +4 -32
- codex/views/browser/group_mtime.py +2 -2
- codex/views/browser/metadata/annotate.py +1 -1
- codex/views/browser/metadata/metadata.py +1 -1
- codex/views/browser/order_by.py +6 -1
- codex/views/frontend.py +19 -4
- codex/views/mixins.py +16 -28
- codex/views/opds/const.py +2 -0
- codex/views/opds/util.py +2 -2
- codex/views/opds/v1/entry/links.py +1 -2
- codex/views/opds/v2/feed.py +3 -2
- codex/views/opds/v2/href.py +60 -0
- codex/views/opds/v2/links.py +13 -47
- codex/views/opds/v2/progression.py +137 -0
- codex/views/opds/v2/publications.py +21 -1
- codex/views/opds/v2/top_links.py +2 -1
- codex/views/public.py +11 -4
- codex/views/reader/books.py +19 -8
- codex/views/reader/page.py +5 -5
- {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/METADATA +21 -26
- {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/RECORD +387 -370
- codex/serializers/opds/v2.py +0 -178
- codex/static_root/assets/VCheckbox-DLMzIII1.1ff8fc2d9a21.js +0 -1
- codex/static_root/assets/VCheckbox-DLMzIII1.1ff8fc2d9a21.js.br +0 -0
- codex/static_root/assets/VCheckbox-DLMzIII1.1ff8fc2d9a21.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DLMzIII1.js +0 -1
- codex/static_root/assets/VCheckbox-DLMzIII1.js.br +0 -0
- codex/static_root/assets/VCheckbox-DLMzIII1.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BzoQl8XC.094679a3624b.js +0 -1
- codex/static_root/assets/VCheckboxBtn-BzoQl8XC.094679a3624b.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BzoQl8XC.094679a3624b.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BzoQl8XC.js +0 -1
- codex/static_root/assets/VCheckboxBtn-BzoQl8XC.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BzoQl8XC.js.gz +0 -0
- codex/static_root/assets/VCombobox-CBY8K5CP.fcf2221a9edd.js +0 -1
- codex/static_root/assets/VCombobox-CBY8K5CP.fcf2221a9edd.js.br +0 -0
- codex/static_root/assets/VCombobox-CBY8K5CP.fcf2221a9edd.js.gz +0 -0
- codex/static_root/assets/VCombobox-CBY8K5CP.js +0 -1
- codex/static_root/assets/VCombobox-CBY8K5CP.js.br +0 -0
- codex/static_root/assets/VCombobox-CBY8K5CP.js.gz +0 -0
- codex/static_root/assets/VDialog-DXQz0Ylp.7b23d86dbd0d.css +0 -1
- codex/static_root/assets/VDialog-DXQz0Ylp.7b23d86dbd0d.css.br +0 -0
- codex/static_root/assets/VDialog-DXQz0Ylp.7b23d86dbd0d.css.gz +0 -0
- codex/static_root/assets/VDialog-DXQz0Ylp.css +0 -1
- codex/static_root/assets/VDialog-DXQz0Ylp.css.br +0 -0
- codex/static_root/assets/VDialog-DXQz0Ylp.css.gz +0 -0
- codex/static_root/assets/VDialog-Db4tJYqR.e21a79a5030e.js +0 -1
- codex/static_root/assets/VDialog-Db4tJYqR.e21a79a5030e.js.br +0 -0
- codex/static_root/assets/VDialog-Db4tJYqR.e21a79a5030e.js.gz +0 -0
- codex/static_root/assets/VDialog-Db4tJYqR.js +0 -1
- codex/static_root/assets/VDialog-Db4tJYqR.js.br +0 -0
- codex/static_root/assets/VDialog-Db4tJYqR.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DN4yGbOt.163e59e38865.js +0 -1
- codex/static_root/assets/VExpansionPanels-DN4yGbOt.163e59e38865.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DN4yGbOt.163e59e38865.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DN4yGbOt.js +0 -1
- codex/static_root/assets/VExpansionPanels-DN4yGbOt.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DN4yGbOt.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-D2-PWEfs.ce7644863c25.js +0 -1
- codex/static_root/assets/VRadioGroup-D2-PWEfs.ce7644863c25.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D2-PWEfs.ce7644863c25.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-D2-PWEfs.js +0 -1
- codex/static_root/assets/VRadioGroup-D2-PWEfs.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D2-PWEfs.js.gz +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css +0 -1
- codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.br +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.gz +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.css +0 -1
- codex/static_root/assets/VSelect-DLu8bpcs.css.br +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.css.gz +0 -0
- codex/static_root/assets/VSelect-DqXJ23Pc.a1360cbed050.js +0 -1
- codex/static_root/assets/VSelect-DqXJ23Pc.a1360cbed050.js.br +0 -0
- codex/static_root/assets/VSelect-DqXJ23Pc.a1360cbed050.js.gz +0 -0
- codex/static_root/assets/VSelect-DqXJ23Pc.js +0 -1
- codex/static_root/assets/VSelect-DqXJ23Pc.js.br +0 -0
- codex/static_root/assets/VSelect-DqXJ23Pc.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css +0 -1
- codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.br +0 -0
- codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-CiViEYoC.css +0 -1
- codex/static_root/assets/VSelectionControl-CiViEYoC.css.br +0 -0
- codex/static_root/assets/VSelectionControl-CiViEYoC.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-CxhDNufT.dd7ca99567fc.js +0 -1
- codex/static_root/assets/VSelectionControl-CxhDNufT.dd7ca99567fc.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CxhDNufT.dd7ca99567fc.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CxhDNufT.js +0 -1
- codex/static_root/assets/VSelectionControl-CxhDNufT.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CxhDNufT.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css +0 -1
- codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.br +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.gz +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.css +0 -1
- codex/static_root/assets/VSlideGroup-BtXPhMOT.css.br +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.css.gz +0 -0
- codex/static_root/assets/VSlideGroup-DRbthp6q.7365c5542d47.js +0 -1
- codex/static_root/assets/VSlideGroup-DRbthp6q.7365c5542d47.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DRbthp6q.7365c5542d47.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DRbthp6q.js +0 -1
- codex/static_root/assets/VSlideGroup-DRbthp6q.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DRbthp6q.js.gz +0 -0
- codex/static_root/assets/VTable-Cbq-yJos.c884706cac21.js +0 -1
- codex/static_root/assets/VTable-Cbq-yJos.c884706cac21.js.br +0 -0
- codex/static_root/assets/VTable-Cbq-yJos.c884706cac21.js.gz +0 -0
- codex/static_root/assets/VTable-Cbq-yJos.js +0 -1
- codex/static_root/assets/VTable-Cbq-yJos.js.br +0 -0
- codex/static_root/assets/VTable-Cbq-yJos.js.gz +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css +0 -1
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.br +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.gz +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css +0 -1
- codex/static_root/assets/VTextField-Crc3gdNh.css.br +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css.gz +0 -0
- codex/static_root/assets/VTextField-X5yAw35c.155e276dd747.js +0 -1
- codex/static_root/assets/VTextField-X5yAw35c.155e276dd747.js.br +0 -0
- codex/static_root/assets/VTextField-X5yAw35c.155e276dd747.js.gz +0 -0
- codex/static_root/assets/VTextField-X5yAw35c.js +0 -1
- codex/static_root/assets/VTextField-X5yAw35c.js.br +0 -0
- codex/static_root/assets/VTextField-X5yAw35c.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CRqhVh-9.cd73d01e0591.js +0 -1
- codex/static_root/assets/VWindowItem-CRqhVh-9.cd73d01e0591.js.br +0 -0
- codex/static_root/assets/VWindowItem-CRqhVh-9.cd73d01e0591.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CRqhVh-9.js +0 -1
- codex/static_root/assets/VWindowItem-CRqhVh-9.js.br +0 -0
- codex/static_root/assets/VWindowItem-CRqhVh-9.js.gz +0 -0
- codex/static_root/assets/admin-BFvQwinN.6cfff21f780a.js +0 -1
- codex/static_root/assets/admin-BFvQwinN.6cfff21f780a.js.br +0 -0
- codex/static_root/assets/admin-BFvQwinN.6cfff21f780a.js.gz +0 -0
- codex/static_root/assets/admin-BFvQwinN.js +0 -1
- codex/static_root/assets/admin-BFvQwinN.js.br +0 -0
- codex/static_root/assets/admin-BFvQwinN.js.gz +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.css +0 -1
- codex/static_root/assets/admin-DJ7pIXDQ.css.br +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.css.gz +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css +0 -1
- codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.br +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.gz +0 -0
- codex/static_root/assets/admin-menu-Jfmp_Yd8.39fa7a18b3b4.js +0 -1
- codex/static_root/assets/admin-menu-Jfmp_Yd8.39fa7a18b3b4.js.br +0 -0
- codex/static_root/assets/admin-menu-Jfmp_Yd8.39fa7a18b3b4.js.gz +0 -0
- codex/static_root/assets/admin-menu-Jfmp_Yd8.js +0 -1
- codex/static_root/assets/admin-menu-Jfmp_Yd8.js.br +0 -0
- codex/static_root/assets/admin-menu-Jfmp_Yd8.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.cbf73c71330d.js +0 -1
- codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.cbf73c71330d.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.cbf73c71330d.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.js +0 -1
- codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.js.gz +0 -0
- codex/static_root/assets/browser-CctlSJji.css +0 -1
- codex/static_root/assets/browser-CctlSJji.css.br +0 -0
- codex/static_root/assets/browser-CctlSJji.css.gz +0 -0
- codex/static_root/assets/browser-CctlSJji.d42d796476d2.css +0 -1
- codex/static_root/assets/browser-CctlSJji.d42d796476d2.css.br +0 -0
- codex/static_root/assets/browser-CctlSJji.d42d796476d2.css.gz +0 -0
- codex/static_root/assets/browser-DJnEeVHz.a78c47516b08.js +0 -1
- codex/static_root/assets/browser-DJnEeVHz.a78c47516b08.js.br +0 -0
- codex/static_root/assets/browser-DJnEeVHz.a78c47516b08.js.gz +0 -0
- codex/static_root/assets/browser-DJnEeVHz.js +0 -1
- codex/static_root/assets/browser-DJnEeVHz.js.br +0 -0
- codex/static_root/assets/browser-DJnEeVHz.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DJtOQw7K.be7fa5ec653d.js +0 -1
- codex/static_root/assets/change-password-dialog-DJtOQw7K.be7fa5ec653d.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DJtOQw7K.be7fa5ec653d.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DJtOQw7K.js +0 -1
- codex/static_root/assets/change-password-dialog-DJtOQw7K.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DJtOQw7K.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CME4gKSZ.1606233e784c.js +0 -1
- codex/static_root/assets/confirm-dialog-CME4gKSZ.1606233e784c.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CME4gKSZ.1606233e784c.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CME4gKSZ.js +0 -1
- codex/static_root/assets/confirm-dialog-CME4gKSZ.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CME4gKSZ.js.gz +0 -0
- codex/static_root/assets/datetime-column-D_F30OLW.09cdce0ef96e.js +0 -1
- codex/static_root/assets/datetime-column-D_F30OLW.09cdce0ef96e.js.br +0 -2
- codex/static_root/assets/datetime-column-D_F30OLW.09cdce0ef96e.js.gz +0 -0
- codex/static_root/assets/datetime-column-D_F30OLW.js +0 -1
- codex/static_root/assets/datetime-column-D_F30OLW.js.br +0 -2
- codex/static_root/assets/datetime-column-D_F30OLW.js.gz +0 -0
- codex/static_root/assets/filter-ud5mPZpn.0f0a537959aa.js.br +0 -0
- codex/static_root/assets/filter-ud5mPZpn.0f0a537959aa.js.gz +0 -0
- codex/static_root/assets/filter-ud5mPZpn.js.br +0 -0
- codex/static_root/assets/filter-ud5mPZpn.js.gz +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css +0 -1
- codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.br +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.gz +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.css +0 -1
- codex/static_root/assets/flag-tab-CqWovuBu.css.br +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.css.gz +0 -0
- codex/static_root/assets/flag-tab-k2g6u_vi.3e1da9318af9.js +0 -1
- codex/static_root/assets/flag-tab-k2g6u_vi.3e1da9318af9.js.br +0 -0
- codex/static_root/assets/flag-tab-k2g6u_vi.3e1da9318af9.js.gz +0 -0
- codex/static_root/assets/flag-tab-k2g6u_vi.js +0 -1
- codex/static_root/assets/flag-tab-k2g6u_vi.js.br +0 -0
- codex/static_root/assets/flag-tab-k2g6u_vi.js.gz +0 -0
- codex/static_root/assets/group-tab-C6Ykf03Z.ff0cc204013a.js +0 -1
- codex/static_root/assets/group-tab-C6Ykf03Z.ff0cc204013a.js.br +0 -0
- codex/static_root/assets/group-tab-C6Ykf03Z.ff0cc204013a.js.gz +0 -0
- codex/static_root/assets/group-tab-C6Ykf03Z.js +0 -1
- codex/static_root/assets/group-tab-C6Ykf03Z.js.br +0 -0
- codex/static_root/assets/group-tab-C6Ykf03Z.js.gz +0 -0
- codex/static_root/assets/http-error-Ctcws1xe.cfd378a792d2.css +0 -1
- codex/static_root/assets/http-error-Ctcws1xe.cfd378a792d2.css.br +0 -0
- codex/static_root/assets/http-error-Ctcws1xe.cfd378a792d2.css.gz +0 -0
- codex/static_root/assets/http-error-Ctcws1xe.css +0 -1
- codex/static_root/assets/http-error-Ctcws1xe.css.br +0 -0
- codex/static_root/assets/http-error-Ctcws1xe.css.gz +0 -0
- codex/static_root/assets/http-error-DQBHK8v5.aea50b1327fd.js +0 -1
- codex/static_root/assets/http-error-DQBHK8v5.aea50b1327fd.js.br +0 -0
- codex/static_root/assets/http-error-DQBHK8v5.aea50b1327fd.js.gz +0 -0
- codex/static_root/assets/http-error-DQBHK8v5.js +0 -1
- codex/static_root/assets/http-error-DQBHK8v5.js.br +0 -0
- codex/static_root/assets/http-error-DQBHK8v5.js.gz +0 -0
- codex/static_root/assets/index-ClGiHwWc.0717bacf5135.js +0 -1
- codex/static_root/assets/index-ClGiHwWc.0717bacf5135.js.br +0 -0
- codex/static_root/assets/index-ClGiHwWc.0717bacf5135.js.gz +0 -0
- codex/static_root/assets/index-ClGiHwWc.js +0 -1
- codex/static_root/assets/index-ClGiHwWc.js.br +0 -0
- codex/static_root/assets/index-ClGiHwWc.js.gz +0 -0
- codex/static_root/assets/library-tab-BpinbqAu.53fa2625e727.js +0 -1
- codex/static_root/assets/library-tab-BpinbqAu.53fa2625e727.js.br +0 -0
- codex/static_root/assets/library-tab-BpinbqAu.53fa2625e727.js.gz +0 -0
- codex/static_root/assets/library-tab-BpinbqAu.js +0 -1
- codex/static_root/assets/library-tab-BpinbqAu.js.br +0 -0
- codex/static_root/assets/library-tab-BpinbqAu.js.gz +0 -0
- codex/static_root/assets/library-tab-Cat0MAK5.5911f022188e.css.br +0 -0
- codex/static_root/assets/library-tab-Cat0MAK5.5911f022188e.css.gz +0 -0
- codex/static_root/assets/library-tab-Cat0MAK5.css.br +0 -0
- codex/static_root/assets/library-tab-Cat0MAK5.css.gz +0 -0
- codex/static_root/assets/main-BVKrDunc.1c6f8cdc3afb.js +0 -35
- codex/static_root/assets/main-BVKrDunc.1c6f8cdc3afb.js.br +0 -0
- codex/static_root/assets/main-BVKrDunc.1c6f8cdc3afb.js.gz +0 -0
- codex/static_root/assets/main-BVKrDunc.js +0 -35
- codex/static_root/assets/main-BVKrDunc.js.br +0 -0
- codex/static_root/assets/main-BVKrDunc.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-C1ZSyXfG.f2e95b19621c.js +0 -1
- codex/static_root/assets/pager-full-pdf-C1ZSyXfG.f2e95b19621c.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-C1ZSyXfG.f2e95b19621c.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-C1ZSyXfG.js +0 -1
- codex/static_root/assets/pager-full-pdf-C1ZSyXfG.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-C1ZSyXfG.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Cx_9nh6D.546121fa7a0d.css +0 -1
- codex/static_root/assets/pagination-toolbar-Cx_9nh6D.546121fa7a0d.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-Cx_9nh6D.546121fa7a0d.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Cx_9nh6D.css +0 -1
- codex/static_root/assets/pagination-toolbar-Cx_9nh6D.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-Cx_9nh6D.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Diad0EdD.140911bf86af.js +0 -1
- codex/static_root/assets/pagination-toolbar-Diad0EdD.140911bf86af.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Diad0EdD.140911bf86af.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Diad0EdD.js +0 -1
- codex/static_root/assets/pagination-toolbar-Diad0EdD.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Diad0EdD.js.gz +0 -0
- codex/static_root/assets/pdf-doc-b-GyNR6z.c9d18f0d36ed.js +0 -305
- codex/static_root/assets/pdf-doc-b-GyNR6z.c9d18f0d36ed.js.br +0 -0
- codex/static_root/assets/pdf-doc-b-GyNR6z.c9d18f0d36ed.js.gz +0 -0
- codex/static_root/assets/pdf-doc-b-GyNR6z.js +0 -305
- codex/static_root/assets/pdf-doc-b-GyNR6z.js.br +0 -0
- codex/static_root/assets/pdf-doc-b-GyNR6z.js.gz +0 -0
- codex/static_root/assets/reader-2up6WEuE.c56ad81d14c6.js +0 -2
- codex/static_root/assets/reader-2up6WEuE.c56ad81d14c6.js.br +0 -0
- codex/static_root/assets/reader-2up6WEuE.c56ad81d14c6.js.gz +0 -0
- codex/static_root/assets/reader-2up6WEuE.js +0 -2
- codex/static_root/assets/reader-2up6WEuE.js.br +0 -0
- codex/static_root/assets/reader-2up6WEuE.js.gz +0 -0
- codex/static_root/assets/reader-Dd2KmdCN.115d4bfe9280.css.br +0 -0
- codex/static_root/assets/reader-Dd2KmdCN.115d4bfe9280.css.gz +0 -0
- codex/static_root/assets/reader-Dd2KmdCN.css.br +0 -0
- codex/static_root/assets/reader-Dd2KmdCN.css.gz +0 -0
- codex/static_root/assets/relation-chips-piYv1IcD.f8a2ae79de89.js +0 -1
- codex/static_root/assets/relation-chips-piYv1IcD.f8a2ae79de89.js.br +0 -0
- codex/static_root/assets/relation-chips-piYv1IcD.f8a2ae79de89.js.gz +0 -0
- codex/static_root/assets/relation-chips-piYv1IcD.js +0 -1
- codex/static_root/assets/relation-chips-piYv1IcD.js.br +0 -0
- codex/static_root/assets/relation-chips-piYv1IcD.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css +0 -1
- codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.br +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.gz +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.css +0 -1
- codex/static_root/assets/settings-drawer-DKncO_vX.css.br +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.css.gz +0 -0
- codex/static_root/assets/settings-drawer-yVoY-WCS.1240cc47a246.js +0 -2
- codex/static_root/assets/settings-drawer-yVoY-WCS.1240cc47a246.js.br +0 -0
- codex/static_root/assets/settings-drawer-yVoY-WCS.1240cc47a246.js.gz +0 -0
- codex/static_root/assets/settings-drawer-yVoY-WCS.js +0 -2
- codex/static_root/assets/settings-drawer-yVoY-WCS.js.br +0 -0
- codex/static_root/assets/settings-drawer-yVoY-WCS.js.gz +0 -0
- codex/static_root/assets/stats-tab-bqSPLvTp.05106cb03ef0.js +0 -1
- codex/static_root/assets/stats-tab-bqSPLvTp.05106cb03ef0.js.br +0 -0
- codex/static_root/assets/stats-tab-bqSPLvTp.05106cb03ef0.js.gz +0 -0
- codex/static_root/assets/stats-tab-bqSPLvTp.js +0 -1
- codex/static_root/assets/stats-tab-bqSPLvTp.js.br +0 -0
- codex/static_root/assets/stats-tab-bqSPLvTp.js.gz +0 -0
- codex/static_root/assets/task-tab-DWPKaGPT.da4df28dde5c.js +0 -1
- codex/static_root/assets/task-tab-DWPKaGPT.da4df28dde5c.js.br +0 -0
- codex/static_root/assets/task-tab-DWPKaGPT.da4df28dde5c.js.gz +0 -0
- codex/static_root/assets/task-tab-DWPKaGPT.js +0 -1
- codex/static_root/assets/task-tab-DWPKaGPT.js.br +0 -0
- codex/static_root/assets/task-tab-DWPKaGPT.js.gz +0 -0
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css +0 -1
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.br +0 -4
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.gz +0 -0
- codex/static_root/assets/transition-5LRFTPo5.css +0 -1
- codex/static_root/assets/transition-5LRFTPo5.css.br +0 -4
- codex/static_root/assets/transition-5LRFTPo5.css.gz +0 -0
- codex/static_root/assets/transition-DWVQ2EZ3.0bdfaa37b937.js +0 -1
- codex/static_root/assets/transition-DWVQ2EZ3.0bdfaa37b937.js.br +0 -0
- codex/static_root/assets/transition-DWVQ2EZ3.0bdfaa37b937.js.gz +0 -0
- codex/static_root/assets/transition-DWVQ2EZ3.js +0 -1
- codex/static_root/assets/transition-DWVQ2EZ3.js.br +0 -0
- codex/static_root/assets/transition-DWVQ2EZ3.js.gz +0 -0
- codex/static_root/assets/unauthorized-CEGD20tR.064b0993a944.js +0 -1
- codex/static_root/assets/unauthorized-CEGD20tR.064b0993a944.js.br +0 -0
- codex/static_root/assets/unauthorized-CEGD20tR.064b0993a944.js.gz +0 -0
- codex/static_root/assets/unauthorized-CEGD20tR.js +0 -1
- codex/static_root/assets/unauthorized-CEGD20tR.js.br +0 -0
- codex/static_root/assets/unauthorized-CEGD20tR.js.gz +0 -0
- codex/static_root/assets/unauthorized-DyojW2ic.css +0 -1
- codex/static_root/assets/unauthorized-DyojW2ic.css.br +0 -0
- codex/static_root/assets/unauthorized-DyojW2ic.css.gz +0 -0
- codex/static_root/assets/unauthorized-DyojW2ic.feefa7628a23.css +0 -1
- codex/static_root/assets/unauthorized-DyojW2ic.feefa7628a23.css.br +0 -0
- codex/static_root/assets/unauthorized-DyojW2ic.feefa7628a23.css.gz +0 -0
- codex/static_root/assets/user-tab-EsokzMCB.b0acc46b56a3.js +0 -1
- codex/static_root/assets/user-tab-EsokzMCB.b0acc46b56a3.js.br +0 -0
- codex/static_root/assets/user-tab-EsokzMCB.b0acc46b56a3.js.gz +0 -0
- codex/static_root/assets/user-tab-EsokzMCB.js +0 -1
- codex/static_root/assets/user-tab-EsokzMCB.js.br +0 -0
- codex/static_root/assets/user-tab-EsokzMCB.js.gz +0 -0
- codex/static_root/manifest.4a38de3db1c5.json +0 -712
- codex/static_root/manifest.4a38de3db1c5.json.br +0 -0
- codex/static_root/manifest.4a38de3db1c5.json.gz +0 -0
- {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/LICENSE +0 -0
- {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/WHEEL +0 -0
- {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/entry_points.txt +0 -0
codex/views/browser/order_by.py
CHANGED
|
@@ -19,7 +19,10 @@ class BrowserOrderByView(BrowserGroupMtimeView):
|
|
|
19
19
|
if not self._order_key:
|
|
20
20
|
order_key: str = self.params["order_by"]
|
|
21
21
|
if (order_key == "search_score" and not self.fts_mode) or (
|
|
22
|
-
order_key == "filename"
|
|
22
|
+
order_key == "filename"
|
|
23
|
+
and not self.admin_flags["folder_view"]
|
|
24
|
+
or order_key == "child_count"
|
|
25
|
+
and self.TARGET == "cover"
|
|
23
26
|
):
|
|
24
27
|
order_key = "sort_name"
|
|
25
28
|
self._order_key = order_key
|
|
@@ -29,6 +32,8 @@ class BrowserOrderByView(BrowserGroupMtimeView):
|
|
|
29
32
|
"""Order by for comics (and covers)."""
|
|
30
33
|
if not order_key:
|
|
31
34
|
order_key = self.order_key
|
|
35
|
+
if order_key == "child_count":
|
|
36
|
+
order_key = "sort_name"
|
|
32
37
|
if order_key == "sort_name":
|
|
33
38
|
if not comic_sort_names:
|
|
34
39
|
comic_sort_names = self._comic_sort_names
|
codex/views/frontend.py
CHANGED
|
@@ -6,6 +6,7 @@ from rest_framework.permissions import AllowAny
|
|
|
6
6
|
from rest_framework.renderers import TemplateHTMLRenderer
|
|
7
7
|
from rest_framework.response import Response
|
|
8
8
|
|
|
9
|
+
from codex.models.admin import AdminFlag
|
|
9
10
|
from codex.serializers.route import RouteSerializer
|
|
10
11
|
from codex.views.session import SessionView
|
|
11
12
|
|
|
@@ -19,10 +20,24 @@ class IndexView(SessionView):
|
|
|
19
20
|
renderer_classes: ClassVar[list] = [TemplateHTMLRenderer] # type: ignore
|
|
20
21
|
template_name = "index.html"
|
|
21
22
|
|
|
22
|
-
def
|
|
23
|
-
"""Get the
|
|
23
|
+
def _get_last_route(self):
|
|
24
|
+
"""Get the last route from the session."""
|
|
24
25
|
last_route = self.get_last_route(name=False)
|
|
25
26
|
serializer = RouteSerializer(last_route)
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
return serializer.data
|
|
28
|
+
|
|
29
|
+
def _get_title(self):
|
|
30
|
+
"""Get the title from the banner text."""
|
|
31
|
+
bt_flag = AdminFlag.objects.get(key="BT")
|
|
32
|
+
title = "Codex"
|
|
33
|
+
if bt_flag.value:
|
|
34
|
+
title = bt_flag.value + " - " + title
|
|
35
|
+
return title
|
|
36
|
+
|
|
37
|
+
def get(self, *_args, **_kwargs):
|
|
38
|
+
"""Get the app index page."""
|
|
39
|
+
extra_context = {
|
|
40
|
+
"last_route": self._get_last_route(),
|
|
41
|
+
"title": self._get_title(),
|
|
42
|
+
}
|
|
28
43
|
return Response(extra_context)
|
codex/views/mixins.py
CHANGED
|
@@ -2,40 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
from django.db.models.expressions import F
|
|
4
4
|
|
|
5
|
+
from codex.logger.logging import get_logger
|
|
5
6
|
from codex.models.comic import Comic, Imprint, Volume
|
|
6
7
|
from codex.views.const import GROUP_NAME_MAP
|
|
7
8
|
|
|
9
|
+
LOG = get_logger(__name__)
|
|
8
10
|
_SHOW_GROUPS = tuple(GROUP_NAME_MAP.keys())
|
|
11
|
+
_GROUP_NAME_TARGETS = frozenset({"browser", "opds1", "opds2", "reader"})
|
|
12
|
+
_VARIABLE_SHOW = "pi"
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
class SharedAnnotationsMixin:
|
|
12
16
|
"""Cross view annotation methods."""
|
|
13
17
|
|
|
14
18
|
@staticmethod
|
|
15
|
-
def _get_order_groups(
|
|
19
|
+
def _get_order_groups(parent_group, pks, show):
|
|
16
20
|
"""Annotate sort_name."""
|
|
17
|
-
if
|
|
18
|
-
order_groups = _SHOW_GROUPS
|
|
19
|
-
elif model_group == "c":
|
|
21
|
+
if parent_group == "c":
|
|
20
22
|
order_groups = ()
|
|
21
23
|
else:
|
|
22
24
|
for index, nav_group in enumerate(_SHOW_GROUPS):
|
|
23
|
-
if nav_group
|
|
24
|
-
|
|
25
|
+
if nav_group in _VARIABLE_SHOW and not show.get(nav_group):
|
|
26
|
+
continue
|
|
27
|
+
if nav_group == parent_group:
|
|
28
|
+
watermark = index
|
|
29
|
+
if pks and len(pks) == 1:
|
|
30
|
+
watermark += 1
|
|
31
|
+
order_groups = _SHOW_GROUPS[watermark:]
|
|
25
32
|
break
|
|
26
33
|
else:
|
|
27
34
|
order_groups = _SHOW_GROUPS
|
|
28
35
|
return order_groups
|
|
29
36
|
|
|
30
37
|
@classmethod
|
|
31
|
-
def get_sort_name_annotations(cls, model,
|
|
38
|
+
def get_sort_name_annotations(cls, model, parent_group, pks, show):
|
|
32
39
|
"""Annotate sort names for browser subclasses and reader."""
|
|
33
40
|
sort_name_annotations = {}
|
|
34
41
|
if model is Comic:
|
|
35
|
-
order_groups = cls._get_order_groups(
|
|
42
|
+
order_groups = cls._get_order_groups(parent_group, pks, show)
|
|
36
43
|
for order_group in order_groups:
|
|
37
|
-
if show and not show.get(order_group):
|
|
38
|
-
continue
|
|
39
44
|
group_name = GROUP_NAME_MAP[order_group]
|
|
40
45
|
ann_name = f"{group_name}_sort_name"
|
|
41
46
|
name_field = "name" if group_name == "volume" else "sort_name"
|
|
@@ -50,7 +55,7 @@ class SharedAnnotationsMixin:
|
|
|
50
55
|
"""Annotate name fields by hoisting them up."""
|
|
51
56
|
# Optimized to only lookup what is used on the frontend
|
|
52
57
|
target = cls.TARGET # type: ignore
|
|
53
|
-
if target not in
|
|
58
|
+
if target not in _GROUP_NAME_TARGETS:
|
|
54
59
|
return qs
|
|
55
60
|
group_names = {}
|
|
56
61
|
if qs.model is Comic:
|
|
@@ -69,20 +74,3 @@ class SharedAnnotationsMixin:
|
|
|
69
74
|
elif qs.model is Imprint:
|
|
70
75
|
group_names["publisher_name"] = F("publisher__name")
|
|
71
76
|
return qs.annotate(**group_names)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class BookmarkSearchMixin:
|
|
75
|
-
"""Create Bookmark Search kwargs."""
|
|
76
|
-
|
|
77
|
-
@staticmethod
|
|
78
|
-
def get_bookmark_search_kwargs(auth_filter, comic_filter=None):
|
|
79
|
-
"""Get the search kwargs for a user's authentication state."""
|
|
80
|
-
# search kwargs are relative to the bookmark object.
|
|
81
|
-
search_kwargs = {}
|
|
82
|
-
search_kwargs.update(auth_filter)
|
|
83
|
-
|
|
84
|
-
if comic_filter:
|
|
85
|
-
for key, value in comic_filter.items():
|
|
86
|
-
search_kwargs[f"comic__{key}"] = value
|
|
87
|
-
|
|
88
|
-
return search_kwargs
|
codex/views/opds/const.py
CHANGED
|
@@ -28,6 +28,7 @@ class Rel:
|
|
|
28
28
|
SORT_NEW = "http://opds-spec.org/sort/new"
|
|
29
29
|
POPULAR = "http://opds-spec.org/sort/popular"
|
|
30
30
|
FEATURED = "http://opds-spec.org/featured"
|
|
31
|
+
PROGRESSION = "http://www.cantook.com/api/progression"
|
|
31
32
|
SELF = "self"
|
|
32
33
|
START = "start"
|
|
33
34
|
UP = "up"
|
|
@@ -55,6 +56,7 @@ class MimeType:
|
|
|
55
56
|
STREAM = "image/jpeg"
|
|
56
57
|
OPDS_JSON = "application/opds+json"
|
|
57
58
|
OPDS_PUB = "application/opds-publication+json"
|
|
59
|
+
PROGRESSION = "application/vnd.readium.progression+json"
|
|
58
60
|
BOOK = "http://schema.org/Book"
|
|
59
61
|
# COMIC = "https://schema.org/ComicStory"
|
|
60
62
|
JPEG = "image/jpeg"
|
codex/views/opds/util.py
CHANGED
|
@@ -59,8 +59,8 @@ def get_m2m_objects(pks) -> dict:
|
|
|
59
59
|
for model in OPDS_M2M_MODELS:
|
|
60
60
|
table = model.__name__.lower()
|
|
61
61
|
rel = GroupACLMixin.get_rel_prefix(model)
|
|
62
|
-
comic_filter = {rel + "
|
|
63
|
-
qs = model.objects.filter(**comic_filter).
|
|
62
|
+
comic_filter = {rel + "in": pks}
|
|
63
|
+
qs = model.objects.filter(**comic_filter).only("name").order_by("name")
|
|
64
64
|
cats[table] = qs
|
|
65
65
|
|
|
66
66
|
return cats
|
|
@@ -46,8 +46,7 @@ class OPDS1EntryLinksMixin:
|
|
|
46
46
|
}
|
|
47
47
|
href = reverse("opds:bin:cover", kwargs=kwargs)
|
|
48
48
|
href = update_href_query_params(href, query_params)
|
|
49
|
-
|
|
50
|
-
return OPDS1Link(rel, href, mime_type)
|
|
49
|
+
return OPDS1Link(rel, href, MimeType.WEBP)
|
|
51
50
|
except Exception:
|
|
52
51
|
LOG.exception("create thumb")
|
|
53
52
|
|
codex/views/opds/v2/feed.py
CHANGED
|
@@ -8,7 +8,7 @@ from rest_framework.response import Response
|
|
|
8
8
|
from codex.logger.logging import get_logger
|
|
9
9
|
from codex.models import AdminFlag
|
|
10
10
|
from codex.serializers.browser.settings import OPDSSettingsSerializer
|
|
11
|
-
from codex.serializers.opds.v2 import OPDS2FeedSerializer
|
|
11
|
+
from codex.serializers.opds.v2.feed import OPDS2FeedSerializer
|
|
12
12
|
from codex.views.const import MAX_OBJ_PER_PAGE
|
|
13
13
|
from codex.views.opds.auth import OPDSAuthMixin
|
|
14
14
|
from codex.views.opds.const import BLANK_TITLE
|
|
@@ -20,7 +20,8 @@ from codex.views.opds.v2.const import (
|
|
|
20
20
|
TOP_NAV_GROUP_SECTION_DATA,
|
|
21
21
|
NavigationGroup,
|
|
22
22
|
)
|
|
23
|
-
from codex.views.opds.v2.
|
|
23
|
+
from codex.views.opds.v2.href import HrefData
|
|
24
|
+
from codex.views.opds.v2.links import LinkData
|
|
24
25
|
from codex.views.opds.v2.publications import OPDS2PublicationView
|
|
25
26
|
|
|
26
27
|
LOG = get_logger(__name__)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Href methods for OPDS v2.0 Feed."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from django.urls import reverse
|
|
7
|
+
from rest_framework.views import APIView
|
|
8
|
+
|
|
9
|
+
from codex.views.opds.util import update_href_query_params
|
|
10
|
+
from codex.views.util import pop_name
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class HrefData:
|
|
15
|
+
"""Data for creating hrefs."""
|
|
16
|
+
|
|
17
|
+
kwargs: dict | None = None
|
|
18
|
+
query_params: dict | None = None
|
|
19
|
+
absolute_query_params: bool = False
|
|
20
|
+
url_name: str | None = None
|
|
21
|
+
min_page: int | None = None
|
|
22
|
+
max_page: int | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class OPDS2HrefMixin(APIView, ABC):
|
|
26
|
+
"""Create links method."""
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def num_pages(self) -> int:
|
|
30
|
+
"""Dummy."""
|
|
31
|
+
return 1
|
|
32
|
+
|
|
33
|
+
def _href_page_validate(self, kwargs, data):
|
|
34
|
+
"""Validate the page bounds."""
|
|
35
|
+
min_page = 1 if data.min_page is None else data.min_page
|
|
36
|
+
max_page = self.num_pages if data.max_page is None else data.max_page
|
|
37
|
+
page = int(kwargs["page"])
|
|
38
|
+
return page >= min_page and page <= max_page
|
|
39
|
+
|
|
40
|
+
def _href_update_query_params(self, href, data):
|
|
41
|
+
"""Update the query params."""
|
|
42
|
+
if data.absolute_query_params and data.query_params:
|
|
43
|
+
href = update_href_query_params(href, data.query_params)
|
|
44
|
+
elif hasattr(self, "request"):
|
|
45
|
+
# if request link and not init static links
|
|
46
|
+
href = update_href_query_params(
|
|
47
|
+
href, self.request.GET, new_query_params=data.query_params
|
|
48
|
+
)
|
|
49
|
+
return href
|
|
50
|
+
|
|
51
|
+
def href(self, data):
|
|
52
|
+
"""Create an href."""
|
|
53
|
+
url_name = data.url_name if data.url_name else "opds:v2:feed"
|
|
54
|
+
kwargs = data.kwargs if data.kwargs is not None else self.kwargs
|
|
55
|
+
if "page" in kwargs and not self._href_page_validate(kwargs, data):
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
kwargs = pop_name(kwargs)
|
|
59
|
+
href = reverse(url_name, kwargs=kwargs)
|
|
60
|
+
return self._href_update_query_params(href, data)
|
codex/views/opds/v2/links.py
CHANGED
|
@@ -1,30 +1,17 @@
|
|
|
1
1
|
"""Links methods for OPDS v2.0 Feed."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import Mapping
|
|
3
4
|
from copy import deepcopy
|
|
4
5
|
from dataclasses import dataclass
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from urllib.parse import parse_qsl, urlparse
|
|
7
8
|
|
|
8
9
|
from django.db.models import QuerySet
|
|
9
|
-
from django.urls import reverse
|
|
10
10
|
|
|
11
11
|
from codex.settings.settings import FALSY
|
|
12
12
|
from codex.views.browser.browser import BrowserView
|
|
13
13
|
from codex.views.opds.const import MimeType, Rel
|
|
14
|
-
from codex.views.opds.
|
|
15
|
-
from codex.views.util import pop_name
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@dataclass
|
|
19
|
-
class HrefData:
|
|
20
|
-
"""Data for creating hrefs."""
|
|
21
|
-
|
|
22
|
-
kwargs: dict | None = None
|
|
23
|
-
query_params: dict | None = None
|
|
24
|
-
absolute_query_params: bool = False
|
|
25
|
-
url_name: str | None = None
|
|
26
|
-
min_page: int | None = None
|
|
27
|
-
max_page: int | None = None
|
|
14
|
+
from codex.views.opds.v2.href import HrefData, OPDS2HrefMixin
|
|
28
15
|
|
|
29
16
|
|
|
30
17
|
@dataclass
|
|
@@ -40,9 +27,10 @@ class LinkData:
|
|
|
40
27
|
width: int | None = None
|
|
41
28
|
href: str | None = None
|
|
42
29
|
num_items: int | None = None
|
|
30
|
+
authenticate: Mapping | None = None
|
|
43
31
|
|
|
44
32
|
|
|
45
|
-
class OPDS2LinksView(BrowserView):
|
|
33
|
+
class OPDS2LinksView(OPDS2HrefMixin, BrowserView):
|
|
46
34
|
"""Links methods for OPDS 2.0 Feed."""
|
|
47
35
|
|
|
48
36
|
def __init__(self, *args, **kwargs):
|
|
@@ -70,41 +58,12 @@ class OPDS2LinksView(BrowserView):
|
|
|
70
58
|
self._num_pages = self.group_and_books[2]
|
|
71
59
|
return self._num_pages
|
|
72
60
|
|
|
73
|
-
def _href_page_validate(self, kwargs, data):
|
|
74
|
-
"""Validate the page bounds."""
|
|
75
|
-
min_page = 1 if data.min_page is None else data.min_page
|
|
76
|
-
max_page = self.num_pages if data.max_page is None else data.max_page
|
|
77
|
-
page = int(kwargs["page"])
|
|
78
|
-
return page >= min_page and page <= max_page
|
|
79
|
-
|
|
80
|
-
def _href_update_query_params(self, href, data):
|
|
81
|
-
"""Update the query params."""
|
|
82
|
-
if data.absolute_query_params and data.query_params:
|
|
83
|
-
href = update_href_query_params(href, data.query_params)
|
|
84
|
-
elif hasattr(self, "request"):
|
|
85
|
-
# if request link and not init static links
|
|
86
|
-
href = update_href_query_params(
|
|
87
|
-
href, self.request.GET, new_query_params=data.query_params
|
|
88
|
-
)
|
|
89
|
-
return href
|
|
90
|
-
|
|
91
|
-
def _href(self, data):
|
|
92
|
-
"""Create an href."""
|
|
93
|
-
url_name = data.url_name if data.url_name else "opds:v2:feed"
|
|
94
|
-
kwargs = data.kwargs if data.kwargs is not None else self.kwargs
|
|
95
|
-
if "page" in kwargs and not self._href_page_validate(kwargs, data):
|
|
96
|
-
return None
|
|
97
|
-
|
|
98
|
-
kwargs = pop_name(kwargs)
|
|
99
|
-
href = reverse(url_name, kwargs=kwargs)
|
|
100
|
-
return self._href_update_query_params(href, data)
|
|
101
|
-
|
|
102
61
|
def link(self, data):
|
|
103
62
|
"""Create a link element."""
|
|
104
63
|
if data.href:
|
|
105
64
|
href = data.href
|
|
106
65
|
else:
|
|
107
|
-
href = self.
|
|
66
|
+
href = self.href(data.href_data)
|
|
108
67
|
if not href:
|
|
109
68
|
return None
|
|
110
69
|
mime_type = data.mime_type if data.mime_type else MimeType.OPDS_JSON
|
|
@@ -118,8 +77,15 @@ class OPDS2LinksView(BrowserView):
|
|
|
118
77
|
link["height"] = data.height
|
|
119
78
|
if data.width:
|
|
120
79
|
link["width"] = data.width
|
|
80
|
+
|
|
81
|
+
# Properties
|
|
82
|
+
if data.num_items or data.authenticate:
|
|
83
|
+
link["properties"] = {}
|
|
121
84
|
if data.num_items:
|
|
122
|
-
link["properties"]
|
|
85
|
+
link["properties"]["number_of_items"] = data.num_items
|
|
86
|
+
if data.authenticate:
|
|
87
|
+
link["properties"]["authenticate"] = data.authenticate
|
|
88
|
+
|
|
123
89
|
return link
|
|
124
90
|
|
|
125
91
|
@staticmethod
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""OPDS 2 Progression view."""
|
|
2
|
+
# https://github.com/opds-community/drafts/discussions/67#discussioncomment-6414507
|
|
3
|
+
|
|
4
|
+
from types import MappingProxyType
|
|
5
|
+
|
|
6
|
+
from django.db.models.expressions import F, Value
|
|
7
|
+
from django.db.models.fields import FloatField
|
|
8
|
+
from django.db.models.functions.comparison import Cast, Coalesce, Greatest, Least
|
|
9
|
+
from django.db.models.query_utils import FilteredRelation
|
|
10
|
+
from rest_framework.exceptions import ValidationError
|
|
11
|
+
from rest_framework.response import Response
|
|
12
|
+
|
|
13
|
+
from codex.logger.logging import get_logger
|
|
14
|
+
from codex.serializers.opds.v2.progression import OPDS2ProgressionSerializer
|
|
15
|
+
from codex.util import max_none
|
|
16
|
+
from codex.views.auth import AuthFilterGenericAPIView
|
|
17
|
+
from codex.views.bookmark import BookmarkFilterMixin, BookmarkPageView
|
|
18
|
+
from codex.views.const import GROUP_MODEL_MAP
|
|
19
|
+
from codex.views.opds.v2.href import HrefData, OPDS2HrefMixin
|
|
20
|
+
|
|
21
|
+
_EMPTY_DEVICE = MappingProxyType(
|
|
22
|
+
{
|
|
23
|
+
"id": "",
|
|
24
|
+
"name": "",
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
LOG = get_logger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# This is an independent api requiring a separate get.
|
|
31
|
+
class OPDS2ProgressionView(
|
|
32
|
+
BookmarkFilterMixin, OPDS2HrefMixin, BookmarkPageView, AuthFilterGenericAPIView
|
|
33
|
+
):
|
|
34
|
+
"""OPDS 2 Progression view."""
|
|
35
|
+
|
|
36
|
+
serializer_class = OPDS2ProgressionSerializer
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def modified(self):
|
|
40
|
+
"""Get modified from bookmark."""
|
|
41
|
+
return self._obj.bookmark_updated_at # type: ignore
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def device(self):
|
|
45
|
+
"""Dummy device."""
|
|
46
|
+
# Codex doesn't record this.
|
|
47
|
+
return _EMPTY_DEVICE
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def title(self):
|
|
51
|
+
"""The locator title is the page number."""
|
|
52
|
+
return f"Page {self._obj.page}" # type: ignore
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def _progression_href(self):
|
|
56
|
+
"""Build a Progression HRef."""
|
|
57
|
+
acq_kwargs = {
|
|
58
|
+
"pk": self._obj.pk,
|
|
59
|
+
"page": self._obj.page, # type: ignore
|
|
60
|
+
}
|
|
61
|
+
max_page = max_none(self._obj.page_count - 1, 0) # type: ignore
|
|
62
|
+
data = HrefData(
|
|
63
|
+
acq_kwargs,
|
|
64
|
+
url_name="opds:bin:page",
|
|
65
|
+
min_page=0,
|
|
66
|
+
max_page=max_page, # type: ignore
|
|
67
|
+
# absolute_query_params=True,
|
|
68
|
+
)
|
|
69
|
+
return self.href(data)
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def _locations(self):
|
|
73
|
+
"""Build the Locations object."""
|
|
74
|
+
return {
|
|
75
|
+
"position": self._obj.page, # type: ignore
|
|
76
|
+
"progression": self._obj.progress, # type: ignore
|
|
77
|
+
"total_progression": self._obj.progress, # type: ignore
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def locator(self):
|
|
82
|
+
"""Build the Locator object."""
|
|
83
|
+
return {
|
|
84
|
+
"title": self.title, # See publication.py:103
|
|
85
|
+
"href": self._progression_href,
|
|
86
|
+
"type": "image/jpeg",
|
|
87
|
+
"locations": self._locations,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
def get_object(self):
|
|
91
|
+
"""Build the progression data object."""
|
|
92
|
+
group = self.kwargs.get("group")
|
|
93
|
+
pk = self.kwargs.get("pk")
|
|
94
|
+
|
|
95
|
+
if not pk:
|
|
96
|
+
reason = f"Bad primary key for {group}:{pk}"
|
|
97
|
+
return ValidationError(reason, code="422")
|
|
98
|
+
|
|
99
|
+
model = GROUP_MODEL_MAP.get(group)
|
|
100
|
+
if not model:
|
|
101
|
+
reason = f"No model found for group {group}"
|
|
102
|
+
raise ValidationError(reason, code="422")
|
|
103
|
+
|
|
104
|
+
acl_filter = self.get_group_acl_filter(model, self.request.user)
|
|
105
|
+
qs = model.objects.filter(acl_filter).distinct()
|
|
106
|
+
|
|
107
|
+
bm_rel = self.get_bm_rel(model)
|
|
108
|
+
bm_filter = self.get_my_bookmark_filter(bm_rel)
|
|
109
|
+
progress = Least(
|
|
110
|
+
F("page") / Greatest(Cast(F("page_count"), FloatField()), 1.0), Value(1.0)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
qs = qs.annotate(
|
|
114
|
+
my_bookmark=FilteredRelation("bookmark", condition=bm_filter),
|
|
115
|
+
bookmark_updated_at=F("my_bookmark__updated_at"),
|
|
116
|
+
page=Coalesce(F("my_bookmark__page"), 0),
|
|
117
|
+
progress=progress,
|
|
118
|
+
)
|
|
119
|
+
self._obj = qs.only("page_count").distinct().get(pk=pk)
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
"modified": self.modified,
|
|
123
|
+
"device": self.device,
|
|
124
|
+
"locator": self.locator,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
def get(self, *args, **kwargs):
|
|
128
|
+
"""Get Response."""
|
|
129
|
+
try:
|
|
130
|
+
obj = self.get_object()
|
|
131
|
+
serializer = self.get_serializer(obj)
|
|
132
|
+
return Response(serializer.data)
|
|
133
|
+
except Exception:
|
|
134
|
+
LOG.exception("progression")
|
|
135
|
+
raise
|
|
136
|
+
|
|
137
|
+
# PUT handled by BookmarkPageView parent
|
|
@@ -10,7 +10,9 @@ from codex.models import Comic
|
|
|
10
10
|
from codex.settings.settings import FALSY
|
|
11
11
|
from codex.views.opds.const import AUTHOR_ROLES, MimeType, Rel
|
|
12
12
|
from codex.views.opds.util import get_contributors, get_m2m_objects
|
|
13
|
-
from codex.views.opds.v2.
|
|
13
|
+
from codex.views.opds.v2.href import HrefData
|
|
14
|
+
from codex.views.opds.v2.links import LinkData
|
|
15
|
+
from codex.views.opds.v2.top_links import OPDS2TopLinksView
|
|
14
16
|
|
|
15
17
|
_MD_CONTRIBUTOR_MAP = MappingProxyType(
|
|
16
18
|
{
|
|
@@ -154,11 +156,29 @@ class OPDS2PublicationView(OPDS2TopLinksView):
|
|
|
154
156
|
acq_href_data,
|
|
155
157
|
mime_type=download_mime_type,
|
|
156
158
|
)
|
|
159
|
+
prog_kwargs = {"group": "c", "pk": obj.pk}
|
|
160
|
+
prog_href_data = HrefData(prog_kwargs, url_name="opds:v2:position")
|
|
161
|
+
|
|
162
|
+
auth_href_data = HrefData({}, url_name="opds:authentication:v1")
|
|
163
|
+
auth_link_data = LinkData(
|
|
164
|
+
Rel.AUTHENTICATION,
|
|
165
|
+
auth_href_data,
|
|
166
|
+
mime_type=MimeType.AUTHENTICATION,
|
|
167
|
+
)
|
|
168
|
+
auth_link = self.link(auth_link_data)
|
|
169
|
+
|
|
170
|
+
prog_link_data = LinkData(
|
|
171
|
+
Rel.PROGRESSION,
|
|
172
|
+
prog_href_data,
|
|
173
|
+
mime_type=MimeType.PROGRESSION,
|
|
174
|
+
authenticate=auth_link,
|
|
175
|
+
)
|
|
157
176
|
links = [
|
|
158
177
|
# Comic streaming link (unsupported)
|
|
159
178
|
# self.link(self_link_data),
|
|
160
179
|
self.link(alt_link_data),
|
|
161
180
|
self.link(acq_link_data),
|
|
181
|
+
self.link(prog_link_data),
|
|
162
182
|
]
|
|
163
183
|
pub["links"] = links
|
|
164
184
|
|
codex/views/opds/v2/top_links.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""OPDS v2.0 top links section methods."""
|
|
2
2
|
|
|
3
3
|
from codex.views.opds.const import MimeType, Rel
|
|
4
|
-
from codex.views.opds.v2.
|
|
4
|
+
from codex.views.opds.v2.href import HrefData
|
|
5
|
+
from codex.views.opds.v2.links import LinkData, OPDS2LinksView
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class OPDS2TopLinksView(OPDS2LinksView):
|
codex/views/public.py
CHANGED
|
@@ -13,6 +13,7 @@ _ADMIN_FLAG_KEYS = frozenset(
|
|
|
13
13
|
{
|
|
14
14
|
AdminFlag.FlagChoices.NON_USERS.value,
|
|
15
15
|
AdminFlag.FlagChoices.REGISTRATION.value,
|
|
16
|
+
AdminFlag.FlagChoices.BANNER_TEXT.value,
|
|
16
17
|
}
|
|
17
18
|
)
|
|
18
19
|
|
|
@@ -21,16 +22,22 @@ class AdminFlagsView(GenericAPIView, RetrieveModelMixin):
|
|
|
21
22
|
"""Get admin flags relevant to auth."""
|
|
22
23
|
|
|
23
24
|
serializer_class = AuthAdminFlagsSerializer
|
|
24
|
-
queryset = AdminFlag.objects.filter(key__in=_ADMIN_FLAG_KEYS).only(
|
|
25
|
+
queryset = AdminFlag.objects.filter(key__in=_ADMIN_FLAG_KEYS).only(
|
|
26
|
+
"key", "on", "value"
|
|
27
|
+
)
|
|
25
28
|
|
|
26
29
|
def get_object(self):
|
|
27
30
|
"""Get admin flags."""
|
|
28
31
|
flags = {}
|
|
29
32
|
for obj in self.get_queryset(): # type: ignore
|
|
30
33
|
name = ADMIN_FLAG_CHOICES[obj.key].lower().replace(" ", "_")
|
|
31
|
-
|
|
34
|
+
if obj.key == AdminFlag.FlagChoices.BANNER_TEXT.value:
|
|
35
|
+
val = obj.value
|
|
36
|
+
else:
|
|
37
|
+
val = obj.on
|
|
38
|
+
flags[name] = val
|
|
32
39
|
return flags
|
|
33
40
|
|
|
34
|
-
def get(self,
|
|
41
|
+
def get(self, *args, **kwargs):
|
|
35
42
|
"""Get admin flags relevant to auth."""
|
|
36
|
-
return self.retrieve(
|
|
43
|
+
return self.retrieve(*args, **kwargs)
|
codex/views/reader/books.py
CHANGED
|
@@ -6,7 +6,7 @@ from django.db.models import F
|
|
|
6
6
|
from django.db.models.query import Q
|
|
7
7
|
|
|
8
8
|
from codex.models import Bookmark, Comic
|
|
9
|
-
from codex.views.bookmark import
|
|
9
|
+
from codex.views.bookmark import BookmarkAuthMixin
|
|
10
10
|
from codex.views.browser.filters.field import ComicFieldFilterView
|
|
11
11
|
from codex.views.const import (
|
|
12
12
|
FOLDER_GROUP,
|
|
@@ -15,7 +15,7 @@ from codex.views.const import (
|
|
|
15
15
|
NONE_INTEGERFIELD,
|
|
16
16
|
STORY_ARC_GROUP,
|
|
17
17
|
)
|
|
18
|
-
from codex.views.mixins import
|
|
18
|
+
from codex.views.mixins import SharedAnnotationsMixin
|
|
19
19
|
from codex.views.reader.params import ReaderParamsView
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
@@ -34,9 +34,7 @@ _COMIC_FIELDS = (
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
class ReaderBooksView(
|
|
38
|
-
BookmarkBaseView, ReaderParamsView, SharedAnnotationsMixin, BookmarkSearchMixin
|
|
39
|
-
):
|
|
37
|
+
class ReaderBooksView(SharedAnnotationsMixin, BookmarkAuthMixin, ReaderParamsView):
|
|
40
38
|
"""Get Books methods."""
|
|
41
39
|
|
|
42
40
|
def _get_reader_arc_pks(
|
|
@@ -81,10 +79,10 @@ class ReaderBooksView(
|
|
|
81
79
|
"""Get ordering for query."""
|
|
82
80
|
sort_name_annotations = {}
|
|
83
81
|
if arc_group in ("v", "s"):
|
|
82
|
+
parent_group = "i" if arc_group == "s" else "s"
|
|
84
83
|
show = self.params["show"]
|
|
85
|
-
model_group = "i" if arc_group == "s" else "s"
|
|
86
84
|
sort_name_annotations = self.get_sort_name_annotations(
|
|
87
|
-
model,
|
|
85
|
+
model, parent_group, arc_pks, show
|
|
88
86
|
)
|
|
89
87
|
if sort_name_annotations and model is Comic:
|
|
90
88
|
ordering += (*sort_name_annotations.keys(),)
|
|
@@ -149,6 +147,19 @@ class ReaderBooksView(
|
|
|
149
147
|
qs = qs.order_by(*ordering)
|
|
150
148
|
return qs, arc_group
|
|
151
149
|
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _get_bookmark_filter(auth_filter, comic_filter=None):
|
|
152
|
+
"""Get the search kwargs for a user's authentication state."""
|
|
153
|
+
# search kwargs are relative to the bookmark object.
|
|
154
|
+
search_kwargs = {}
|
|
155
|
+
search_kwargs.update(auth_filter)
|
|
156
|
+
|
|
157
|
+
if comic_filter:
|
|
158
|
+
for key, value in comic_filter.items():
|
|
159
|
+
search_kwargs[f"comic__{key}"] = value
|
|
160
|
+
|
|
161
|
+
return search_kwargs
|
|
162
|
+
|
|
152
163
|
def _append_with_settings(self, book, bookmark_filter):
|
|
153
164
|
"""Append bookmark to book list."""
|
|
154
165
|
book.settings = (
|
|
@@ -169,7 +180,7 @@ class ReaderBooksView(
|
|
|
169
180
|
"""
|
|
170
181
|
comics, arc_group = self._get_comics_list()
|
|
171
182
|
auth_filter = self.get_bookmark_auth_filter()
|
|
172
|
-
bookmark_filter = self.
|
|
183
|
+
bookmark_filter = self._get_bookmark_filter(auth_filter)
|
|
173
184
|
books = {}
|
|
174
185
|
prev_book = None
|
|
175
186
|
pk = self.kwargs.get("pk")
|