codex 1.6.0a9__py3-none-any.whl → 1.6.0a10__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/librarian/importer/cache.py +46 -133
- codex/librarian/importer/create_fks.py +3 -1
- codex/librarian/importer/importer.py +1 -1
- codex/librarian/importer/importerd.py +5 -5
- codex/librarian/importer/tasks.py +2 -2
- codex/librarian/janitor/janitor.py +0 -3
- codex/librarian/librariand.py +0 -2
- codex/librarian/notifier/notifierd.py +1 -1
- codex/librarian/notifier/tasks.py +4 -4
- codex/middleware.py +1 -2
- codex/migrations/0027_import_order_and_covers.py +1 -66
- codex/models/comic.py +0 -1
- codex/models/groups.py +0 -3
- codex/serializers/admin.py +41 -40
- codex/serializers/browser/choices.py +22 -6
- codex/serializers/browser/metadata.py +45 -1
- codex/serializers/browser/page.py +2 -4
- codex/serializers/browser/settings.py +34 -14
- codex/serializers/fields.py +19 -1
- codex/serializers/reader.py +11 -1
- codex/serializers/route.py +1 -1
- codex/serializers/settings.py +12 -0
- codex/settings/settings.py +10 -1
- codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js +1 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js.br +0 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js.gz +0 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.js +1 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.js.br +0 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DLzF_WHB.9a47873b8548.js → VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DLzF_WHB.js → VCheckboxBtn-D2tPTt6P.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.js.gz +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js +1 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js.br +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js.gz +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.js +1 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.js.br +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.js.gz +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js +1 -0
- codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js.br +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js.gz +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.js +1 -0
- codex/static_root/assets/VDialog-61pzfUB2.js.br +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.js.gz +0 -0
- codex/static_root/assets/{VDialog-C5Nxd_zP.css → VDialog-lQ13FnRR.1c801ad756b2.css} +1 -1
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
- codex/static_root/assets/{VDialog-C5Nxd_zP.d327f505ef0e.css → VDialog-lQ13FnRR.css} +1 -1
- codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js +1 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.js +1 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-B--7K_v5.4a4e9c2d6c42.js → VRadioGroup-QFQbK1qo.750a42b1b814.js} +1 -1
- codex/static_root/assets/VRadioGroup-QFQbK1qo.750a42b1b814.js.br +0 -0
- codex/static_root/assets/VRadioGroup-QFQbK1qo.750a42b1b814.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-B--7K_v5.js → VRadioGroup-QFQbK1qo.js} +1 -1
- codex/static_root/assets/VRadioGroup-QFQbK1qo.js.br +0 -0
- codex/static_root/assets/VRadioGroup-QFQbK1qo.js.gz +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js +1 -0
- codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js.br +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js.gz +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.js +1 -0
- codex/static_root/assets/VSelect-Dixy6okt.js.br +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CVQ9wkE2.c02186cceba6.js → VSelectionControl-MNmxKc3J.08055d00917e.js} +1 -1
- codex/static_root/assets/VSelectionControl-MNmxKc3J.08055d00917e.js.br +0 -0
- codex/static_root/assets/VSelectionControl-MNmxKc3J.08055d00917e.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CVQ9wkE2.js → VSelectionControl-MNmxKc3J.js} +1 -1
- codex/static_root/assets/VSelectionControl-MNmxKc3J.js.br +0 -0
- codex/static_root/assets/VSelectionControl-MNmxKc3J.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js +1 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.js +1 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.js.gz +0 -0
- codex/static_root/assets/{VTable-CrWkhkiP.6d530eea0c09.js → VTable-lcnYg9Ee.d72927b13c2e.js} +1 -1
- codex/static_root/assets/VTable-lcnYg9Ee.d72927b13c2e.js.br +0 -0
- codex/static_root/assets/VTable-lcnYg9Ee.d72927b13c2e.js.gz +0 -0
- codex/static_root/assets/{VTable-CrWkhkiP.js → VTable-lcnYg9Ee.js} +1 -1
- codex/static_root/assets/VTable-lcnYg9Ee.js.br +0 -0
- codex/static_root/assets/VTable-lcnYg9Ee.js.gz +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js +1 -0
- codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js.br +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js.gz +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.js +1 -0
- codex/static_root/assets/VTextField-BkFrjhdV.js.br +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js +1 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js.br +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.js +1 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.js.br +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.js.gz +0 -0
- codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js +1 -0
- codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js.br +0 -0
- codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js.gz +0 -0
- codex/static_root/assets/admin-CXES2HXe.js +1 -0
- codex/static_root/assets/admin-CXES2HXe.js.br +0 -0
- codex/static_root/assets/admin-CXES2HXe.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-o87BwD38.ce39427c8201.css → admin-drawer-panel-mne3FIj6.561b3f75fe65.css} +1 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-o87BwD38.css → admin-drawer-panel-mne3FIj6.css} +1 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js +30 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js +30 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js.gz +0 -0
- codex/static_root/assets/browser-BuDeXGEe.css +1 -0
- codex/static_root/assets/browser-BuDeXGEe.css.br +0 -0
- codex/static_root/assets/browser-BuDeXGEe.css.gz +0 -0
- codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css +1 -0
- codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css.br +0 -0
- codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css.gz +0 -0
- codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js +1 -0
- codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js.br +0 -0
- codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js.gz +0 -0
- codex/static_root/assets/browser-s6mFzrK9.js +1 -0
- codex/static_root/assets/browser-s6mFzrK9.js.br +0 -0
- codex/static_root/assets/browser-s6mFzrK9.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +1 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css +1 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js +1 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.js +1 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-jwL7B98r.1cfd579cbcfe.js → confirm-dialog-CWszioQE.859051c35dd9.js} +1 -1
- codex/static_root/assets/confirm-dialog-CWszioQE.859051c35dd9.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CWszioQE.859051c35dd9.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-jwL7B98r.js → confirm-dialog-CWszioQE.js} +1 -1
- codex/static_root/assets/confirm-dialog-CWszioQE.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CWszioQE.js.gz +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js +1 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js.br +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js.gz +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.js +1 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.js.br +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.js.gz +0 -0
- codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js +1 -0
- codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js.br +0 -0
- codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js.gz +0 -0
- codex/static_root/assets/filter-D3gaGf87.js +1 -0
- codex/static_root/assets/filter-D3gaGf87.js.br +0 -0
- codex/static_root/assets/filter-D3gaGf87.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Da2DBNyz.ffbe5ca37e1c.js → flag-tab-8vzpdySX.a982c7f0776a.js} +1 -1
- codex/static_root/assets/flag-tab-8vzpdySX.a982c7f0776a.js.br +0 -0
- codex/static_root/assets/flag-tab-8vzpdySX.a982c7f0776a.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Da2DBNyz.js → flag-tab-8vzpdySX.js} +1 -1
- codex/static_root/assets/flag-tab-8vzpdySX.js.br +0 -0
- codex/static_root/assets/flag-tab-8vzpdySX.js.gz +0 -0
- codex/static_root/assets/{group-tab-BONYWOc1.6ddf321a4a56.js → group-tab-Cz9t_ECi.a9e8aea79254.js} +1 -1
- codex/static_root/assets/group-tab-Cz9t_ECi.a9e8aea79254.js.br +0 -0
- codex/static_root/assets/group-tab-Cz9t_ECi.a9e8aea79254.js.gz +0 -0
- codex/static_root/assets/{group-tab-BONYWOc1.js → group-tab-Cz9t_ECi.js} +1 -1
- codex/static_root/assets/group-tab-Cz9t_ECi.js.br +0 -0
- codex/static_root/assets/group-tab-Cz9t_ECi.js.gz +0 -0
- codex/static_root/assets/{http-error-D2Jnc20C.32846bc3a43d.js → http-error-Cqtszzf1.b31df3d9c29f.js} +1 -1
- codex/static_root/assets/http-error-Cqtszzf1.b31df3d9c29f.js.br +0 -0
- codex/static_root/assets/http-error-Cqtszzf1.b31df3d9c29f.js.gz +0 -0
- codex/static_root/assets/{http-error-D2Jnc20C.js → http-error-Cqtszzf1.js} +1 -1
- codex/static_root/assets/http-error-Cqtszzf1.js.br +0 -0
- codex/static_root/assets/http-error-Cqtszzf1.js.gz +0 -0
- codex/static_root/assets/{library-tab-B419agXA.js → library-tab-DMlnswUU.30f454e84184.js} +1 -1
- codex/static_root/assets/library-tab-DMlnswUU.30f454e84184.js.br +0 -0
- codex/static_root/assets/library-tab-DMlnswUU.30f454e84184.js.gz +0 -0
- codex/static_root/assets/{library-tab-B419agXA.d4b778172aaa.js → library-tab-DMlnswUU.js} +1 -1
- codex/static_root/assets/library-tab-DMlnswUU.js.br +0 -0
- codex/static_root/assets/library-tab-DMlnswUU.js.gz +0 -0
- codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js +32 -0
- codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js.br +0 -0
- codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js.gz +0 -0
- codex/static_root/assets/main-vjXu_QYL.js +32 -0
- codex/static_root/assets/main-vjXu_QYL.js.br +0 -0
- codex/static_root/assets/main-vjXu_QYL.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css +1 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.css +1 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js +1 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.js +1 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-8RVZqILx.2e7166dd26fb.js → pdf-doc-BiamrjRZ.4c9be1ade43c.js} +1 -1
- codex/static_root/assets/pdf-doc-BiamrjRZ.4c9be1ade43c.js.br +0 -0
- codex/static_root/assets/pdf-doc-BiamrjRZ.4c9be1ade43c.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-8RVZqILx.js → pdf-doc-BiamrjRZ.js} +1 -1
- codex/static_root/assets/pdf-doc-BiamrjRZ.js.br +0 -0
- codex/static_root/assets/pdf-doc-BiamrjRZ.js.gz +0 -0
- codex/static_root/assets/{reader-CCCfpM3x.1553e45bd775.css → reader-BpsctRsY.5d72e393911a.css} +1 -1
- codex/static_root/assets/reader-BpsctRsY.5d72e393911a.css.br +0 -0
- codex/static_root/assets/reader-BpsctRsY.5d72e393911a.css.gz +0 -0
- codex/static_root/assets/{reader-CCCfpM3x.css → reader-BpsctRsY.css} +1 -1
- codex/static_root/assets/reader-BpsctRsY.css.br +0 -0
- codex/static_root/assets/reader-BpsctRsY.css.gz +0 -0
- codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js +2 -0
- codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js.br +0 -0
- codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js.gz +0 -0
- codex/static_root/assets/reader-CDk8oZzB.js +2 -0
- codex/static_root/assets/reader-CDk8oZzB.js.br +0 -0
- codex/static_root/assets/reader-CDk8oZzB.js.gz +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js +1 -0
- codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js.br +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js.gz +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.js +1 -0
- codex/static_root/assets/relation-chips-C5acBjpy.js.br +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.js.gz +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js +2 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js.br +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js.gz +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.js +2 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.js.br +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.js.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +1 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css +1 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js +1 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js.br +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js.gz +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.js +1 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.js.br +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.js.gz +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js +1 -0
- codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js.br +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js.gz +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.js +1 -0
- codex/static_root/assets/task-tab-DF9qa6LO.js.br +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.js.gz +0 -0
- codex/static_root/assets/to-case-CGbULRaE.4c80644f460d.js +1 -0
- codex/static_root/assets/to-case-CGbULRaE.4c80644f460d.js.br +0 -0
- codex/static_root/assets/to-case-CGbULRaE.js +1 -0
- codex/static_root/assets/to-case-CGbULRaE.js.br +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js +1 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js.br +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js.gz +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.js +1 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.js.br +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.js.gz +0 -0
- codex/static_root/assets/{unauthorized-DHV6Q1n-.b7847b2872a1.css → unauthorized-CibJYD8o.4c2de98c2b53.css} +1 -1
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
- codex/static_root/assets/{unauthorized-DHV6Q1n-.css → unauthorized-CibJYD8o.css} +1 -1
- codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js +1 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js.br +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js.gz +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.js +1 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.js.br +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.js.gz +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json +1 -0
- codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.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.35e46a3ec8fa.json +622 -0
- codex/static_root/manifest.35e46a3ec8fa.json.br +0 -0
- codex/static_root/manifest.35e46a3ec8fa.json.gz +0 -0
- codex/static_root/manifest.json +245 -252
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/views/admin/api_key.py +5 -2
- codex/views/admin/stats.py +13 -20
- codex/views/admin/tasks.py +2 -3
- codex/views/auth.py +1 -1
- codex/views/bookmark.py +30 -16
- codex/views/browser/annotations.py +2 -0
- codex/views/browser/base.py +22 -6
- codex/views/browser/breadcrumbs.py +5 -5
- codex/views/browser/browser.py +14 -22
- codex/views/browser/choices.py +18 -15
- codex/views/browser/cover.py +30 -66
- codex/views/browser/filters/annotations.py +14 -6
- codex/views/browser/filters/group.py +24 -15
- codex/views/browser/filters/search.py +3 -4
- codex/views/browser/metadata.py +86 -47
- codex/views/browser/mtime.py +3 -14
- codex/views/browser/order_by.py +1 -6
- codex/views/browser/settings.py +53 -0
- codex/views/browser/validate.py +3 -0
- codex/views/const.py +24 -1
- codex/views/opds/v1/feed.py +3 -5
- codex/views/opds/v2/feed.py +3 -3
- codex/views/reader/arcs.py +42 -51
- codex/views/reader/books.py +47 -63
- codex/views/reader/init.py +121 -0
- codex/views/reader/reader.py +9 -70
- codex/views/session.py +15 -11
- codex/views/settings.py +23 -10
- codex/views/util.py +2 -1
- codex/websockets/consumers.py +13 -9
- {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/METADATA +1 -1
- {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/RECORD +321 -317
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js +0 -1
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.br +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.gz +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.js +0 -1
- codex/static_root/assets/VCheckbox-eNspVUje.js.br +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.gz +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js +0 -1
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.br +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.gz +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.js +0 -1
- codex/static_root/assets/VCombobox-BIO7lU_B.js.br +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.js.gz +0 -0
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js +0 -1
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.br +0 -0
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.gz +0 -0
- codex/static_root/assets/VDialog-87AymJC5.js +0 -1
- codex/static_root/assets/VDialog-87AymJC5.js.br +0 -0
- codex/static_root/assets/VDialog-87AymJC5.js.gz +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js +0 -1
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js +0 -1
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.js.gz +0 -0
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js +0 -1
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.br +0 -0
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.gz +0 -0
- codex/static_root/assets/VSelect-BoJq222l.js +0 -1
- codex/static_root/assets/VSelect-BoJq222l.js.br +0 -0
- codex/static_root/assets/VSelect-BoJq222l.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js +0 -1
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.br +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js +0 -1
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.br +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.gz +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.br +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.gz +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.js.br +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.js.gz +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js +0 -1
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.br +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.gz +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.js +0 -1
- codex/static_root/assets/VTextField-CkWbil3K.js.br +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.js.gz +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js +0 -1
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.br +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.gz +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.js +0 -1
- codex/static_root/assets/VWindowItem-7z55Y4lK.js.br +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.js.gz +0 -0
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js +0 -1
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.br +0 -0
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.gz +0 -0
- codex/static_root/assets/admin-D3OK784R.js +0 -1
- codex/static_root/assets/admin-D3OK784R.js.br +0 -0
- codex/static_root/assets/admin-D3OK784R.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js +0 -30
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js +0 -30
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.css.gz +0 -0
- codex/static_root/assets/browser-BuU2x9y7.css +0 -1
- codex/static_root/assets/browser-BuU2x9y7.css.br +0 -0
- codex/static_root/assets/browser-BuU2x9y7.css.gz +0 -0
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css +0 -1
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.br +0 -0
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.gz +0 -0
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js +0 -1
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.br +0 -0
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.gz +0 -0
- codex/static_root/assets/browser-CO9EYHIr.js +0 -1
- codex/static_root/assets/browser-CO9EYHIr.js.br +0 -0
- codex/static_root/assets/browser-CO9EYHIr.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js +0 -1
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js +0 -1
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +0 -1
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css +0 -1
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.1cfd579cbcfe.js.br +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.1cfd579cbcfe.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.js.br +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.js.gz +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js +0 -1
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.br +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.gz +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.js +0 -1
- codex/static_root/assets/datetime-column-9w3GcKvo.js.br +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.js.gz +0 -0
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js +0 -1
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.br +0 -0
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.gz +0 -0
- codex/static_root/assets/filter-C-pghOri.js +0 -1
- codex/static_root/assets/filter-C-pghOri.js.br +0 -0
- codex/static_root/assets/filter-C-pghOri.js.gz +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.br +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.gz +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.js.br +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.js.gz +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.br +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.gz +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.js.br +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.js.gz +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.br +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.gz +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.js.br +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.js.gz +0 -0
- codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.br +0 -0
- codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.gz +0 -0
- codex/static_root/assets/library-tab-B419agXA.js.br +0 -0
- codex/static_root/assets/library-tab-B419agXA.js.gz +0 -0
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js +0 -32
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.br +0 -0
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.gz +0 -0
- codex/static_root/assets/main-CMQwJ9aG.js +0 -32
- codex/static_root/assets/main-CMQwJ9aG.js.br +0 -0
- codex/static_root/assets/main-CMQwJ9aG.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css +0 -1
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css +0 -1
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js +0 -1
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js +0 -1
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.gz +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.br +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.gz +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.js.br +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.js.gz +0 -0
- codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.br +0 -0
- codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.gz +0 -0
- codex/static_root/assets/reader-CCCfpM3x.css.br +0 -0
- codex/static_root/assets/reader-CCCfpM3x.css.gz +0 -0
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js +0 -2
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.br +0 -0
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.gz +0 -0
- codex/static_root/assets/reader-DNnK_Chr.js +0 -2
- codex/static_root/assets/reader-DNnK_Chr.js.br +0 -0
- codex/static_root/assets/reader-DNnK_Chr.js.gz +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js +0 -1
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.br +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.gz +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.js +0 -1
- codex/static_root/assets/relation-chips-PYua7aXv.js.br +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js +0 -2
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.br +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.js +0 -2
- codex/static_root/assets/settings-drawer-DbN6ISRH.js.br +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.js.gz +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js +0 -1
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.br +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.gz +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.js +0 -1
- codex/static_root/assets/stats-tab-DImNhH8O.js.br +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.js.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +0 -1
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +0 -2
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css +0 -1
- codex/static_root/assets/stats-tab-TsyG3wH_.css.br +0 -2
- codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js +0 -1
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.br +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.gz +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.js +0 -1
- codex/static_root/assets/task-tab-DPD53kjv.js.br +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.js.gz +0 -0
- codex/static_root/assets/to-case-ehC9Ccyj.a7b8e25b391f.js +0 -1
- codex/static_root/assets/to-case-ehC9Ccyj.js +0 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js +0 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.br +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.gz +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.js +0 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.js.br +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.js.gz +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.b7847b2872a1.css.br +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.b7847b2872a1.css.gz +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.css.br +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.css.gz +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js +0 -1
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.br +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.gz +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.js +0 -1
- codex/static_root/assets/user-tab-BABwfxLO.js.br +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.js.gz +0 -0
- codex/static_root/js/choices-admin.d790df01c20a.json +0 -1
- codex/static_root/js/choices-admin.d790df01c20a.json.br +0 -0
- codex/static_root/js/choices-admin.d790df01c20a.json.gz +0 -0
- codex/static_root/manifest.cbea5663003a.json +0 -629
- codex/static_root/manifest.cbea5663003a.json.br +0 -0
- codex/static_root/manifest.cbea5663003a.json.gz +0 -0
- {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/LICENSE +0 -0
- {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/WHEEL +0 -0
- {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/entry_points.txt +0 -0
codex/views/opds/v1/feed.py
CHANGED
|
@@ -11,10 +11,10 @@ from rest_framework.throttling import ScopedRateThrottle
|
|
|
11
11
|
from codex.librarian.importer.tasks import LazyImportComicsTask
|
|
12
12
|
from codex.librarian.mp_queue import LIBRARIAN_QUEUE
|
|
13
13
|
from codex.logger.logging import get_logger
|
|
14
|
+
from codex.serializers.browser.settings import OPDSSettingsSerializer
|
|
14
15
|
from codex.serializers.opds.v1 import (
|
|
15
16
|
OPDS1TemplateSerializer,
|
|
16
17
|
)
|
|
17
|
-
from codex.views.browser.browser import BrowserView
|
|
18
18
|
from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
|
|
19
19
|
from codex.views.opds.auth import OPDSTemplateView
|
|
20
20
|
from codex.views.opds.const import BLANK_TITLE, MimeType
|
|
@@ -56,6 +56,7 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
56
56
|
|
|
57
57
|
template_name = "opds_v1/index.xml"
|
|
58
58
|
serializer_class = OPDS1TemplateSerializer
|
|
59
|
+
input_serializer_class = OPDSSettingsSerializer
|
|
59
60
|
throttle_classes = (ScopedRateThrottle,)
|
|
60
61
|
throttle_scope = "opds"
|
|
61
62
|
TARGET = "opds1"
|
|
@@ -254,10 +255,7 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
254
255
|
self._set_user_agent_variables()
|
|
255
256
|
self.skip_order_facets |= self.kwargs.get("group") == "c"
|
|
256
257
|
|
|
257
|
-
@extend_schema(
|
|
258
|
-
request=BrowserView.input_serializer_class,
|
|
259
|
-
parameters=[BrowserView.input_serializer_class],
|
|
260
|
-
)
|
|
258
|
+
@extend_schema(parameters=[input_serializer_class])
|
|
261
259
|
def get(self, *_args, **_kwargs):
|
|
262
260
|
"""Get the feed."""
|
|
263
261
|
self.init_request()
|
codex/views/opds/v2/feed.py
CHANGED
|
@@ -7,8 +7,8 @@ from rest_framework.response import Response
|
|
|
7
7
|
|
|
8
8
|
from codex.logger.logging import get_logger
|
|
9
9
|
from codex.models import AdminFlag
|
|
10
|
+
from codex.serializers.browser.settings import OPDSSettingsSerializer
|
|
10
11
|
from codex.serializers.opds.v2 import OPDS2FeedSerializer
|
|
11
|
-
from codex.views.browser.browser import BrowserView
|
|
12
12
|
from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
|
|
13
13
|
from codex.views.opds.auth import OPDSAuthMixin
|
|
14
14
|
from codex.views.opds.const import BLANK_TITLE
|
|
@@ -36,6 +36,7 @@ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
|
|
|
36
36
|
throttle_scope = "opds"
|
|
37
37
|
|
|
38
38
|
serializer_class = OPDS2FeedSerializer
|
|
39
|
+
input_serializer_class = OPDSSettingsSerializer
|
|
39
40
|
|
|
40
41
|
def _title(self, browser_title):
|
|
41
42
|
"""Create the feed title."""
|
|
@@ -233,8 +234,7 @@ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
|
|
|
233
234
|
# self._detect_user_agent()
|
|
234
235
|
|
|
235
236
|
@extend_schema(
|
|
236
|
-
|
|
237
|
-
parameters=[BrowserView.input_serializer_class],
|
|
237
|
+
parameters=[input_serializer_class],
|
|
238
238
|
)
|
|
239
239
|
def get(self, *_args, **_kwargs):
|
|
240
240
|
"""Get the feed."""
|
codex/views/reader/arcs.py
CHANGED
|
@@ -4,7 +4,7 @@ from types import MappingProxyType
|
|
|
4
4
|
|
|
5
5
|
from codex.models import AdminFlag
|
|
6
6
|
from codex.util import max_none
|
|
7
|
-
from codex.views.const import FOLDER_GROUP
|
|
7
|
+
from codex.views.const import FOLDER_GROUP, STORY_ARC_GROUP
|
|
8
8
|
from codex.views.reader.books import ReaderBooksView
|
|
9
9
|
|
|
10
10
|
|
|
@@ -16,6 +16,22 @@ class ReaderArcsView(ReaderBooksView):
|
|
|
16
16
|
super().__init__(*args, **kwargs)
|
|
17
17
|
self.params: MappingProxyType = MappingProxyType({})
|
|
18
18
|
|
|
19
|
+
def _get_group_arc(self, book, attr):
|
|
20
|
+
"""Create the volume arc."""
|
|
21
|
+
group = getattr(book, attr, None)
|
|
22
|
+
if not group:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
_, arc_pks = self.get_group_pks_from_breadcrumbs([group])
|
|
26
|
+
if group.pk not in arc_pks:
|
|
27
|
+
arc_pks = (group.pk,)
|
|
28
|
+
return {
|
|
29
|
+
"group": attr[0],
|
|
30
|
+
"pks": arc_pks,
|
|
31
|
+
"name": group.name,
|
|
32
|
+
"mtime": group.updated_at,
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
def _get_folder_arc(self, book):
|
|
20
36
|
"""Create the folder arc."""
|
|
21
37
|
efv_flag = (
|
|
@@ -23,39 +39,16 @@ class ReaderArcsView(ReaderBooksView):
|
|
|
23
39
|
.get(key=AdminFlag.FlagChoices.FOLDER_VIEW.value)
|
|
24
40
|
.on
|
|
25
41
|
)
|
|
42
|
+
if not efv_flag:
|
|
43
|
+
return None
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
else:
|
|
36
|
-
folder_arc = None
|
|
37
|
-
return folder_arc
|
|
38
|
-
|
|
39
|
-
def _get_series_arc(self, book):
|
|
40
|
-
"""Create the series arc."""
|
|
41
|
-
series = book.series
|
|
42
|
-
if series:
|
|
43
|
-
arc_pks = self.get_series_pks_from_breadcrumbs()
|
|
44
|
-
if book.series.pk not in arc_pks:
|
|
45
|
-
arc_pks = (book.series.pk,)
|
|
46
|
-
arc = (
|
|
47
|
-
{
|
|
48
|
-
"group": "s",
|
|
49
|
-
"pks": arc_pks,
|
|
50
|
-
"name": series.name,
|
|
51
|
-
"mtime": series.updated_at,
|
|
52
|
-
}
|
|
53
|
-
if series
|
|
54
|
-
else None
|
|
55
|
-
)
|
|
56
|
-
else:
|
|
57
|
-
arc = None
|
|
58
|
-
return arc
|
|
45
|
+
folder = book.parent_folder
|
|
46
|
+
return {
|
|
47
|
+
"group": FOLDER_GROUP,
|
|
48
|
+
"pks": (folder.pk,),
|
|
49
|
+
"name": folder.name,
|
|
50
|
+
"mtime": folder.updated_at,
|
|
51
|
+
}
|
|
59
52
|
|
|
60
53
|
def _get_story_arcs(self, book):
|
|
61
54
|
"""Create story arcs."""
|
|
@@ -74,26 +67,24 @@ class ReaderArcsView(ReaderBooksView):
|
|
|
74
67
|
def get_arcs(self, book):
|
|
75
68
|
"""Get all series/folder/story arcs."""
|
|
76
69
|
# create top arcs
|
|
77
|
-
|
|
78
|
-
series_arc = self._get_series_arc(book)
|
|
79
|
-
|
|
80
|
-
# order top arcs
|
|
70
|
+
arcs = []
|
|
81
71
|
top_group = self.params.get("top_group")
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
else:
|
|
72
|
+
arc = None
|
|
73
|
+
if series_arc := self._get_group_arc(book, "series"):
|
|
74
|
+
arcs.append(series_arc)
|
|
86
75
|
arc = series_arc
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if
|
|
92
|
-
arcs.append(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
76
|
+
if volume_arc := self._get_group_arc(book, "volume"):
|
|
77
|
+
arcs.append(volume_arc)
|
|
78
|
+
if not arc:
|
|
79
|
+
arc = volume_arc
|
|
80
|
+
if folder_arc := self._get_folder_arc(book):
|
|
81
|
+
arcs.append(folder_arc)
|
|
82
|
+
if top_group == FOLDER_GROUP:
|
|
83
|
+
arc = folder_arc
|
|
84
|
+
if story_arcs := self._get_story_arcs(book):
|
|
85
|
+
arcs = [*arcs, *story_arcs]
|
|
86
|
+
if top_group == STORY_ARC_GROUP:
|
|
87
|
+
arc = story_arcs[0]
|
|
97
88
|
|
|
98
89
|
max_mtime = None
|
|
99
90
|
for arc in arcs:
|
codex/views/reader/books.py
CHANGED
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
from django.db.models import F
|
|
5
|
+
from django.db.models import F
|
|
6
6
|
|
|
7
7
|
from codex.models import Bookmark, Comic
|
|
8
8
|
from codex.views.bookmark import BookmarkBaseView
|
|
9
|
-
from codex.views.const import FOLDER_GROUP
|
|
9
|
+
from codex.views.const import FOLDER_GROUP, NONE_INTEGERFIELD
|
|
10
10
|
from codex.views.mixins import SharedAnnotationsMixin
|
|
11
|
-
from codex.views.
|
|
11
|
+
from codex.views.reader.init import ReaderInitView
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from collections.abc import Mapping
|
|
15
15
|
|
|
16
|
-
_MIN_CRUMB_WALKBACK_LEN = 3
|
|
17
16
|
_SETTINGS_ATTRS = ("fit_to", "two_pages", "reading_direction")
|
|
18
17
|
_COMIC_FIELDS = (
|
|
19
18
|
"file_type",
|
|
@@ -25,60 +24,40 @@ _COMIC_FIELDS = (
|
|
|
25
24
|
"reading_direction",
|
|
26
25
|
"updated_at",
|
|
27
26
|
)
|
|
28
|
-
_SHOW = {
|
|
29
|
-
"series": True,
|
|
30
|
-
"volume": True,
|
|
31
|
-
}
|
|
32
27
|
|
|
33
28
|
|
|
34
|
-
class ReaderBooksView(BookmarkBaseView,
|
|
29
|
+
class ReaderBooksView(BookmarkBaseView, ReaderInitView, SharedAnnotationsMixin):
|
|
35
30
|
"""Get Books methods."""
|
|
36
31
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
if self.series_pks:
|
|
40
|
-
return self.series_pks
|
|
41
|
-
breadcrumbs = self.get_from_session(
|
|
42
|
-
"breadcrumbs", session_key=self.BROWSER_SESSION_KEY
|
|
43
|
-
)
|
|
44
|
-
if breadcrumbs:
|
|
45
|
-
crumb = breadcrumbs[-1]
|
|
46
|
-
crumb_group = crumb.get("group")
|
|
47
|
-
if crumb_group == "v" and len(breadcrumbs) >= _MIN_CRUMB_WALKBACK_LEN:
|
|
48
|
-
crumb = breadcrumbs[-2]
|
|
49
|
-
crumb_group = crumb.get("group")
|
|
50
|
-
if crumb_group == "s":
|
|
51
|
-
self.series_pks = crumb.get("pks", ())
|
|
52
|
-
|
|
53
|
-
return self.series_pks
|
|
54
|
-
|
|
55
|
-
def _get_reader_arc_pks(
|
|
56
|
-
self, arc, arc_pk_select_related, prefetch_related, arc_pk_rel
|
|
32
|
+
def _get_reader_arc_pks( # noqa: PLR0913
|
|
33
|
+
self, arc, arc_pk_select_related, prefetch_related, arc_pk_rel, arc_group
|
|
57
34
|
):
|
|
58
35
|
"""Get the nav filter."""
|
|
59
|
-
arc_pks
|
|
36
|
+
if arc_pks := arc.get("pks", ()):
|
|
37
|
+
return arc_pks
|
|
38
|
+
|
|
39
|
+
comic_pk = self.kwargs["pk"]
|
|
40
|
+
try:
|
|
41
|
+
arc_pk_qs = Comic.objects.filter(pk=comic_pk)
|
|
42
|
+
arc_pk_qs = arc_pk_qs.select_related(*arc_pk_select_related)
|
|
43
|
+
arc_pk_qs = arc_pk_qs.prefetch_related(*prefetch_related)
|
|
44
|
+
arc_pk_qs = arc_pk_qs.values_list(arc_pk_rel, flat=True)
|
|
45
|
+
arc_pk = arc_pk_qs.first()
|
|
46
|
+
except IndexError:
|
|
47
|
+
arc_pk = 0
|
|
48
|
+
|
|
49
|
+
_, multi_arc_pks = self.get_group_pks_from_breadcrumbs([arc_group])
|
|
50
|
+
if not arc_pk or arc_pk in multi_arc_pks:
|
|
51
|
+
arc_pks = multi_arc_pks
|
|
52
|
+
|
|
60
53
|
if not arc_pks:
|
|
61
|
-
|
|
62
|
-
try:
|
|
63
|
-
arc_pk_qs = Comic.objects.filter(pk=comic_pk)
|
|
64
|
-
arc_pk_qs = arc_pk_qs.select_related(*arc_pk_select_related)
|
|
65
|
-
arc_pk_qs = arc_pk_qs.prefetch_related(*prefetch_related)
|
|
66
|
-
arc_pk = arc_pk_qs.values_list(arc_pk_rel, flat=True)[0]
|
|
67
|
-
except IndexError:
|
|
68
|
-
arc_pk = 0
|
|
69
|
-
|
|
70
|
-
if arc_pk_rel == "series__pk":
|
|
71
|
-
multi_arc_pks = self.get_series_pks_from_breadcrumbs()
|
|
72
|
-
if not arc_pk or arc_pk in multi_arc_pks:
|
|
73
|
-
arc_pks = multi_arc_pks
|
|
74
|
-
if not arc_pks:
|
|
75
|
-
arc_pks = (arc_pk,)
|
|
54
|
+
arc_pks = (arc_pk,)
|
|
76
55
|
|
|
77
56
|
return arc_pks
|
|
78
57
|
|
|
79
58
|
def _get_comics_list(self):
|
|
80
59
|
"""Get the reader naviation group filter."""
|
|
81
|
-
select_related = ("series",)
|
|
60
|
+
select_related = ("series", "volume")
|
|
82
61
|
prefetch_related = ()
|
|
83
62
|
|
|
84
63
|
arc: Mapping = self.params.get("arc", {}) # type: ignore
|
|
@@ -91,31 +70,35 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
|
|
|
91
70
|
arc_pk_rel = "story_arc_numbers__story_arc__pk"
|
|
92
71
|
prefetch_related = (*prefetch_related, "story_arc_numbers__story_arc")
|
|
93
72
|
arc_index = F("story_arc_numbers__number")
|
|
94
|
-
ordering = ("arc_index", "date")
|
|
73
|
+
ordering = ("arc_index", "date", "pk")
|
|
95
74
|
arc_pk_select_related = ()
|
|
96
75
|
elif arc_group == FOLDER_GROUP:
|
|
97
76
|
# folder mode
|
|
98
77
|
rel = "parent_folder"
|
|
99
78
|
fields = (*_COMIC_FIELDS, "parent_folder")
|
|
100
79
|
arc_pk_rel = "parent_folder__pk"
|
|
101
|
-
|
|
102
|
-
arc_index = Value(None, IntegerField())
|
|
80
|
+
arc_index = NONE_INTEGERFIELD
|
|
103
81
|
ordering = ("path", "pk")
|
|
104
82
|
arc_pk_select_related = ("parent_folder",)
|
|
83
|
+
elif arc_group == "v":
|
|
84
|
+
# volume
|
|
85
|
+
rel = "volume"
|
|
86
|
+
fields = _COMIC_FIELDS
|
|
87
|
+
arc_pk_rel = "volume__pk"
|
|
88
|
+
arc_index = NONE_INTEGERFIELD
|
|
89
|
+
ordering = ()
|
|
90
|
+
arc_pk_select_related = ("volume",)
|
|
105
91
|
else:
|
|
106
|
-
#
|
|
92
|
+
# series
|
|
107
93
|
rel = "series"
|
|
108
94
|
fields = _COMIC_FIELDS
|
|
109
95
|
arc_pk_rel = "series__pk"
|
|
110
|
-
arc_index =
|
|
96
|
+
arc_index = NONE_INTEGERFIELD
|
|
111
97
|
ordering = ()
|
|
112
98
|
arc_pk_select_related = ("series",)
|
|
113
99
|
|
|
114
100
|
arc_pks = self._get_reader_arc_pks(
|
|
115
|
-
arc,
|
|
116
|
-
arc_pk_select_related,
|
|
117
|
-
prefetch_related,
|
|
118
|
-
arc_pk_rel,
|
|
101
|
+
arc, arc_pk_select_related, prefetch_related, arc_pk_rel, arc_group
|
|
119
102
|
)
|
|
120
103
|
nav_filter = {f"{rel}__in": arc_pks}
|
|
121
104
|
group_acl_filter = self.get_group_acl_filter(Comic)
|
|
@@ -136,9 +119,11 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
|
|
|
136
119
|
.annotate(mtime=F("updated_at"))
|
|
137
120
|
)
|
|
138
121
|
qs = self.annotate_group_names(qs, Comic)
|
|
139
|
-
if arc_group
|
|
122
|
+
if arc_group in ("v", "s"):
|
|
123
|
+
show = self.params["show"]
|
|
124
|
+
model_group = "i" if arc_group == "s" else "s"
|
|
140
125
|
qs, comic_sort_names = self.alias_sort_names(
|
|
141
|
-
qs, Comic, pks=arc_pks, model_group=
|
|
126
|
+
qs, Comic, pks=arc_pks, model_group=model_group, show=show
|
|
142
127
|
)
|
|
143
128
|
ordering = (
|
|
144
129
|
*comic_sort_names,
|
|
@@ -146,7 +131,8 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
|
|
|
146
131
|
"issue_suffix",
|
|
147
132
|
"sort_name",
|
|
148
133
|
)
|
|
149
|
-
|
|
134
|
+
qs = qs.order_by(*ordering)
|
|
135
|
+
return qs, arc_group
|
|
150
136
|
|
|
151
137
|
def _append_with_settings(self, book, bookmark_filter):
|
|
152
138
|
"""Append bookmark to book list."""
|
|
@@ -183,9 +169,9 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
|
|
|
183
169
|
prev_book, bookmark_filter
|
|
184
170
|
)
|
|
185
171
|
# create extra current book attrs:
|
|
186
|
-
if book.arc_index is None:
|
|
187
|
-
book.arc_index = index + 1
|
|
188
|
-
book.filename = book.get_filename()
|
|
172
|
+
if book.arc_index is None:
|
|
173
|
+
book.arc_index = index + 1
|
|
174
|
+
book.filename = book.get_filename()
|
|
189
175
|
book.arc_group = arc_group
|
|
190
176
|
book.arc_count = comics.count()
|
|
191
177
|
books["current"] = self._append_with_settings(book, bookmark_filter)
|
|
@@ -193,5 +179,3 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
|
|
|
193
179
|
# Haven't matched yet, so set the previous comic
|
|
194
180
|
prev_book = book
|
|
195
181
|
return books
|
|
196
|
-
|
|
197
|
-
#####
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Views for reading comic books."""
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from types import MappingProxyType
|
|
5
|
+
|
|
6
|
+
from codex.logger.logging import get_logger
|
|
7
|
+
from codex.models import Comic
|
|
8
|
+
from codex.serializers.reader import (
|
|
9
|
+
ReaderViewInputSerializer,
|
|
10
|
+
)
|
|
11
|
+
from codex.views.const import FOLDER_GROUP, GROUP_RELATION, STORY_ARC_GROUP
|
|
12
|
+
from codex.views.session import SessionView
|
|
13
|
+
from codex.views.util import reparse_json_query_params
|
|
14
|
+
|
|
15
|
+
LOG = get_logger(__name__)
|
|
16
|
+
VALID_ARC_GROUPS = frozenset({"s", "v", "f", "a"})
|
|
17
|
+
_JSON_KEYS = frozenset({"arc", "breadcrumbs", "show"})
|
|
18
|
+
_BROWSER_SESSION_DEFAULTS = SessionView.SESSION_DEFAULTS[
|
|
19
|
+
SessionView.BROWSER_SESSION_KEY
|
|
20
|
+
]
|
|
21
|
+
_DEFAULT_PARAMS = {
|
|
22
|
+
"breadcrumbs": _BROWSER_SESSION_DEFAULTS["breadcrumbs"],
|
|
23
|
+
"show": _BROWSER_SESSION_DEFAULTS["show"],
|
|
24
|
+
"top_group": _BROWSER_SESSION_DEFAULTS["top_group"],
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ReaderInitView(SessionView):
|
|
29
|
+
"""Reader initialization."""
|
|
30
|
+
|
|
31
|
+
input_serializer_class = ReaderViewInputSerializer
|
|
32
|
+
|
|
33
|
+
def __init__(self, *args, **kwargs):
|
|
34
|
+
"""Initialize instance vars."""
|
|
35
|
+
super().__init__(*args, **kwargs)
|
|
36
|
+
self.group_pks: dict[str, tuple[int, ...]] = {}
|
|
37
|
+
|
|
38
|
+
def _set_group_pks_from_breadcrumbs(self, groups, breadcrumbs):
|
|
39
|
+
"""Set Multi-Group pks from breadcrumbs to the cache."""
|
|
40
|
+
index = len(breadcrumbs) - 1
|
|
41
|
+
min_index = max(index - 1, 0) if FOLDER_GROUP in groups else 0
|
|
42
|
+
while index > min_index:
|
|
43
|
+
crumb = breadcrumbs[index]
|
|
44
|
+
crumb_group = crumb.get("group")
|
|
45
|
+
if crumb_group in groups:
|
|
46
|
+
pks = crumb.get("pks", ())
|
|
47
|
+
self.group_pks[crumb_group] = pks
|
|
48
|
+
break
|
|
49
|
+
index -= 1
|
|
50
|
+
else:
|
|
51
|
+
crumb_group = ""
|
|
52
|
+
pks = ()
|
|
53
|
+
for group in groups:
|
|
54
|
+
self.group_pks[group] = pks
|
|
55
|
+
return crumb_group, pks
|
|
56
|
+
|
|
57
|
+
def get_group_pks_from_breadcrumbs(self, groups):
|
|
58
|
+
"""Get Multi-Group pks from the breadcrumbs."""
|
|
59
|
+
# Return cached values
|
|
60
|
+
for group in groups:
|
|
61
|
+
if pks := self.group_pks.get(group):
|
|
62
|
+
return group, pks
|
|
63
|
+
|
|
64
|
+
# Validate input
|
|
65
|
+
breadcrumbs: tuple = self.params.get("breadcrumbs") # type: ignore
|
|
66
|
+
if not breadcrumbs:
|
|
67
|
+
return "", ()
|
|
68
|
+
|
|
69
|
+
return self._set_group_pks_from_breadcrumbs(groups, breadcrumbs)
|
|
70
|
+
|
|
71
|
+
def _ensure_arc_contains_comic(self, params):
|
|
72
|
+
"""Arc sanity check."""
|
|
73
|
+
arc = params.get("arc")
|
|
74
|
+
if not arc:
|
|
75
|
+
return
|
|
76
|
+
pk = self.kwargs["pk"]
|
|
77
|
+
arc_group = arc.get("group")
|
|
78
|
+
arc_pks = arc.get("pks")
|
|
79
|
+
valid = False
|
|
80
|
+
if rel := GROUP_RELATION.get(arc_group):
|
|
81
|
+
arc_filter = {rel: arc_pks}
|
|
82
|
+
valid = Comic.objects.filter(pk=pk, **arc_filter).exists()
|
|
83
|
+
if not valid:
|
|
84
|
+
LOG.warning(f"Invalid arc {arc} submitted to reader for comic {pk}.")
|
|
85
|
+
params.pop("arc", None)
|
|
86
|
+
|
|
87
|
+
def _ensure_arc(self, params):
|
|
88
|
+
"""arc.group validation."""
|
|
89
|
+
# Can't be in the serializer
|
|
90
|
+
arc = params.get("arc", {})
|
|
91
|
+
|
|
92
|
+
if arc.get("group") not in VALID_ARC_GROUPS:
|
|
93
|
+
top_group = params["top_group"]
|
|
94
|
+
if top_group in (FOLDER_GROUP, STORY_ARC_GROUP):
|
|
95
|
+
search_groups = (top_group,)
|
|
96
|
+
else:
|
|
97
|
+
search_groups = ("v", "s")
|
|
98
|
+
|
|
99
|
+
group, pks = self.get_group_pks_from_breadcrumbs(search_groups)
|
|
100
|
+
if not group:
|
|
101
|
+
group = "s"
|
|
102
|
+
|
|
103
|
+
arc["group"] = group
|
|
104
|
+
if pks:
|
|
105
|
+
arc["pks"] = pks
|
|
106
|
+
|
|
107
|
+
params["arc"] = arc
|
|
108
|
+
|
|
109
|
+
def _parse_params(self):
|
|
110
|
+
data = self.request.GET
|
|
111
|
+
data = reparse_json_query_params(self.request.GET, _JSON_KEYS)
|
|
112
|
+
serializer = self.input_serializer_class(data=data)
|
|
113
|
+
serializer.is_valid(raise_exception=True)
|
|
114
|
+
|
|
115
|
+
params = deepcopy(_DEFAULT_PARAMS)
|
|
116
|
+
if serializer.validated_data:
|
|
117
|
+
params.update(serializer.validated_data) # type: ignore
|
|
118
|
+
self._ensure_arc_contains_comic(params)
|
|
119
|
+
self._ensure_arc(params)
|
|
120
|
+
|
|
121
|
+
self.params = MappingProxyType(params)
|
codex/views/reader/reader.py
CHANGED
|
@@ -1,43 +1,30 @@
|
|
|
1
1
|
"""Views for reading comic books."""
|
|
2
2
|
|
|
3
|
-
from types import MappingProxyType
|
|
4
|
-
|
|
5
3
|
from comicbox.box import Comicbox
|
|
6
4
|
from django.urls import reverse
|
|
7
|
-
from drf_spectacular.
|
|
8
|
-
from
|
|
9
|
-
from rest_framework.exceptions import NotFound, ValidationError
|
|
5
|
+
from drf_spectacular.utils import extend_schema
|
|
6
|
+
from rest_framework.exceptions import NotFound
|
|
10
7
|
from rest_framework.response import Response
|
|
11
8
|
|
|
12
9
|
from codex.librarian.importer.tasks import LazyImportComicsTask
|
|
13
10
|
from codex.librarian.mp_queue import LIBRARIAN_QUEUE
|
|
14
11
|
from codex.logger.logging import get_logger
|
|
15
|
-
from codex.serializers.reader import
|
|
12
|
+
from codex.serializers.reader import ReaderComicsSerializer, ReaderViewInputSerializer
|
|
16
13
|
from codex.serializers.redirect import ReaderRedirectSerializer
|
|
17
14
|
from codex.views.reader.arcs import ReaderArcsView
|
|
18
|
-
from codex.views.util import pop_name
|
|
15
|
+
from codex.views.util import pop_name
|
|
19
16
|
|
|
20
17
|
LOG = get_logger(__name__)
|
|
21
|
-
_VALID_ARC_GROUPS = frozenset({"f", "s", "a"})
|
|
22
|
-
_JSON_KEYS = frozenset({"arc"})
|
|
23
18
|
|
|
24
19
|
|
|
25
|
-
class ReaderView(
|
|
26
|
-
ReaderArcsView,
|
|
27
|
-
):
|
|
20
|
+
class ReaderView(ReaderArcsView):
|
|
28
21
|
"""Get info for displaying comic pages."""
|
|
29
22
|
|
|
30
23
|
serializer_class = ReaderComicsSerializer
|
|
31
|
-
input_serializer_class = ReaderArcSerializer
|
|
32
24
|
|
|
33
25
|
SESSION_KEY = "reader"
|
|
34
26
|
TARGET = "reader"
|
|
35
27
|
|
|
36
|
-
def __init__(self, *args, **kwargs):
|
|
37
|
-
"""Initialize instance vars."""
|
|
38
|
-
super().__init__(*args, **kwargs)
|
|
39
|
-
self.series_pks: tuple[int, ...] = ()
|
|
40
|
-
|
|
41
28
|
def _raise_not_found(self):
|
|
42
29
|
"""Raise not found exception."""
|
|
43
30
|
pk = self.kwargs.get("pk")
|
|
@@ -48,48 +35,6 @@ class ReaderView(
|
|
|
48
35
|
}
|
|
49
36
|
raise NotFound(detail=detail)
|
|
50
37
|
|
|
51
|
-
def _validate_params(self, arc, top_group):
|
|
52
|
-
"""arc.group validation."""
|
|
53
|
-
# Can't be in the serializer
|
|
54
|
-
arc_group = arc.get("group", "")
|
|
55
|
-
if not arc_group:
|
|
56
|
-
series_pks = self.get_series_pks_from_breadcrumbs()
|
|
57
|
-
if series_pks:
|
|
58
|
-
arc_group = "s"
|
|
59
|
-
arc["pks"] = series_pks
|
|
60
|
-
if not arc_group:
|
|
61
|
-
arc_group = top_group
|
|
62
|
-
if arc_group not in _VALID_ARC_GROUPS:
|
|
63
|
-
arc_group = None
|
|
64
|
-
|
|
65
|
-
def _parse_params(self):
|
|
66
|
-
data = self.request.GET
|
|
67
|
-
arc = {}
|
|
68
|
-
data = reparse_json_query_params(self.request.GET, _JSON_KEYS)
|
|
69
|
-
arc_data = data.get("arc", [])
|
|
70
|
-
if arc_data:
|
|
71
|
-
serializer = self.input_serializer_class(data=arc_data)
|
|
72
|
-
try:
|
|
73
|
-
serializer.is_valid()
|
|
74
|
-
arc: dict = serializer.validated_data # type: ignore
|
|
75
|
-
except ValidationError:
|
|
76
|
-
pass
|
|
77
|
-
|
|
78
|
-
# From Browser Session
|
|
79
|
-
top_group = self.get_from_session(
|
|
80
|
-
"top_group", "s", session_key=self.BROWSER_SESSION_KEY
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
self._validate_params(arc, top_group)
|
|
84
|
-
|
|
85
|
-
params = MappingProxyType(
|
|
86
|
-
{
|
|
87
|
-
"arc": arc,
|
|
88
|
-
"top_group": top_group,
|
|
89
|
-
}
|
|
90
|
-
)
|
|
91
|
-
self.params = params
|
|
92
|
-
|
|
93
38
|
@staticmethod
|
|
94
39
|
def _lazy_metadata(current, prev_book, next_book):
|
|
95
40
|
"""Get reader metadata from comicbox if it's not in the db."""
|
|
@@ -117,7 +62,6 @@ class ReaderView(
|
|
|
117
62
|
def get_object(self):
|
|
118
63
|
"""Get the previous and next comics in a group or story arc."""
|
|
119
64
|
# Books
|
|
120
|
-
self.last_route = self.get_last_route()
|
|
121
65
|
books = self.get_book_collection()
|
|
122
66
|
|
|
123
67
|
current = books.get("current")
|
|
@@ -144,7 +88,9 @@ class ReaderView(
|
|
|
144
88
|
arc["pks"] = (current.arc_pk,) # type: ignore
|
|
145
89
|
arc["index"] = current.arc_index # type:ignore
|
|
146
90
|
arc["count"] = current.arc_count # type: ignore
|
|
147
|
-
|
|
91
|
+
|
|
92
|
+
close_route = self.get_last_route()
|
|
93
|
+
close_route = pop_name(close_route)
|
|
148
94
|
|
|
149
95
|
return {
|
|
150
96
|
"books": books,
|
|
@@ -154,14 +100,7 @@ class ReaderView(
|
|
|
154
100
|
"mtime": mtime,
|
|
155
101
|
}
|
|
156
102
|
|
|
157
|
-
@extend_schema(
|
|
158
|
-
parameters=[
|
|
159
|
-
OpenApiParameter(
|
|
160
|
-
"arc[group]", OpenApiTypes.STR, enum=sorted(_VALID_ARC_GROUPS)
|
|
161
|
-
),
|
|
162
|
-
OpenApiParameter("arc[pks]", OpenApiTypes.STR),
|
|
163
|
-
]
|
|
164
|
-
)
|
|
103
|
+
@extend_schema(parameters=[ReaderViewInputSerializer])
|
|
165
104
|
def get(self, *args, **kwargs):
|
|
166
105
|
"""Get the book info."""
|
|
167
106
|
self._parse_params()
|
codex/views/session.py
CHANGED
|
@@ -102,17 +102,6 @@ class SessionView(AuthFilterGenericAPIView, ABC):
|
|
|
102
102
|
|
|
103
103
|
return last_route
|
|
104
104
|
|
|
105
|
-
def load_params_from_session(self, validated_data=None, session_key=None):
|
|
106
|
-
"""Load params from the session with defaults."""
|
|
107
|
-
session_key = session_key if session_key else self.SESSION_KEY
|
|
108
|
-
params = deepcopy(dict(self.SESSION_DEFAULTS[session_key]))
|
|
109
|
-
session_params = self.request.session.get(session_key)
|
|
110
|
-
if session_params:
|
|
111
|
-
params.update(session_params)
|
|
112
|
-
if validated_data:
|
|
113
|
-
params.update(validated_data)
|
|
114
|
-
self.params = MappingProxyType(params)
|
|
115
|
-
|
|
116
105
|
@classmethod
|
|
117
106
|
def _get_source_values_or_set_defaults(cls, defaults_dict, source_dict, data):
|
|
118
107
|
"""Recursively copy source_dict values into data or use defaults."""
|
|
@@ -128,6 +117,21 @@ class SessionView(AuthFilterGenericAPIView, ABC):
|
|
|
128
117
|
)
|
|
129
118
|
return result
|
|
130
119
|
|
|
120
|
+
def load_params_from_session(self, session_key=None, only=None):
|
|
121
|
+
"""Get session settings with defaults."""
|
|
122
|
+
if not session_key:
|
|
123
|
+
session_key = self.SESSION_KEY
|
|
124
|
+
if only:
|
|
125
|
+
defaults = {}
|
|
126
|
+
for key in only:
|
|
127
|
+
if key:
|
|
128
|
+
defaults[key] = self.SESSION_DEFAULTS[session_key][key]
|
|
129
|
+
else:
|
|
130
|
+
defaults = self.SESSION_DEFAULTS[session_key]
|
|
131
|
+
|
|
132
|
+
session = self.request.session.get(session_key, defaults)
|
|
133
|
+
return self._get_source_values_or_set_defaults(defaults, session, {})
|
|
134
|
+
|
|
131
135
|
def save_params_to_session(self, params): # reader session & browser final
|
|
132
136
|
"""Save the session from params with defaults for missing values."""
|
|
133
137
|
try:
|