codex 1.6.19__py3-none-any.whl → 1.7.0a0__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/db.py +119 -0
- codex/librarian/covers/purge.py +1 -1
- codex/librarian/importer/const.py +7 -1
- codex/librarian/importer/create_fks.py +5 -2
- codex/librarian/importer/importerd.py +17 -12
- codex/librarian/importer/moved.py +44 -13
- codex/librarian/importer/tasks.py +2 -0
- codex/librarian/janitor/cleanup.py +21 -8
- codex/librarian/janitor/integrity.py +369 -0
- codex/librarian/janitor/janitor.py +50 -15
- codex/librarian/janitor/latest_version.py +2 -1
- codex/librarian/janitor/status.py +5 -0
- codex/librarian/janitor/tasks.py +37 -0
- codex/librarian/janitor/update.py +1 -1
- codex/librarian/librariand.py +32 -24
- codex/librarian/search/optimize.py +43 -0
- codex/librarian/search/remove.py +18 -46
- codex/librarian/search/searchd.py +14 -17
- codex/librarian/search/status.py +2 -1
- codex/librarian/search/tasks.py +4 -9
- codex/librarian/search/update.py +223 -301
- codex/librarian/watchdog/observers.py +2 -1
- codex/migrations/0029_comicfts.py +58 -0
- codex/migrations/0030_nocase_collation_day_month_indexes_status_types.py +198 -0
- codex/models/admin.py +15 -6
- codex/models/base.py +9 -0
- codex/models/bookmark.py +3 -3
- codex/models/comic.py +68 -16
- codex/models/functions.py +59 -2
- codex/models/groups.py +6 -1
- codex/models/library.py +1 -3
- codex/models/paths.py +9 -3
- codex/serializers/browser/page.py +2 -0
- codex/settings/settings.py +16 -17
- codex/signals/django_signals.py +4 -10
- codex/startup.py +1 -35
- codex/static_root/assets/{VCheckbox-Cko1sQK-.170726b3b8a1.js → VCheckbox-CWiqcZ7a.3feebd47daab.js} +1 -1
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.gz +0 -0
- codex/static_root/assets/{VCheckbox-Cko1sQK-.js → VCheckbox-CWiqcZ7a.js} +1 -1
- codex/static_root/assets/VCheckbox-CWiqcZ7a.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js → VCheckboxBtn-Dk3iB62X.bc66d5351baa.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CO3-aqQL.js → VCheckboxBtn-Dk3iB62X.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.gz +0 -0
- codex/static_root/assets/{VCombobox-Bjp6lffE.js → VCombobox-D1_KU27A.b1d30587a973.js} +1 -1
- codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.gz +0 -0
- codex/static_root/assets/{VCombobox-Bjp6lffE.caf59fba486e.js → VCombobox-D1_KU27A.js} +1 -1
- codex/static_root/assets/VCombobox-D1_KU27A.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.js.gz +0 -0
- codex/static_root/assets/{VDialog-BkpVGB70.6eea3ca762a5.js → VDialog-GbxvFMMN.594fb3a39ced.js} +1 -1
- codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.br +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.gz +0 -0
- codex/static_root/assets/{VDialog-BkpVGB70.js → VDialog-GbxvFMMN.js} +1 -1
- codex/static_root/assets/VDialog-GbxvFMMN.js.br +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js → VExpansionPanels-Cywv_bEj.a762e63858e5.js} +1 -1
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-Ci2-j8XX.js → VExpansionPanels-Cywv_bEj.js} +1 -1
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-D3Py5BfQ.a4f25edb86d3.js → VRadioGroup-C0etmxpZ.0eb3009a6300.js} +1 -1
- codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-D3Py5BfQ.js → VRadioGroup-C0etmxpZ.js} +1 -1
- codex/static_root/assets/VRadioGroup-C0etmxpZ.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.js.gz +0 -0
- codex/static_root/assets/{VSelect-DtvZsYZz.e98745d858eb.js → VSelect-txrnRiNJ.a5625760018c.js} +1 -1
- codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.br +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.gz +0 -0
- codex/static_root/assets/{VSelect-DtvZsYZz.js → VSelect-txrnRiNJ.js} +1 -1
- codex/static_root/assets/VSelect-txrnRiNJ.js.br +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-D6kmykQW.1e1fda62ceba.js → VSelectionControl-CY6RmcvW.7e8824224f6c.js} +1 -1
- codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-D6kmykQW.js → VSelectionControl-CY6RmcvW.js} +1 -1
- codex/static_root/assets/VSelectionControl-CY6RmcvW.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-UGZWxpF5.360bdefcd215.js → VSlideGroup-DtEQMwY1.6c72325635ed.js} +1 -1
- codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-UGZWxpF5.js → VSlideGroup-DtEQMwY1.js} +1 -1
- codex/static_root/assets/VSlideGroup-DtEQMwY1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.js.gz +0 -0
- codex/static_root/assets/{VTable-DXH_yQ0o.1149f56ad566.js → VTable-2af995xo.6c5bf36631b2.js} +1 -1
- codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.br +0 -0
- codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.gz +0 -0
- codex/static_root/assets/{VTable-DXH_yQ0o.js → VTable-2af995xo.js} +1 -1
- codex/static_root/assets/VTable-2af995xo.js.br +0 -0
- codex/static_root/assets/VTable-2af995xo.js.gz +0 -0
- codex/static_root/assets/{VTextField-Blqy56_L.26d8761e0155.js → VTextField-G6CMj7yO.657a130dd302.js} +1 -1
- codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.br +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.gz +0 -0
- codex/static_root/assets/{VTextField-Blqy56_L.js → VTextField-G6CMj7yO.js} +1 -1
- codex/static_root/assets/VTextField-G6CMj7yO.js.br +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-C8_ovV7e.afbc08414765.js → VWindowItem-CzoGd6Ul.721e5da11ad4.js} +1 -1
- codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.br +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-C8_ovV7e.js → VWindowItem-CzoGd6Ul.js} +1 -1
- codex/static_root/assets/VWindowItem-CzoGd6Ul.js.br +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.js.gz +0 -0
- codex/static_root/assets/{admin-DTRn8bBs.d031ea42d8d0.js → admin-CsPAIQeG.5175467148a3.js} +1 -1
- codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.br +0 -0
- codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.gz +0 -0
- codex/static_root/assets/{admin-DTRn8bBs.js → admin-CsPAIQeG.js} +1 -1
- codex/static_root/assets/admin-CsPAIQeG.js.br +0 -0
- codex/static_root/assets/admin-CsPAIQeG.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BaXFOcru.b1a44b05f482.js → admin-drawer-panel-BAT7GA64.73a064628c19.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BaXFOcru.js → admin-drawer-panel-BAT7GA64.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.gz +0 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js +1 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js.br +0 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js.gz +0 -0
- codex/static_root/assets/browser-C18l93yc.js +1 -0
- codex/static_root/assets/browser-C18l93yc.js.br +0 -0
- codex/static_root/assets/browser-C18l93yc.js.gz +0 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css +1 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.br +0 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.gz +0 -0
- codex/static_root/assets/browser-DaAqlBYO.css +1 -0
- codex/static_root/assets/browser-DaAqlBYO.css.br +0 -0
- codex/static_root/assets/browser-DaAqlBYO.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js +1 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js +1 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css +1 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css +1 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.gz +0 -0
- codex/static_root/assets/{confirm-dialog-D3_s2DQy.9dde66036c72.js → confirm-dialog-CbW3hlOp.9dd39b443c31.js} +1 -1
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-D3_s2DQy.js → confirm-dialog-CbW3hlOp.js} +1 -1
- codex/static_root/assets/confirm-dialog-CbW3hlOp.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.js.gz +0 -0
- codex/static_root/assets/{datetime-column-BxC1Li9e.3dc314b3295d.js → datetime-column-uSqKZU37.b157b0743db5.js} +1 -1
- codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.br +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.gz +0 -0
- codex/static_root/assets/{datetime-column-BxC1Li9e.js → datetime-column-uSqKZU37.js} +1 -1
- codex/static_root/assets/datetime-column-uSqKZU37.js.br +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.js.gz +0 -0
- codex/static_root/assets/{filter-Bzk7x841.d0c3b602e62d.js → filter-CBl5KEsB.845a81573c2e.js} +1 -1
- codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.br +0 -0
- codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.gz +0 -0
- codex/static_root/assets/{filter-Bzk7x841.js → filter-CBl5KEsB.js} +1 -1
- codex/static_root/assets/filter-CBl5KEsB.js.br +0 -0
- codex/static_root/assets/filter-CBl5KEsB.js.gz +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js +1 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.br +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.gz +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js +1 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js.br +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js.gz +0 -0
- codex/static_root/assets/{group-tab-5esH4lwX.a81fdd258117.js → group-tab-BOW9kigw.7e5007596367.js} +1 -1
- codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.br +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.gz +0 -0
- codex/static_root/assets/{group-tab-5esH4lwX.js → group-tab-BOW9kigw.js} +1 -1
- codex/static_root/assets/group-tab-BOW9kigw.js.br +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.js.gz +0 -0
- codex/static_root/assets/{http-error-BTOBfcGY.52ced6f28420.js → http-error-C6yuLApo.45cf99eb7a70.js} +1 -1
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.br +4 -0
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.gz +0 -0
- codex/static_root/assets/{http-error-BTOBfcGY.js → http-error-C6yuLApo.js} +1 -1
- codex/static_root/assets/http-error-C6yuLApo.js.br +4 -0
- codex/static_root/assets/http-error-C6yuLApo.js.gz +0 -0
- codex/static_root/assets/{library-tab-DiPEdOF7.ece2d642a9dc.js → library-tab-D3WOtpE0.9af34e6e7235.js} +1 -1
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.br +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.gz +0 -0
- codex/static_root/assets/{library-tab-DiPEdOF7.js → library-tab-D3WOtpE0.js} +1 -1
- codex/static_root/assets/library-tab-D3WOtpE0.js.br +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.js.gz +0 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js +6 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.br +0 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.gz +0 -0
- codex/static_root/assets/main-CmjU2oTp.js +6 -0
- codex/static_root/assets/main-CmjU2oTp.js.br +0 -0
- codex/static_root/assets/main-CmjU2oTp.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js +1 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js +1 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-C45-onwe.4722195bb60b.css → pagination-toolbar-DGrxp2OD.css} +1 -1
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.css.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-C45-onwe.css → pagination-toolbar-DGrxp2OD.f960a624f04f.css} +1 -1
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.f960a624f04f.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.f960a624f04f.css.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-CkIzuyx1.00c354a82e6e.js → pagination-toolbar-JH3Kbffr.451171f7ac33.js} +1 -1
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-CkIzuyx1.js → pagination-toolbar-JH3Kbffr.js} +1 -1
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BjlRPuf4.0f056fa321b6.js → pdf-doc-BcRNpoaW.882f6e967869.js} +1 -1
- codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.br +0 -0
- codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BjlRPuf4.js → pdf-doc-BcRNpoaW.js} +1 -1
- codex/static_root/assets/pdf-doc-BcRNpoaW.js.br +0 -0
- codex/static_root/assets/pdf-doc-BcRNpoaW.js.gz +0 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js +2 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.br +0 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.gz +0 -0
- codex/static_root/assets/reader-DIPRg2VB.js +2 -0
- codex/static_root/assets/reader-DIPRg2VB.js.br +0 -0
- codex/static_root/assets/reader-DIPRg2VB.js.gz +0 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css +1 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.br +0 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.gz +0 -0
- codex/static_root/assets/reader-DveZZr9x.css +1 -0
- codex/static_root/assets/reader-DveZZr9x.css.br +0 -0
- codex/static_root/assets/reader-DveZZr9x.css.gz +0 -0
- codex/static_root/assets/{relation-chips-CUSatZET.js → relation-chips-3tTlR5QH.71f18bc92adc.js} +1 -1
- codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.br +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.gz +0 -0
- codex/static_root/assets/{relation-chips-CUSatZET.a58031ff8141.js → relation-chips-3tTlR5QH.js} +1 -1
- codex/static_root/assets/relation-chips-3tTlR5QH.js.br +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-BSHTAsJ9.js → settings-drawer-BdyFhA_r.151f4c3061ed.js} +2 -2
- codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.br +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-BSHTAsJ9.1d07ce084fc9.js → settings-drawer-BdyFhA_r.js} +2 -2
- codex/static_root/assets/settings-drawer-BdyFhA_r.js.br +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.js.gz +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js +1 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.br +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.gz +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js +1 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js.br +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js.gz +0 -0
- codex/static_root/assets/{stats-tab-BXGisn5N.9b7a71e7fe28.css → stats-tab-DpfGsVX3.1cb66f2649a9.css} +1 -1
- codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.br +0 -0
- codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.gz +0 -0
- codex/static_root/assets/{stats-tab-BXGisn5N.css → stats-tab-DpfGsVX3.css} +1 -1
- codex/static_root/assets/stats-tab-DpfGsVX3.css.br +0 -0
- codex/static_root/assets/stats-tab-DpfGsVX3.css.gz +0 -0
- codex/static_root/assets/{task-tab-NORFAa9p.97e07e366ddd.js → task-tab-C8D0qRP_.d4ab174c69a3.js} +1 -1
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.br +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.gz +0 -0
- codex/static_root/assets/{task-tab-NORFAa9p.js → task-tab-C8D0qRP_.js} +1 -1
- codex/static_root/assets/task-tab-C8D0qRP_.js.br +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.js.gz +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js +1 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.br +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.gz +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js +1 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js.br +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js.gz +0 -0
- codex/static_root/assets/{unauthorized-muDsv-rC.css → unauthorized-BjhIc97q.728511c92cd0.css} +1 -1
- codex/static_root/assets/unauthorized-BjhIc97q.728511c92cd0.css.br +0 -0
- codex/static_root/assets/unauthorized-BjhIc97q.728511c92cd0.css.gz +0 -0
- codex/static_root/assets/{unauthorized-muDsv-rC.2166ec8b3a32.css → unauthorized-BjhIc97q.css} +1 -1
- codex/static_root/assets/unauthorized-BjhIc97q.css.br +0 -0
- codex/static_root/assets/unauthorized-BjhIc97q.css.gz +0 -0
- codex/static_root/assets/{user-tab-CHbyN1EW.e7206db08680.js → user-tab-BU31Z92y.35613c713faf.js} +1 -1
- codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.br +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.gz +0 -0
- codex/static_root/assets/{user-tab-CHbyN1EW.js → user-tab-BU31Z92y.js} +1 -1
- codex/static_root/assets/user-tab-BU31Z92y.js.br +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.js.gz +0 -0
- codex/static_root/js/choices-admin.8aa64c911203.json +1 -0
- codex/static_root/js/choices-admin.8aa64c911203.json.br +0 -0
- codex/static_root/js/choices-admin.8aa64c911203.json.gz +0 -0
- codex/static_root/js/choices-admin.json +1 -1
- codex/static_root/js/choices-admin.json.br +0 -0
- codex/static_root/js/choices-admin.json.gz +0 -0
- codex/static_root/manifest.45cb46e19686.json +635 -0
- codex/static_root/manifest.45cb46e19686.json.br +0 -0
- codex/static_root/manifest.45cb46e19686.json.gz +0 -0
- codex/static_root/manifest.json +248 -248
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/status_controller.py +10 -4
- codex/views/admin/tasks.py +46 -40
- codex/views/auth.py +15 -4
- codex/views/browser/annotations.py +94 -51
- codex/views/browser/base.py +1 -2
- codex/views/browser/browser.py +52 -21
- codex/views/browser/cover.py +8 -4
- codex/views/browser/filters/annotations.py +28 -6
- codex/views/browser/filters/search/__init__.py +1 -0
- codex/views/browser/filters/search/aliases.py +81 -0
- codex/views/browser/filters/search/field/__init__.py +1 -0
- codex/views/browser/filters/search/field/column.py +53 -0
- codex/views/browser/filters/search/field/expression.py +177 -0
- codex/views/browser/filters/search/field/filter.py +70 -0
- codex/views/browser/filters/search/field/optimize.py +71 -0
- codex/views/browser/filters/search/field/parse.py +187 -0
- codex/views/browser/filters/search/fts.py +38 -0
- codex/views/browser/filters/search/parse.py +220 -0
- codex/views/browser/order_by.py +8 -2
- codex/views/browser/page_in_bounds.py +88 -0
- codex/views/browser/paginate.py +28 -113
- codex/views/const.py +0 -1
- codex/views/opds/v1/feed.py +2 -1
- codex/views/opds/v2/feed.py +2 -1
- codex/views/opds/v2/links.py +1 -1
- codex/views/reader/books.py +1 -1
- codex/views/reader/page.py +3 -3
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/METADATA +18 -10
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/RECORD +321 -372
- codex/_vendor/__init__.py +0 -0
- codex/_vendor/django_haystack-3.2.1.dist-info/AUTHORS +0 -127
- codex/_vendor/django_haystack-3.2.1.dist-info/INSTALLER +0 -1
- codex/_vendor/django_haystack-3.2.1.dist-info/LICENSE +0 -31
- codex/_vendor/django_haystack-3.2.1.dist-info/METADATA +0 -98
- codex/_vendor/django_haystack-3.2.1.dist-info/RECORD +0 -97
- codex/_vendor/django_haystack-3.2.1.dist-info/REQUESTED +0 -0
- codex/_vendor/django_haystack-3.2.1.dist-info/WHEEL +0 -5
- codex/_vendor/django_haystack-3.2.1.dist-info/top_level.txt +0 -1
- codex/_vendor/haystack/__init__.py +0 -80
- codex/_vendor/haystack/admin.py +0 -163
- codex/_vendor/haystack/apps.py +0 -31
- codex/_vendor/haystack/backends/__init__.py +0 -1109
- codex/_vendor/haystack/backends/elasticsearch2_backend.py +0 -390
- codex/_vendor/haystack/backends/elasticsearch5_backend.py +0 -483
- codex/_vendor/haystack/backends/elasticsearch7_backend.py +0 -586
- codex/_vendor/haystack/backends/elasticsearch_backend.py +0 -1141
- codex/_vendor/haystack/backends/simple_backend.py +0 -132
- codex/_vendor/haystack/backends/solr_backend.py +0 -991
- codex/_vendor/haystack/backends/whoosh_backend.py +0 -1104
- codex/_vendor/haystack/constants.py +0 -57
- codex/_vendor/haystack/exceptions.py +0 -57
- codex/_vendor/haystack/fields.py +0 -562
- codex/_vendor/haystack/forms.py +0 -135
- codex/_vendor/haystack/generic_views.py +0 -141
- codex/_vendor/haystack/indexes.py +0 -548
- codex/_vendor/haystack/inputs.py +0 -169
- codex/_vendor/haystack/management/__init__.py +0 -0
- codex/_vendor/haystack/management/commands/__init__.py +0 -0
- codex/_vendor/haystack/management/commands/build_solr_schema.py +0 -187
- codex/_vendor/haystack/management/commands/clear_index.py +0 -67
- codex/_vendor/haystack/management/commands/haystack_info.py +0 -22
- codex/_vendor/haystack/management/commands/rebuild_index.py +0 -65
- codex/_vendor/haystack/management/commands/update_index.py +0 -438
- codex/_vendor/haystack/manager.py +0 -105
- codex/_vendor/haystack/models.py +0 -257
- codex/_vendor/haystack/panels.py +0 -89
- codex/_vendor/haystack/query.py +0 -771
- codex/_vendor/haystack/routers.py +0 -14
- codex/_vendor/haystack/signals.py +0 -88
- codex/_vendor/haystack/templates/panels/haystack.html +0 -33
- codex/_vendor/haystack/templates/search_configuration/schema.xml +0 -1056
- codex/_vendor/haystack/templates/search_configuration/solrconfig.xml +0 -1446
- codex/_vendor/haystack/templatetags/__init__.py +0 -0
- codex/_vendor/haystack/templatetags/highlight.py +0 -131
- codex/_vendor/haystack/templatetags/more_like_this.py +0 -119
- codex/_vendor/haystack/urls.py +0 -5
- codex/_vendor/haystack/utils/__init__.py +0 -84
- codex/_vendor/haystack/utils/app_loading.py +0 -34
- codex/_vendor/haystack/utils/geo.py +0 -71
- codex/_vendor/haystack/utils/highlighting.py +0 -165
- codex/_vendor/haystack/utils/loading.py +0 -374
- codex/_vendor/haystack/utils/log.py +0 -21
- codex/_vendor/haystack/version.py +0 -16
- codex/_vendor/haystack/views.py +0 -253
- codex/integrity.py +0 -492
- codex/librarian/search/merge.py +0 -86
- codex/librarian/search/version.py +0 -63
- codex/search/__init__.py +0 -1
- codex/search/backend.py +0 -524
- codex/search/backend_search.py +0 -229
- codex/search/engine.py +0 -33
- codex/search/indexes.py +0 -79
- codex/search/query.py +0 -67
- codex/search/writing.py +0 -180
- codex/static_root/assets/VCheckbox-Cko1sQK-.170726b3b8a1.js.br +0 -0
- codex/static_root/assets/VCheckbox-Cko1sQK-.170726b3b8a1.js.gz +0 -0
- codex/static_root/assets/VCheckbox-Cko1sQK-.js.br +0 -0
- codex/static_root/assets/VCheckbox-Cko1sQK-.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.caf59fba486e.js.br +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.caf59fba486e.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.js.br +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.js.gz +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.6eea3ca762a5.js.br +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.6eea3ca762a5.js.gz +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.js.br +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.a4f25edb86d3.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.a4f25edb86d3.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.js.gz +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.e98745d858eb.js.br +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.e98745d858eb.js.gz +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.js.br +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.1e1fda62ceba.js.br +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.1e1fda62ceba.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.js.br +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.360bdefcd215.js.br +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.360bdefcd215.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.js.br +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.js.gz +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.1149f56ad566.js.br +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.1149f56ad566.js.gz +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.js.br +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.js.gz +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.26d8761e0155.js.br +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.26d8761e0155.js.gz +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.js.br +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.afbc08414765.js.br +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.afbc08414765.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.js.br +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.js.gz +0 -0
- codex/static_root/assets/admin-DTRn8bBs.d031ea42d8d0.js.br +0 -0
- codex/static_root/assets/admin-DTRn8bBs.d031ea42d8d0.js.gz +0 -0
- codex/static_root/assets/admin-DTRn8bBs.js.br +0 -0
- codex/static_root/assets/admin-DTRn8bBs.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.b1a44b05f482.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.b1a44b05f482.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.js.gz +0 -0
- codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js +0 -1
- codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js.br +0 -0
- codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js.gz +0 -0
- codex/static_root/assets/browser-BBpUyGmz.js +0 -1
- codex/static_root/assets/browser-BBpUyGmz.js.br +0 -0
- codex/static_root/assets/browser-BBpUyGmz.js.gz +0 -0
- codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css +0 -1
- codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.br +0 -0
- codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.gz +0 -0
- codex/static_root/assets/browser-Binc3n9H.css +0 -1
- codex/static_root/assets/browser-Binc3n9H.css.br +0 -0
- codex/static_root/assets/browser-Binc3n9H.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css +0 -1
- codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.css +0 -1
- codex/static_root/assets/change-password-dialog-Cebek1-c.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js +0 -1
- codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DlHoLhDF.js +0 -1
- codex/static_root/assets/change-password-dialog-DlHoLhDF.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DlHoLhDF.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.9dde66036c72.js.br +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.9dde66036c72.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.js.br +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.js.gz +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.3dc314b3295d.js.br +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.3dc314b3295d.js.gz +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.js.br +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.js.gz +0 -0
- codex/static_root/assets/filter-Bzk7x841.d0c3b602e62d.js.br +0 -0
- codex/static_root/assets/filter-Bzk7x841.d0c3b602e62d.js.gz +0 -0
- codex/static_root/assets/filter-Bzk7x841.js.br +0 -0
- codex/static_root/assets/filter-Bzk7x841.js.gz +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js +0 -1
- codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js.br +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js.gz +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.js +0 -1
- codex/static_root/assets/flag-tab-C2bI5zq3.js.br +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.js.gz +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.a81fdd258117.js.br +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.a81fdd258117.js.gz +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.js.br +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.js.gz +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.52ced6f28420.js.br +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.52ced6f28420.js.gz +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.js.br +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.js.gz +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.ece2d642a9dc.js.br +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.ece2d642a9dc.js.gz +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.js.br +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.js.gz +0 -0
- codex/static_root/assets/main-BPIPjzsc.66def8991e38.js +0 -6
- codex/static_root/assets/main-BPIPjzsc.66def8991e38.js.br +0 -0
- codex/static_root/assets/main-BPIPjzsc.66def8991e38.js.gz +0 -0
- codex/static_root/assets/main-BPIPjzsc.js +0 -6
- codex/static_root/assets/main-BPIPjzsc.js.br +0 -0
- codex/static_root/assets/main-BPIPjzsc.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js +0 -1
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.js +0 -1
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.00c354a82e6e.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.00c354a82e6e.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.0f056fa321b6.js.br +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.0f056fa321b6.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.js.br +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.js.gz +0 -0
- codex/static_root/assets/reader-_stAWL-4.5089e9144795.css +0 -1
- codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.br +0 -0
- codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.gz +0 -0
- codex/static_root/assets/reader-_stAWL-4.css +0 -1
- codex/static_root/assets/reader-_stAWL-4.css.br +0 -0
- codex/static_root/assets/reader-_stAWL-4.css.gz +0 -0
- codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js +0 -2
- codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js.br +0 -0
- codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js.gz +0 -0
- codex/static_root/assets/reader-ucxnLi2K.js +0 -2
- codex/static_root/assets/reader-ucxnLi2K.js.br +0 -0
- codex/static_root/assets/reader-ucxnLi2K.js.gz +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.a58031ff8141.js.br +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.a58031ff8141.js.gz +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.js.br +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.1d07ce084fc9.js.br +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.1d07ce084fc9.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.js.br +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.js.gz +0 -0
- codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.br +0 -0
- codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.gz +0 -0
- codex/static_root/assets/stats-tab-BXGisn5N.css.br +0 -0
- codex/static_root/assets/stats-tab-BXGisn5N.css.gz +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js +0 -1
- codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js.br +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js.gz +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.js +0 -1
- codex/static_root/assets/stats-tab-DKXVB2Cc.js.br +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.js.gz +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.97e07e366ddd.js.br +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.97e07e366ddd.js.gz +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.js.br +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.js.gz +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js +0 -1
- codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js.br +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js.gz +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.js +0 -1
- codex/static_root/assets/unauthorized-RHaQZRfr.js.br +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.js.gz +0 -0
- codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.br +0 -0
- codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.gz +0 -0
- codex/static_root/assets/unauthorized-muDsv-rC.css.br +0 -0
- codex/static_root/assets/unauthorized-muDsv-rC.css.gz +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.e7206db08680.js.br +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.e7206db08680.js.gz +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.js.br +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.js.gz +0 -0
- codex/static_root/js/choices-admin.1a20a5648f20.json +0 -1
- codex/static_root/js/choices-admin.1a20a5648f20.json.br +0 -0
- codex/static_root/js/choices-admin.1a20a5648f20.json.gz +0 -0
- codex/static_root/manifest.0fe31a6cb99f.json +0 -635
- codex/static_root/manifest.0fe31a6cb99f.json.br +0 -0
- codex/static_root/manifest.0fe31a6cb99f.json.gz +0 -0
- codex/templates/search/indexes/codex/comic_text.txt +0 -47
- codex/views/browser/filters/search.py +0 -196
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/LICENSE +0 -0
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/WHEEL +0 -0
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/entry_points.txt +0 -0
codex/views/browser/paginate.py
CHANGED
|
@@ -9,148 +9,63 @@ from codex.models import (
|
|
|
9
9
|
Comic,
|
|
10
10
|
Folder,
|
|
11
11
|
)
|
|
12
|
-
from codex.views.browser.
|
|
12
|
+
from codex.views.browser.page_in_bounds import BrowserPageInBoundsView
|
|
13
13
|
from codex.views.const import (
|
|
14
14
|
MAX_OBJ_PER_PAGE,
|
|
15
15
|
)
|
|
16
|
-
from codex.views.util import Route
|
|
17
16
|
|
|
18
17
|
LOG = get_logger(__name__)
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
class BrowserPaginateView(
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
def _get_back_one_page_route(self, num_pages):
|
|
25
|
-
"""Get max page if oob or 1."""
|
|
26
|
-
LOG.debug("Redirect back one page.")
|
|
27
|
-
group = self.kwargs.get("group")
|
|
28
|
-
pks = self.kwargs.get("pks")
|
|
29
|
-
page = self.kwargs.get("page", 1)
|
|
30
|
-
new_page = num_pages if num_pages and page > num_pages else 1
|
|
31
|
-
pks = pks if pks else "0"
|
|
32
|
-
return {"group": group, "pks": pks, "page": new_page}
|
|
33
|
-
|
|
34
|
-
def _get_up_breadcrumbs(self):
|
|
35
|
-
"""Walk up the breadcrumbs to get the next level up."""
|
|
36
|
-
breadcrumbs = self.params.get("breadcrumbs", [])
|
|
37
|
-
new_breadcrumbs = []
|
|
38
|
-
level = False
|
|
39
|
-
group = self.kwargs.get("group")
|
|
40
|
-
pks = self.kwargs.get("pks")
|
|
41
|
-
page = self.kwargs.get("page", 1)
|
|
42
|
-
current_route = Route(group=group, pks=pks, page=page)
|
|
43
|
-
for crumb in reversed(breadcrumbs):
|
|
44
|
-
if not level:
|
|
45
|
-
crumb_route = Route(**crumb)
|
|
46
|
-
level = current_route & crumb_route
|
|
47
|
-
continue
|
|
48
|
-
new_breadcrumbs = [crumb, *new_breadcrumbs]
|
|
49
|
-
|
|
50
|
-
if not new_breadcrumbs:
|
|
51
|
-
if group not in ("f", "a"):
|
|
52
|
-
group = "r"
|
|
53
|
-
top_route = {"group": group, "pks": (), "page": 1}
|
|
54
|
-
new_breadcrumbs = [top_route]
|
|
55
|
-
return new_breadcrumbs
|
|
56
|
-
|
|
57
|
-
def _get_up_page_redirect(self):
|
|
58
|
-
"""Walk up the breadcrumbs."""
|
|
59
|
-
try:
|
|
60
|
-
up_breadcrumbs = self._get_up_breadcrumbs()
|
|
61
|
-
route_mask = up_breadcrumbs[-1]
|
|
62
|
-
settings_mask = {"breadcrumbs": up_breadcrumbs}
|
|
63
|
-
LOG.debug("Redirect up a level.")
|
|
64
|
-
except IndexError:
|
|
65
|
-
group = self.kwargs.get("group")
|
|
66
|
-
pks = "0"
|
|
67
|
-
new_page = 1
|
|
68
|
-
route_mask = {"group": group, "pks": pks, "page": new_page}
|
|
69
|
-
settings_mask = {"breadcrumbs": [route_mask]}
|
|
70
|
-
LOG.debug("Redirect to all at current group.")
|
|
71
|
-
return route_mask, settings_mask
|
|
72
|
-
|
|
73
|
-
def _check_page_in_bounds(self, total_count):
|
|
74
|
-
"""Redirect page out of bounds."""
|
|
75
|
-
pks = self.kwargs.get("pks")
|
|
76
|
-
page = self.kwargs.get("page", 1)
|
|
77
|
-
num_pages = ceil(total_count / MAX_OBJ_PER_PAGE)
|
|
78
|
-
if page == 1 or (page >= 1 and page <= num_pages):
|
|
79
|
-
# Don't redirect if on the root page for the group.
|
|
80
|
-
# Or page within valid range.
|
|
81
|
-
return num_pages
|
|
82
|
-
|
|
83
|
-
group = self.kwargs.get("group")
|
|
84
|
-
reason = f"{group=} {pks=} {page=} does not exist."
|
|
85
|
-
|
|
86
|
-
# Adjust route mask for redirect
|
|
87
|
-
if num_pages and page > 1:
|
|
88
|
-
route_mask = self._get_back_one_page_route(num_pages)
|
|
89
|
-
settings_mask = None
|
|
90
|
-
else:
|
|
91
|
-
# This now only occurs when page < 1
|
|
92
|
-
route_mask, settings_mask = self._get_up_page_redirect()
|
|
93
|
-
|
|
94
|
-
return self.raise_redirect(
|
|
95
|
-
reason, route_mask=route_mask, settings_mask=settings_mask
|
|
96
|
-
)
|
|
20
|
+
class BrowserPaginateView(BrowserPageInBoundsView):
|
|
21
|
+
"""Paginate Groups and Books."""
|
|
97
22
|
|
|
98
23
|
def _paginate_section(self, model, qs, page):
|
|
99
24
|
"""Paginate a group or Comic section."""
|
|
100
25
|
orphans = 0 if self.model_group == "f" or self.params.get("q") else 5
|
|
101
26
|
paginator = Paginator(qs, MAX_OBJ_PER_PAGE, orphans=orphans)
|
|
102
27
|
try:
|
|
103
|
-
|
|
28
|
+
paginator_page = paginator.page(page)
|
|
29
|
+
qs = paginator_page.object_list
|
|
104
30
|
except EmptyPage:
|
|
105
31
|
if model != Folder:
|
|
106
|
-
model_name = self.model.__name__ if self.model else "
|
|
32
|
+
model_name = self.model.__name__ if self.model else "UnknownGroup"
|
|
107
33
|
LOG.warning(f"No {model_name}s on page {page}")
|
|
108
34
|
qs = model.objects.none()
|
|
109
35
|
|
|
110
36
|
return qs
|
|
111
37
|
|
|
112
|
-
def _paginate_groups(self, group_qs
|
|
38
|
+
def _paginate_groups(self, group_qs):
|
|
113
39
|
"""Paginate the group object list before books."""
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
page_group_qs = self._paginate_section(self.model, group_qs, page)
|
|
117
|
-
else:
|
|
118
|
-
page_group_qs = self.model.objects.none() # type: ignore
|
|
119
|
-
return page_group_qs
|
|
40
|
+
page = self.kwargs.get("page", 1)
|
|
41
|
+
return self._paginate_section(self.model, group_qs, page)
|
|
120
42
|
|
|
121
|
-
def _paginate_books(self, book_qs, total_group_count,
|
|
43
|
+
def _paginate_books(self, book_qs, total_group_count, page_group_count):
|
|
122
44
|
"""Paginate the book object list based on how many group/folders are showing."""
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
45
|
+
group_remainder = page_group_count % MAX_OBJ_PER_PAGE
|
|
46
|
+
num_books_on_mixed_page = MAX_OBJ_PER_PAGE - group_remainder
|
|
47
|
+
if page_group_count:
|
|
48
|
+
# There are books after the groups on the same page
|
|
49
|
+
# Add remainder books without the paginator
|
|
50
|
+
page_book_qs = book_qs[:num_books_on_mixed_page]
|
|
126
51
|
else:
|
|
127
|
-
|
|
128
|
-
if
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if group_remainder:
|
|
136
|
-
book_offset = MAX_OBJ_PER_PAGE - group_remainder
|
|
137
|
-
page_book_qs = book_qs[book_offset:]
|
|
138
|
-
else:
|
|
139
|
-
page_book_qs = book_qs
|
|
52
|
+
# There are books after the groups on a new page
|
|
53
|
+
book_offset = 0 if group_remainder else num_books_on_mixed_page
|
|
54
|
+
page_book_qs = book_qs[book_offset:]
|
|
55
|
+
|
|
56
|
+
# Which book page are we on after groups?
|
|
57
|
+
page = self.kwargs.get("page", 1)
|
|
58
|
+
num_group_and_mixed_pages = ceil(total_group_count / MAX_OBJ_PER_PAGE)
|
|
59
|
+
book_only_page = page - num_group_and_mixed_pages
|
|
140
60
|
|
|
141
|
-
|
|
142
|
-
book_only_page = self.kwargs.get("page", 1) - num_group_and_mixed_pages
|
|
143
|
-
page_book_qs = self._paginate_section(
|
|
144
|
-
Comic, page_book_qs, book_only_page
|
|
145
|
-
)
|
|
61
|
+
page_book_qs = self._paginate_section(Comic, page_book_qs, book_only_page)
|
|
146
62
|
return page_book_qs
|
|
147
63
|
|
|
148
|
-
def paginate(self, group_qs, book_qs, group_count
|
|
64
|
+
def paginate(self, group_qs, book_qs, group_count):
|
|
149
65
|
"""Paginate the queryset into a group and book object lists."""
|
|
150
|
-
|
|
151
|
-
page_group_qs = self._paginate_groups(group_qs, group_count)
|
|
66
|
+
page_group_qs = self._paginate_groups(group_qs)
|
|
152
67
|
page_group_count = page_group_qs.count()
|
|
153
68
|
page_book_qs = self._paginate_books(book_qs, group_count, page_group_count)
|
|
154
69
|
page_book_count = page_book_qs.count()
|
|
155
70
|
|
|
156
|
-
return page_group_qs, page_book_qs,
|
|
71
|
+
return page_group_qs, page_book_qs, page_group_count, page_book_count
|
codex/views/const.py
CHANGED
codex/views/opds/v1/feed.py
CHANGED
|
@@ -14,7 +14,8 @@ from codex.serializers.browser.settings import OPDSSettingsSerializer
|
|
|
14
14
|
from codex.serializers.opds.v1 import (
|
|
15
15
|
OPDS1TemplateSerializer,
|
|
16
16
|
)
|
|
17
|
-
from codex.
|
|
17
|
+
from codex.settings.settings import FALSY
|
|
18
|
+
from codex.views.const import MAX_OBJ_PER_PAGE
|
|
18
19
|
from codex.views.opds.auth import OPDSTemplateView
|
|
19
20
|
from codex.views.opds.const import BLANK_TITLE, MimeType, UserAgentNames
|
|
20
21
|
from codex.views.opds.util import get_user_agent_name
|
codex/views/opds/v2/feed.py
CHANGED
|
@@ -9,7 +9,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
11
|
from codex.serializers.opds.v2 import OPDS2FeedSerializer
|
|
12
|
-
from codex.
|
|
12
|
+
from codex.settings.settings import FALSY
|
|
13
|
+
from codex.views.const import MAX_OBJ_PER_PAGE
|
|
13
14
|
from codex.views.opds.auth import OPDSAuthMixin
|
|
14
15
|
from codex.views.opds.const import BLANK_TITLE
|
|
15
16
|
from codex.views.opds.v2.const import (
|
codex/views/opds/v2/links.py
CHANGED
|
@@ -6,8 +6,8 @@ from urllib.parse import parse_qsl, urlparse
|
|
|
6
6
|
|
|
7
7
|
from django.urls import reverse
|
|
8
8
|
|
|
9
|
+
from codex.settings.settings import FALSY
|
|
9
10
|
from codex.views.browser.browser import BrowserView
|
|
10
|
-
from codex.views.const import FALSY
|
|
11
11
|
from codex.views.opds.const import MimeType, Rel
|
|
12
12
|
from codex.views.opds.util import update_href_query_params
|
|
13
13
|
from codex.views.util import pop_name
|
codex/views/reader/books.py
CHANGED
|
@@ -92,8 +92,8 @@ class ReaderBooksView(BookmarkFilterBaseView, ReaderInitView, SharedAnnotationsM
|
|
|
92
92
|
arc_pks = self._get_reader_arc_pks(
|
|
93
93
|
arc, arc_pk_select_related, prefetch_related, arc_pk_rel, arc_group
|
|
94
94
|
)
|
|
95
|
-
nav_filter = {f"{rel}__in": arc_pks}
|
|
96
95
|
group_acl_filter = self.get_group_acl_filter(Comic)
|
|
96
|
+
nav_filter = {f"{rel}__in": arc_pks}
|
|
97
97
|
|
|
98
98
|
qs = Comic.objects.filter(group_acl_filter).filter(**nav_filter)
|
|
99
99
|
if browser_filters := arc.get("filters"):
|
codex/views/reader/page.py
CHANGED
|
@@ -10,9 +10,9 @@ from rest_framework.exceptions import NotFound
|
|
|
10
10
|
from rest_framework.negotiation import BaseContentNegotiation
|
|
11
11
|
|
|
12
12
|
from codex.logger.logging import get_logger
|
|
13
|
-
from codex.models import Comic
|
|
13
|
+
from codex.models.comic import Comic, FileType
|
|
14
|
+
from codex.settings.settings import FALSY
|
|
14
15
|
from codex.views.bookmark import BookmarkBaseView
|
|
15
|
-
from codex.views.const import FALSY
|
|
16
16
|
from codex.views.util import chunker
|
|
17
17
|
|
|
18
18
|
LOG = get_logger(__name__)
|
|
@@ -68,7 +68,7 @@ class ReaderPageView(BookmarkBaseView):
|
|
|
68
68
|
comic = Comic.objects.filter(group_acl_filter).only("path").get(pk=pk)
|
|
69
69
|
page = self.kwargs.get("page")
|
|
70
70
|
to_pixmap = self.request.GET.get("pixmap", "").lower() not in FALSY
|
|
71
|
-
if comic.file_type ==
|
|
71
|
+
if comic.file_type == FileType.PDF.value and not to_pixmap:
|
|
72
72
|
content_type = _PDF_MIME_TYPE
|
|
73
73
|
else:
|
|
74
74
|
content_type = self.content_type
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codex
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0a0
|
|
4
4
|
Summary: A comic archive web server.
|
|
5
5
|
Home-page: https://github.com/ajslater/codex
|
|
6
6
|
License: GPL-3.0-only
|
|
@@ -30,6 +30,7 @@ Requires-Dist: bidict (>=0.23.1,<0.24.0)
|
|
|
30
30
|
Requires-Dist: case-converter (>=1.1.0,<2.0.0)
|
|
31
31
|
Requires-Dist: channels (>=4.0.0,<5.0.0)
|
|
32
32
|
Requires-Dist: comicbox[pdf] (>=1.2.0,<2.0.0)
|
|
33
|
+
Requires-Dist: dateparser (>=1.2.0,<2.0.0)
|
|
33
34
|
Requires-Dist: django (>=5.1.0,<6.0.0)
|
|
34
35
|
Requires-Dist: django-cachalot (>=2.6.3,<3.0.0)
|
|
35
36
|
Requires-Dist: django-cors-headers (>=4.0,<5.0)
|
|
@@ -43,15 +44,14 @@ Requires-Dist: humanfriendly (>=10.0,<11.0)
|
|
|
43
44
|
Requires-Dist: humanize (>=4.0.0,<5.0.0)
|
|
44
45
|
Requires-Dist: hypercorn[h3] (>=0.17.3,<0.18.0)
|
|
45
46
|
Requires-Dist: psutil (>=6.0.0,<7.0.0)
|
|
47
|
+
Requires-Dist: pyparsing (>=3.1.2,<4.0.0)
|
|
46
48
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
47
49
|
Requires-Dist: requests (>=2.24,<3.0)
|
|
48
|
-
Requires-Dist: setuptools (>=74.1.2,<75.0.0)
|
|
49
50
|
Requires-Dist: tzlocal (>=5.0,<6.0)
|
|
50
51
|
Requires-Dist: versio (>=0.5.0,<0.6.0)
|
|
51
52
|
Requires-Dist: watchdog (>=5.0.2,<6.0.0)
|
|
52
53
|
Requires-Dist: websocket_client (>=1.2,<2.0)
|
|
53
54
|
Requires-Dist: whitenoise[brotli] (>=6.0,<7.0)
|
|
54
|
-
Requires-Dist: whoosh-reloaded (>=2.7.5,<3.0.0)
|
|
55
55
|
Project-URL: Documentation, https://github.com/ajslater/codex
|
|
56
56
|
Project-URL: Docker Image, https://hub.docker.com/r/ajslater/codex
|
|
57
57
|
Project-URL: News, https://github.com/ajslater/codex/blob/main/NEWS.md
|
|
@@ -145,7 +145,7 @@ before installing Codex.
|
|
|
145
145
|
<!-- eslint-skip -->
|
|
146
146
|
|
|
147
147
|
```sh
|
|
148
|
-
apt install build-essential libimagequant0 libjpeg-turbo8 libopenjp2-7 libssl libyaml-0-2 libtiff6 libwebp7 python3-dev python3-pip mupdf unrar zlib1g
|
|
148
|
+
apt install build-essential libimagequant0 libjpeg-turbo8 libopenjp2-7 libssl libyaml-0-2 libtiff6 libwebp7 python3-dev python3-pip mupdf sqlite3 unrar zlib1g
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
Versions of packages like libjpeg, libssl, libtiff may differ between flavors
|
|
@@ -164,7 +164,7 @@ apt-cache search libjpeg-turbo
|
|
|
164
164
|
<!-- eslint-skip -->
|
|
165
165
|
|
|
166
166
|
```sh
|
|
167
|
-
apk add bsd-compat-headers build-base jpeg-dev libffi-dev libwebp openssl-dev yaml-dev zlib-dev
|
|
167
|
+
apk add bsd-compat-headers build-base jpeg-dev libffi-dev libwebp openssl-dev sqlite yaml-dev zlib-dev
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
##### Install unrar Runtime Dependency on non-debian Linux
|
|
@@ -182,7 +182,7 @@ Using [Homebrew](https://brew.sh/):
|
|
|
182
182
|
<!-- eslint-skip -->
|
|
183
183
|
|
|
184
184
|
```sh
|
|
185
|
-
brew install jpeg libffi libyaml libzip openssl python unrar webp
|
|
185
|
+
brew install jpeg libffi libyaml libzip openssl python sqlite unrar webp
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
##### <a href="#windows">Windows</a> Dependencies
|
|
@@ -336,8 +336,15 @@ index, a Django cache and comic book cover thumbnails.
|
|
|
336
336
|
`$CWD/config`
|
|
337
337
|
- `CODEX_RESET_ADMIN=1` will reset the admin user and its password to defaults
|
|
338
338
|
when codex starts.
|
|
339
|
-
- `
|
|
340
|
-
|
|
339
|
+
- `CODEX_FIX_FOREIGN_KEYS=1` will check for and try to repair illegal foreign
|
|
340
|
+
keys on startup.
|
|
341
|
+
- `CODEX_INTEGRITY_CHECK=1` will perform a quick full database integrity check
|
|
342
|
+
on startup.
|
|
343
|
+
- `CODEX_INTEGRITY_CHECK=full` will perform a more thorough database integrity
|
|
344
|
+
check on startup.
|
|
345
|
+
- `CODEX_FTS_INTEGRITY_CHECK=1` will perform an integrity check on the full text
|
|
346
|
+
search index.
|
|
347
|
+
- `CODEX_FTS_REBUILD=1` will rebuild the full text search index.
|
|
341
348
|
- `DEBUG_TRANSFORM` will show verbose information about how the comicbox library
|
|
342
349
|
reads all archive metadata sources and transforms it into a the comicbox
|
|
343
350
|
schema.
|
|
@@ -503,7 +510,7 @@ you.
|
|
|
503
510
|
|
|
504
511
|
### Emergency Database Repair
|
|
505
512
|
|
|
506
|
-
If the database becomes corrupt, Codex includes a
|
|
513
|
+
If the database becomes corrupt, Codex includes a facility to rebuild the
|
|
507
514
|
database. Place a file named `rebuild_db` in your Codex config directory like
|
|
508
515
|
so:
|
|
509
516
|
|
|
@@ -518,7 +525,8 @@ Shut down and restart Codex.
|
|
|
518
525
|
The next time Codex starts it will back up the existing database and try to
|
|
519
526
|
rebuild it. The database lives in the config directory as the file
|
|
520
527
|
`config/db.sqlite3`. If this procedure goes kablooey, you may recover the
|
|
521
|
-
original database at `config/
|
|
528
|
+
original database at `config/backups/codex.sqlite3.before-rebuild`. Codex will
|
|
529
|
+
remove the `rebuild_db` file.
|
|
522
530
|
|
|
523
531
|
## <a name="alternatives-to-codex">📚Alternatives</a>
|
|
524
532
|
|