codex 1.6.2__py3-none-any.whl → 1.6.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of codex might be problematic. Click here for more details.
- codex/exceptions.py +42 -16
- codex/librarian/importer/importer.py +1 -1
- codex/librarian/janitor/cleanup.py +2 -4
- codex/librarian/janitor/update.py +10 -9
- codex/librarian/search/merge.py +37 -40
- codex/librarian/search/remove.py +30 -26
- codex/librarian/search/update.py +47 -38
- codex/librarian/stats.py +177 -0
- codex/librarian/watchdog/events.py +2 -2
- codex/serializers/admin/__init__.py +1 -0
- codex/serializers/admin/flags.py +15 -0
- codex/serializers/admin/groups.py +38 -0
- codex/serializers/admin/libraries.py +90 -0
- codex/serializers/admin/stats.py +126 -0
- codex/serializers/admin/tasks.py +16 -0
- codex/serializers/admin/users.py +40 -0
- codex/serializers/fields.py +30 -0
- codex/serializers/reader.py +8 -3
- codex/settings/settings.py +3 -3
- codex/static_root/assets/VCheckbox-DtXDauZz.54986ad416a1.js +1 -0
- codex/static_root/assets/VCheckbox-DtXDauZz.54986ad416a1.js.br +0 -0
- codex/static_root/assets/VCheckbox-DtXDauZz.54986ad416a1.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DtXDauZz.js +1 -0
- codex/static_root/assets/VCheckbox-DtXDauZz.js.br +0 -0
- codex/static_root/assets/VCheckbox-DtXDauZz.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-B-iT1MnG.8505f6eed0fe.js +1 -0
- codex/static_root/assets/VCheckboxBtn-B-iT1MnG.8505f6eed0fe.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B-iT1MnG.8505f6eed0fe.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-B-iT1MnG.js +1 -0
- codex/static_root/assets/VCheckboxBtn-B-iT1MnG.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B-iT1MnG.js.gz +0 -0
- codex/static_root/assets/{VCombobox-CGyBgdqr.2f85598cc1a4.js → VCombobox-DZmum3X-.330883d59742.js} +1 -1
- codex/static_root/assets/VCombobox-DZmum3X-.330883d59742.js.br +0 -0
- codex/static_root/assets/VCombobox-DZmum3X-.330883d59742.js.gz +0 -0
- codex/static_root/assets/{VCombobox-CGyBgdqr.js → VCombobox-DZmum3X-.js} +1 -1
- codex/static_root/assets/VCombobox-DZmum3X-.js.br +0 -0
- codex/static_root/assets/VCombobox-DZmum3X-.js.gz +0 -0
- codex/static_root/assets/VDialog-DUdNpXLj.6088d518d3af.js +1 -0
- codex/static_root/assets/VDialog-DUdNpXLj.6088d518d3af.js.br +0 -0
- codex/static_root/assets/VDialog-DUdNpXLj.6088d518d3af.js.gz +0 -0
- codex/static_root/assets/VDialog-DUdNpXLj.js +1 -0
- codex/static_root/assets/VDialog-DUdNpXLj.js.br +0 -0
- codex/static_root/assets/VDialog-DUdNpXLj.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CurqcyFg.860017abf25d.js +1 -0
- codex/static_root/assets/VExpansionPanels-CurqcyFg.860017abf25d.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CurqcyFg.860017abf25d.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CurqcyFg.js +1 -0
- codex/static_root/assets/VExpansionPanels-CurqcyFg.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CurqcyFg.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-B7n5ReZ-.8acbc71ac411.js +1 -0
- codex/static_root/assets/VRadioGroup-B7n5ReZ-.8acbc71ac411.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B7n5ReZ-.8acbc71ac411.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-B7n5ReZ-.js +1 -0
- codex/static_root/assets/VRadioGroup-B7n5ReZ-.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B7n5ReZ-.js.gz +0 -0
- codex/static_root/assets/VSelect-FYWbH55D.83e91b93fdd1.js +1 -0
- codex/static_root/assets/VSelect-FYWbH55D.83e91b93fdd1.js.br +0 -0
- codex/static_root/assets/VSelect-FYWbH55D.83e91b93fdd1.js.gz +0 -0
- codex/static_root/assets/VSelect-FYWbH55D.js +1 -0
- codex/static_root/assets/VSelect-FYWbH55D.js.br +0 -0
- codex/static_root/assets/VSelect-FYWbH55D.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-DXxRmssH.js → VSelectionControl-CUlT3cc_.63f60240eb49.js} +1 -1
- codex/static_root/assets/VSelectionControl-CUlT3cc_.63f60240eb49.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CUlT3cc_.63f60240eb49.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-DXxRmssH.252890f79cc6.js → VSelectionControl-CUlT3cc_.js} +1 -1
- codex/static_root/assets/VSelectionControl-CUlT3cc_.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CUlT3cc_.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-DYJ3EYZI.a2542c5f90c4.js → VSlideGroup-CRJazQeR.cb89b2222518.js} +1 -1
- codex/static_root/assets/VSlideGroup-CRJazQeR.cb89b2222518.js.br +0 -0
- codex/static_root/assets/VSlideGroup-CRJazQeR.cb89b2222518.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-DYJ3EYZI.js → VSlideGroup-CRJazQeR.js} +1 -1
- codex/static_root/assets/VSlideGroup-CRJazQeR.js.br +0 -0
- codex/static_root/assets/VSlideGroup-CRJazQeR.js.gz +0 -0
- codex/static_root/assets/{VTable-BveGtxei.7f76f4ef812b.js → VTable-DtyBEZ5i.6848f15ea03f.js} +1 -1
- codex/static_root/assets/VTable-DtyBEZ5i.6848f15ea03f.js.br +0 -0
- codex/static_root/assets/VTable-DtyBEZ5i.6848f15ea03f.js.gz +0 -0
- codex/static_root/assets/{VTable-BveGtxei.js → VTable-DtyBEZ5i.js} +1 -1
- codex/static_root/assets/VTable-DtyBEZ5i.js.br +0 -0
- codex/static_root/assets/VTable-DtyBEZ5i.js.gz +0 -0
- codex/static_root/assets/VTextField-DEiVah7O.fbf3130f2dea.js +1 -0
- codex/static_root/assets/VTextField-DEiVah7O.fbf3130f2dea.js.br +0 -0
- codex/static_root/assets/VTextField-DEiVah7O.fbf3130f2dea.js.gz +0 -0
- codex/static_root/assets/VTextField-DEiVah7O.js +1 -0
- codex/static_root/assets/VTextField-DEiVah7O.js.br +0 -0
- codex/static_root/assets/VTextField-DEiVah7O.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-FBuIO_rT.8610c7e83905.js → VWindowItem-BVIaHO-S.419e4ecf523d.js} +1 -1
- codex/static_root/assets/VWindowItem-BVIaHO-S.419e4ecf523d.js.br +0 -0
- codex/static_root/assets/VWindowItem-BVIaHO-S.419e4ecf523d.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-FBuIO_rT.js → VWindowItem-BVIaHO-S.js} +1 -1
- codex/static_root/assets/VWindowItem-BVIaHO-S.js.br +0 -0
- codex/static_root/assets/VWindowItem-BVIaHO-S.js.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.82c98714bcf1.css → admin-D4sw3N4T.b590f88a62cf.css} +1 -1
- codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.br +0 -0
- codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.css → admin-D4sw3N4T.css} +1 -1
- codex/static_root/assets/admin-D4sw3N4T.css.br +0 -0
- codex/static_root/assets/admin-D4sw3N4T.css.gz +0 -0
- codex/static_root/assets/admin-S53JleJt.f629d90f73a1.js +1 -0
- codex/static_root/assets/admin-S53JleJt.f629d90f73a1.js.br +0 -0
- codex/static_root/assets/admin-S53JleJt.f629d90f73a1.js.gz +0 -0
- codex/static_root/assets/admin-S53JleJt.js +1 -0
- codex/static_root/assets/admin-S53JleJt.js.br +0 -0
- codex/static_root/assets/admin-S53JleJt.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-CnAnGx7w.1e4eb021cb21.js +56 -0
- codex/static_root/assets/admin-drawer-panel-CnAnGx7w.1e4eb021cb21.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-CnAnGx7w.1e4eb021cb21.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-CnAnGx7w.js +56 -0
- codex/static_root/assets/admin-drawer-panel-CnAnGx7w.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-CnAnGx7w.js.gz +0 -0
- codex/static_root/assets/browser-BHHgqDP7.7ada4400d63f.js +1 -0
- codex/static_root/assets/browser-BHHgqDP7.7ada4400d63f.js.br +0 -0
- codex/static_root/assets/browser-BHHgqDP7.7ada4400d63f.js.gz +0 -0
- codex/static_root/assets/browser-BHHgqDP7.js +1 -0
- codex/static_root/assets/browser-BHHgqDP7.js.br +0 -0
- codex/static_root/assets/browser-BHHgqDP7.js.gz +0 -0
- codex/static_root/assets/{browser-DP9eEJ0M.css → browser-Cy0OgQJC.css} +1 -1
- codex/static_root/assets/browser-Cy0OgQJC.css.br +0 -0
- codex/static_root/assets/browser-Cy0OgQJC.css.gz +0 -0
- codex/static_root/assets/{browser-DP9eEJ0M.276e8636f8fc.css → browser-Cy0OgQJC.fabbd1aaffcf.css} +1 -1
- codex/static_root/assets/browser-Cy0OgQJC.fabbd1aaffcf.css.br +0 -0
- codex/static_root/assets/browser-Cy0OgQJC.fabbd1aaffcf.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Du1bwEt1.a4c0205c9bc4.js +1 -0
- codex/static_root/assets/change-password-dialog-Du1bwEt1.a4c0205c9bc4.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Du1bwEt1.a4c0205c9bc4.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Du1bwEt1.js +1 -0
- codex/static_root/assets/change-password-dialog-Du1bwEt1.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Du1bwEt1.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-eKN71B8a.af6187dc12bd.js +1 -0
- codex/static_root/assets/confirm-dialog-eKN71B8a.af6187dc12bd.js.br +0 -0
- codex/static_root/assets/confirm-dialog-eKN71B8a.af6187dc12bd.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-eKN71B8a.js +1 -0
- codex/static_root/assets/confirm-dialog-eKN71B8a.js.br +0 -0
- codex/static_root/assets/confirm-dialog-eKN71B8a.js.gz +0 -0
- codex/static_root/assets/{datetime-column-uaXWFgF_.29c6d50b483e.js → datetime-column-CWp600fz.e8a969126bf5.js} +1 -1
- codex/static_root/assets/datetime-column-CWp600fz.e8a969126bf5.js.br +0 -0
- codex/static_root/assets/datetime-column-CWp600fz.e8a969126bf5.js.gz +0 -0
- codex/static_root/assets/{datetime-column-uaXWFgF_.js → datetime-column-CWp600fz.js} +1 -1
- codex/static_root/assets/datetime-column-CWp600fz.js.br +0 -0
- codex/static_root/assets/datetime-column-CWp600fz.js.gz +0 -0
- codex/static_root/assets/{filter-DS84uTvk.4fbebd4832bc.js → filter-_XbKRiIR.2c7ac68a2d03.js} +1 -1
- codex/static_root/assets/filter-_XbKRiIR.2c7ac68a2d03.js.br +0 -0
- codex/static_root/assets/filter-_XbKRiIR.2c7ac68a2d03.js.gz +0 -0
- codex/static_root/assets/{filter-DS84uTvk.js → filter-_XbKRiIR.js} +1 -1
- codex/static_root/assets/filter-_XbKRiIR.js.br +0 -0
- codex/static_root/assets/filter-_XbKRiIR.js.gz +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.css +1 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.css.br +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.css.gz +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css +1 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.br +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.gz +0 -0
- codex/static_root/assets/flag-tab-DR_nY0Jg.7323e386c6ed.js +1 -0
- codex/static_root/assets/flag-tab-DR_nY0Jg.7323e386c6ed.js.br +0 -0
- codex/static_root/assets/flag-tab-DR_nY0Jg.7323e386c6ed.js.gz +0 -0
- codex/static_root/assets/flag-tab-DR_nY0Jg.js +1 -0
- codex/static_root/assets/flag-tab-DR_nY0Jg.js.br +0 -0
- codex/static_root/assets/flag-tab-DR_nY0Jg.js.gz +0 -0
- codex/static_root/assets/group-tab-JKsMHOmK.d2c2f3fceec1.js +1 -0
- codex/static_root/assets/group-tab-JKsMHOmK.d2c2f3fceec1.js.br +0 -0
- codex/static_root/assets/group-tab-JKsMHOmK.d2c2f3fceec1.js.gz +0 -0
- codex/static_root/assets/group-tab-JKsMHOmK.js +1 -0
- codex/static_root/assets/group-tab-JKsMHOmK.js.br +0 -0
- codex/static_root/assets/group-tab-JKsMHOmK.js.gz +0 -0
- codex/static_root/assets/http-error-Bw1wUv3U.c8313a2fb129.js +1 -0
- codex/static_root/assets/http-error-Bw1wUv3U.c8313a2fb129.js.br +0 -0
- codex/static_root/assets/http-error-Bw1wUv3U.c8313a2fb129.js.gz +0 -0
- codex/static_root/assets/http-error-Bw1wUv3U.js +1 -0
- codex/static_root/assets/http-error-Bw1wUv3U.js.br +0 -0
- codex/static_root/assets/http-error-Bw1wUv3U.js.gz +0 -0
- codex/static_root/assets/{library-tab-BN0SfPcH.913e236f625e.css → library-tab-BygwYPnO.94e92aa60bf2.css} +1 -1
- codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.br +0 -0
- codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.gz +0 -0
- codex/static_root/assets/{library-tab-BN0SfPcH.css → library-tab-BygwYPnO.css} +1 -1
- codex/static_root/assets/library-tab-BygwYPnO.css.br +0 -0
- codex/static_root/assets/library-tab-BygwYPnO.css.gz +0 -0
- codex/static_root/assets/library-tab-D9GTugZ6.8e3e6ebd38b7.js +1 -0
- codex/static_root/assets/library-tab-D9GTugZ6.8e3e6ebd38b7.js.br +0 -0
- codex/static_root/assets/library-tab-D9GTugZ6.8e3e6ebd38b7.js.gz +0 -0
- codex/static_root/assets/library-tab-D9GTugZ6.js +1 -0
- codex/static_root/assets/library-tab-D9GTugZ6.js.br +0 -0
- codex/static_root/assets/library-tab-D9GTugZ6.js.gz +0 -0
- codex/static_root/assets/main-BJiV8IG3.9d708627091f.js +6 -0
- codex/static_root/assets/main-BJiV8IG3.9d708627091f.js.br +0 -0
- codex/static_root/assets/main-BJiV8IG3.9d708627091f.js.gz +0 -0
- codex/static_root/assets/main-BJiV8IG3.js +6 -0
- codex/static_root/assets/main-BJiV8IG3.js.br +0 -0
- codex/static_root/assets/main-BJiV8IG3.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-i2VoXaGH.c3dbb703af2e.js +1 -0
- codex/static_root/assets/pagination-toolbar-i2VoXaGH.c3dbb703af2e.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-i2VoXaGH.c3dbb703af2e.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-i2VoXaGH.js +1 -0
- codex/static_root/assets/pagination-toolbar-i2VoXaGH.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-i2VoXaGH.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-C-4EM9-d.ce9cf66bd4c3.js → pdf-doc-BLxZLD38.4cf9622c535f.js} +1 -1
- codex/static_root/assets/pdf-doc-BLxZLD38.4cf9622c535f.js.br +0 -0
- codex/static_root/assets/pdf-doc-BLxZLD38.4cf9622c535f.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-C-4EM9-d.js → pdf-doc-BLxZLD38.js} +1 -1
- codex/static_root/assets/pdf-doc-BLxZLD38.js.br +0 -0
- codex/static_root/assets/pdf-doc-BLxZLD38.js.gz +0 -0
- codex/static_root/assets/reader-D2O_yHTW.70d616ddc2cc.js +2 -0
- codex/static_root/assets/reader-D2O_yHTW.70d616ddc2cc.js.br +0 -0
- codex/static_root/assets/reader-D2O_yHTW.70d616ddc2cc.js.gz +0 -0
- codex/static_root/assets/reader-D2O_yHTW.js +2 -0
- codex/static_root/assets/reader-D2O_yHTW.js.br +0 -0
- codex/static_root/assets/reader-D2O_yHTW.js.gz +0 -0
- codex/static_root/assets/{reader-DOQuhDYO.css → reader-YbzUiD0T.css} +1 -1
- codex/static_root/assets/reader-YbzUiD0T.css.br +0 -0
- codex/static_root/assets/reader-YbzUiD0T.css.gz +0 -0
- codex/static_root/assets/{reader-DOQuhDYO.df559ad29ab3.css → reader-YbzUiD0T.efb15835b39d.css} +1 -1
- codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.br +0 -0
- codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.98e65384c3c9.css → relation-chips-BGitp3yx.b3e7d178a615.css} +1 -1
- codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.br +0 -0
- codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.css → relation-chips-BGitp3yx.css} +1 -1
- codex/static_root/assets/relation-chips-BGitp3yx.css.br +0 -0
- codex/static_root/assets/relation-chips-BGitp3yx.css.gz +0 -0
- codex/static_root/assets/relation-chips-CmeSIsRq.f169f7c3f48d.js +1 -0
- codex/static_root/assets/relation-chips-CmeSIsRq.f169f7c3f48d.js.br +0 -0
- codex/static_root/assets/relation-chips-CmeSIsRq.f169f7c3f48d.js.gz +0 -0
- codex/static_root/assets/relation-chips-CmeSIsRq.js +1 -0
- codex/static_root/assets/relation-chips-CmeSIsRq.js.br +0 -0
- codex/static_root/assets/relation-chips-CmeSIsRq.js.gz +0 -0
- codex/static_root/assets/settings-drawer-XH4sNDNT.98ee4b24901c.js +2 -0
- codex/static_root/assets/settings-drawer-XH4sNDNT.98ee4b24901c.js.br +0 -0
- codex/static_root/assets/settings-drawer-XH4sNDNT.98ee4b24901c.js.gz +0 -0
- codex/static_root/assets/settings-drawer-XH4sNDNT.js +2 -0
- codex/static_root/assets/settings-drawer-XH4sNDNT.js.br +0 -0
- codex/static_root/assets/settings-drawer-XH4sNDNT.js.gz +0 -0
- codex/static_root/assets/stats-tab-BGxkeKnY.44cf18ca895d.js +1 -0
- codex/static_root/assets/stats-tab-BGxkeKnY.44cf18ca895d.js.br +0 -0
- codex/static_root/assets/stats-tab-BGxkeKnY.44cf18ca895d.js.gz +0 -0
- codex/static_root/assets/stats-tab-BGxkeKnY.js +1 -0
- codex/static_root/assets/stats-tab-BGxkeKnY.js.br +0 -0
- codex/static_root/assets/stats-tab-BGxkeKnY.js.gz +0 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css +1 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css.br +0 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css.gz +0 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.css +1 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.css.br +0 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.css.gz +0 -0
- codex/static_root/assets/task-tab-B74Kah_b.58627993d3c6.js +1 -0
- codex/static_root/assets/task-tab-B74Kah_b.58627993d3c6.js.br +0 -0
- codex/static_root/assets/task-tab-B74Kah_b.58627993d3c6.js.gz +0 -0
- codex/static_root/assets/task-tab-B74Kah_b.js +1 -0
- codex/static_root/assets/task-tab-B74Kah_b.js.br +0 -0
- codex/static_root/assets/task-tab-B74Kah_b.js.gz +0 -0
- codex/static_root/assets/unauthorized-BF45der6.d34f8022124b.js +1 -0
- codex/static_root/assets/unauthorized-BF45der6.d34f8022124b.js.br +0 -0
- codex/static_root/assets/unauthorized-BF45der6.d34f8022124b.js.gz +0 -0
- codex/static_root/assets/unauthorized-BF45der6.js +1 -0
- codex/static_root/assets/unauthorized-BF45der6.js.br +0 -0
- codex/static_root/assets/unauthorized-BF45der6.js.gz +0 -0
- codex/static_root/assets/user-tab-BP2TTPg5.e63a5661a1a5.js +1 -0
- codex/static_root/assets/user-tab-BP2TTPg5.e63a5661a1a5.js.br +0 -0
- codex/static_root/assets/user-tab-BP2TTPg5.e63a5661a1a5.js.gz +0 -0
- codex/static_root/assets/user-tab-BP2TTPg5.js +1 -0
- codex/static_root/assets/user-tab-BP2TTPg5.js.br +0 -0
- codex/static_root/assets/user-tab-BP2TTPg5.js.gz +0 -0
- codex/static_root/{manifest.80e911824648.json → manifest.7033f9a22987.json} +230 -242
- codex/static_root/manifest.7033f9a22987.json.br +0 -0
- codex/static_root/manifest.7033f9a22987.json.gz +0 -0
- codex/static_root/manifest.json +230 -242
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/urls/root.py +12 -2
- codex/views/admin/api_key.py +1 -1
- codex/views/admin/flag.py +1 -1
- codex/views/admin/group.py +1 -1
- codex/views/admin/library.py +1 -1
- codex/views/admin/stats.py +41 -155
- codex/views/admin/tasks.py +1 -1
- codex/views/admin/user.py +1 -1
- codex/views/browser/browser.py +4 -1
- codex/views/browser/filters/field.py +15 -6
- codex/views/browser/paginate.py +61 -11
- codex/views/browser/validate.py +7 -5
- codex/views/opds/binary.py +1 -3
- codex/views/opds/const.py +10 -0
- codex/views/opds/util.py +10 -0
- codex/views/opds/v1/entry/entry.py +14 -18
- codex/views/opds/v1/feed.py +10 -24
- codex/views/opds/v1/links.py +1 -0
- codex/views/reader/arcs.py +65 -53
- codex/views/reader/books.py +38 -38
- codex/views/reader/init.py +7 -5
- codex/views/reader/reader.py +0 -2
- {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/METADATA +18 -7
- {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/RECORD +293 -289
- codex/serializers/admin.py +0 -268
- codex/settings/logging.py +0 -20
- codex/static_root/assets/VCheckbox-DUxuAZcY.46ab94043464.js +0 -1
- codex/static_root/assets/VCheckbox-DUxuAZcY.46ab94043464.js.br +0 -0
- codex/static_root/assets/VCheckbox-DUxuAZcY.46ab94043464.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DUxuAZcY.js +0 -1
- codex/static_root/assets/VCheckbox-DUxuAZcY.js.br +0 -0
- codex/static_root/assets/VCheckbox-DUxuAZcY.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BTFFFGx3.0cbde4eb9a65.js +0 -1
- codex/static_root/assets/VCheckboxBtn-BTFFFGx3.0cbde4eb9a65.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BTFFFGx3.0cbde4eb9a65.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BTFFFGx3.js +0 -1
- codex/static_root/assets/VCheckboxBtn-BTFFFGx3.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BTFFFGx3.js.gz +0 -0
- codex/static_root/assets/VCombobox-CGyBgdqr.2f85598cc1a4.js.br +0 -0
- codex/static_root/assets/VCombobox-CGyBgdqr.2f85598cc1a4.js.gz +0 -0
- codex/static_root/assets/VCombobox-CGyBgdqr.js.br +0 -0
- codex/static_root/assets/VCombobox-CGyBgdqr.js.gz +0 -0
- codex/static_root/assets/VDialog-DTWAoZBU.b8c4c276e9b2.js +0 -1
- codex/static_root/assets/VDialog-DTWAoZBU.b8c4c276e9b2.js.br +0 -0
- codex/static_root/assets/VDialog-DTWAoZBU.b8c4c276e9b2.js.gz +0 -0
- codex/static_root/assets/VDialog-DTWAoZBU.js +0 -1
- codex/static_root/assets/VDialog-DTWAoZBU.js.br +0 -0
- codex/static_root/assets/VDialog-DTWAoZBU.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-D4psrz4h.b55b901da65f.js +0 -1
- codex/static_root/assets/VExpansionPanels-D4psrz4h.b55b901da65f.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-D4psrz4h.b55b901da65f.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-D4psrz4h.js +0 -1
- codex/static_root/assets/VExpansionPanels-D4psrz4h.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-D4psrz4h.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-BY572TP-.6d47a94795de.js +0 -1
- codex/static_root/assets/VRadioGroup-BY572TP-.6d47a94795de.js.br +0 -2
- codex/static_root/assets/VRadioGroup-BY572TP-.6d47a94795de.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-BY572TP-.js +0 -1
- codex/static_root/assets/VRadioGroup-BY572TP-.js.br +0 -2
- codex/static_root/assets/VRadioGroup-BY572TP-.js.gz +0 -0
- codex/static_root/assets/VSelect-Bnhj5kBc.fdcd87c9e253.js +0 -1
- codex/static_root/assets/VSelect-Bnhj5kBc.fdcd87c9e253.js.br +0 -0
- codex/static_root/assets/VSelect-Bnhj5kBc.fdcd87c9e253.js.gz +0 -0
- codex/static_root/assets/VSelect-Bnhj5kBc.js +0 -1
- codex/static_root/assets/VSelect-Bnhj5kBc.js.br +0 -0
- codex/static_root/assets/VSelect-Bnhj5kBc.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DXxRmssH.252890f79cc6.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DXxRmssH.252890f79cc6.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DXxRmssH.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DXxRmssH.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DYJ3EYZI.a2542c5f90c4.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DYJ3EYZI.a2542c5f90c4.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DYJ3EYZI.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DYJ3EYZI.js.gz +0 -0
- codex/static_root/assets/VTable-BveGtxei.7f76f4ef812b.js.br +0 -0
- codex/static_root/assets/VTable-BveGtxei.7f76f4ef812b.js.gz +0 -0
- codex/static_root/assets/VTable-BveGtxei.js.br +0 -0
- codex/static_root/assets/VTable-BveGtxei.js.gz +0 -0
- codex/static_root/assets/VTextField-BayL7C1D.6e53c61b3760.js +0 -1
- codex/static_root/assets/VTextField-BayL7C1D.6e53c61b3760.js.br +0 -0
- codex/static_root/assets/VTextField-BayL7C1D.6e53c61b3760.js.gz +0 -0
- codex/static_root/assets/VTextField-BayL7C1D.js +0 -1
- codex/static_root/assets/VTextField-BayL7C1D.js.br +0 -0
- codex/static_root/assets/VTextField-BayL7C1D.js.gz +0 -0
- codex/static_root/assets/VWindowItem-FBuIO_rT.8610c7e83905.js.br +0 -0
- codex/static_root/assets/VWindowItem-FBuIO_rT.8610c7e83905.js.gz +0 -0
- codex/static_root/assets/VWindowItem-FBuIO_rT.js.br +0 -0
- codex/static_root/assets/VWindowItem-FBuIO_rT.js.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
- codex/static_root/assets/admin-BH6uiYBb.d51c720fa05c.js +0 -1
- codex/static_root/assets/admin-BH6uiYBb.d51c720fa05c.js.br +0 -0
- codex/static_root/assets/admin-BH6uiYBb.d51c720fa05c.js.gz +0 -0
- codex/static_root/assets/admin-BH6uiYBb.js +0 -1
- codex/static_root/assets/admin-BH6uiYBb.js.br +0 -0
- codex/static_root/assets/admin-BH6uiYBb.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-DsgLbfnD.7c409b430d79.js +0 -30
- codex/static_root/assets/admin-drawer-panel-DsgLbfnD.7c409b430d79.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-DsgLbfnD.7c409b430d79.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-DsgLbfnD.js +0 -30
- codex/static_root/assets/admin-drawer-panel-DsgLbfnD.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-DsgLbfnD.js.gz +0 -0
- codex/static_root/assets/browser-BVciW8XB.ab1d75272aa7.js +0 -1
- codex/static_root/assets/browser-BVciW8XB.ab1d75272aa7.js.br +0 -0
- codex/static_root/assets/browser-BVciW8XB.ab1d75272aa7.js.gz +0 -0
- codex/static_root/assets/browser-BVciW8XB.js +0 -1
- codex/static_root/assets/browser-BVciW8XB.js.br +0 -0
- codex/static_root/assets/browser-BVciW8XB.js.gz +0 -0
- codex/static_root/assets/browser-DP9eEJ0M.276e8636f8fc.css.br +0 -0
- codex/static_root/assets/browser-DP9eEJ0M.276e8636f8fc.css.gz +0 -0
- codex/static_root/assets/browser-DP9eEJ0M.css.br +0 -0
- codex/static_root/assets/browser-DP9eEJ0M.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-B-Sd5U5p.adf0a0f02866.js +0 -1
- codex/static_root/assets/change-password-dialog-B-Sd5U5p.adf0a0f02866.js.br +0 -0
- codex/static_root/assets/change-password-dialog-B-Sd5U5p.adf0a0f02866.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-B-Sd5U5p.js +0 -1
- codex/static_root/assets/change-password-dialog-B-Sd5U5p.js.br +0 -0
- codex/static_root/assets/change-password-dialog-B-Sd5U5p.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CYEBgtj5.a5f142656c12.js +0 -1
- codex/static_root/assets/confirm-dialog-CYEBgtj5.a5f142656c12.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CYEBgtj5.a5f142656c12.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CYEBgtj5.js +0 -1
- codex/static_root/assets/confirm-dialog-CYEBgtj5.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CYEBgtj5.js.gz +0 -0
- codex/static_root/assets/datetime-column-uaXWFgF_.29c6d50b483e.js.br +0 -0
- codex/static_root/assets/datetime-column-uaXWFgF_.29c6d50b483e.js.gz +0 -0
- codex/static_root/assets/datetime-column-uaXWFgF_.js.br +0 -0
- codex/static_root/assets/datetime-column-uaXWFgF_.js.gz +0 -0
- codex/static_root/assets/filter-DS84uTvk.4fbebd4832bc.js.br +0 -0
- codex/static_root/assets/filter-DS84uTvk.4fbebd4832bc.js.gz +0 -0
- codex/static_root/assets/filter-DS84uTvk.js.br +0 -0
- codex/static_root/assets/filter-DS84uTvk.js.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
- codex/static_root/assets/flag-tab-iJTJldQ1.213201f8e03b.js +0 -1
- codex/static_root/assets/flag-tab-iJTJldQ1.213201f8e03b.js.br +0 -0
- codex/static_root/assets/flag-tab-iJTJldQ1.213201f8e03b.js.gz +0 -0
- codex/static_root/assets/flag-tab-iJTJldQ1.js +0 -1
- codex/static_root/assets/flag-tab-iJTJldQ1.js.br +0 -0
- codex/static_root/assets/flag-tab-iJTJldQ1.js.gz +0 -0
- codex/static_root/assets/group-tab-DdmGE4gB.48ef83a4c7cd.js +0 -1
- codex/static_root/assets/group-tab-DdmGE4gB.48ef83a4c7cd.js.br +0 -0
- codex/static_root/assets/group-tab-DdmGE4gB.48ef83a4c7cd.js.gz +0 -0
- codex/static_root/assets/group-tab-DdmGE4gB.js +0 -1
- codex/static_root/assets/group-tab-DdmGE4gB.js.br +0 -0
- codex/static_root/assets/group-tab-DdmGE4gB.js.gz +0 -0
- codex/static_root/assets/http-error-HtOe8jPa.141ad665f81d.js +0 -1
- codex/static_root/assets/http-error-HtOe8jPa.141ad665f81d.js.br +0 -0
- codex/static_root/assets/http-error-HtOe8jPa.141ad665f81d.js.gz +0 -0
- codex/static_root/assets/http-error-HtOe8jPa.js +0 -1
- codex/static_root/assets/http-error-HtOe8jPa.js.br +0 -0
- codex/static_root/assets/http-error-HtOe8jPa.js.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
- codex/static_root/assets/library-tab-DLAJV6j2.dae6292bbf28.js +0 -1
- codex/static_root/assets/library-tab-DLAJV6j2.dae6292bbf28.js.br +0 -0
- codex/static_root/assets/library-tab-DLAJV6j2.dae6292bbf28.js.gz +0 -0
- codex/static_root/assets/library-tab-DLAJV6j2.js +0 -1
- codex/static_root/assets/library-tab-DLAJV6j2.js.br +0 -0
- codex/static_root/assets/library-tab-DLAJV6j2.js.gz +0 -0
- codex/static_root/assets/main-BjGE3kSi.22c1b19a9e19.js +0 -32
- codex/static_root/assets/main-BjGE3kSi.22c1b19a9e19.js.br +0 -0
- codex/static_root/assets/main-BjGE3kSi.22c1b19a9e19.js.gz +0 -0
- codex/static_root/assets/main-BjGE3kSi.js +0 -32
- codex/static_root/assets/main-BjGE3kSi.js.br +0 -0
- codex/static_root/assets/main-BjGE3kSi.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-C8CSryCU.9b61f95fd710.js +0 -1
- codex/static_root/assets/pagination-toolbar-C8CSryCU.9b61f95fd710.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-C8CSryCU.9b61f95fd710.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-C8CSryCU.js +0 -1
- codex/static_root/assets/pagination-toolbar-C8CSryCU.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-C8CSryCU.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C-4EM9-d.ce9cf66bd4c3.js.br +0 -0
- codex/static_root/assets/pdf-doc-C-4EM9-d.ce9cf66bd4c3.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C-4EM9-d.js.br +0 -0
- codex/static_root/assets/pdf-doc-C-4EM9-d.js.gz +0 -0
- codex/static_root/assets/reader-C5OTEQ7N.fae52c1fee51.js +0 -2
- codex/static_root/assets/reader-C5OTEQ7N.fae52c1fee51.js.br +0 -0
- codex/static_root/assets/reader-C5OTEQ7N.fae52c1fee51.js.gz +0 -0
- codex/static_root/assets/reader-C5OTEQ7N.js +0 -2
- codex/static_root/assets/reader-C5OTEQ7N.js.br +0 -0
- codex/static_root/assets/reader-C5OTEQ7N.js.gz +0 -0
- codex/static_root/assets/reader-DOQuhDYO.css.br +0 -0
- codex/static_root/assets/reader-DOQuhDYO.css.gz +0 -0
- codex/static_root/assets/reader-DOQuhDYO.df559ad29ab3.css.br +0 -0
- codex/static_root/assets/reader-DOQuhDYO.df559ad29ab3.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
- codex/static_root/assets/relation-chips-C4LQSHRc.3c85433df91c.js +0 -1
- codex/static_root/assets/relation-chips-C4LQSHRc.3c85433df91c.js.br +0 -0
- codex/static_root/assets/relation-chips-C4LQSHRc.3c85433df91c.js.gz +0 -0
- codex/static_root/assets/relation-chips-C4LQSHRc.js +0 -1
- codex/static_root/assets/relation-chips-C4LQSHRc.js.br +0 -0
- codex/static_root/assets/relation-chips-C4LQSHRc.js.gz +0 -0
- codex/static_root/assets/settings-drawer-aTAAoX2A.2f21008ade35.js +0 -2
- codex/static_root/assets/settings-drawer-aTAAoX2A.2f21008ade35.js.br +0 -0
- codex/static_root/assets/settings-drawer-aTAAoX2A.2f21008ade35.js.gz +0 -0
- codex/static_root/assets/settings-drawer-aTAAoX2A.js +0 -2
- codex/static_root/assets/settings-drawer-aTAAoX2A.js.br +0 -0
- codex/static_root/assets/settings-drawer-aTAAoX2A.js.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +0 -1
- 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 +0 -1
- 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-DL4J3pvZ.b9056e70a70f.js +0 -1
- codex/static_root/assets/stats-tab-DL4J3pvZ.b9056e70a70f.js.br +0 -0
- codex/static_root/assets/stats-tab-DL4J3pvZ.b9056e70a70f.js.gz +0 -0
- codex/static_root/assets/stats-tab-DL4J3pvZ.js +0 -1
- codex/static_root/assets/stats-tab-DL4J3pvZ.js.br +0 -0
- codex/static_root/assets/stats-tab-DL4J3pvZ.js.gz +0 -0
- codex/static_root/assets/task-tab-0t7ZJlwL.1ff1cc14a6aa.js +0 -1
- codex/static_root/assets/task-tab-0t7ZJlwL.1ff1cc14a6aa.js.br +0 -0
- codex/static_root/assets/task-tab-0t7ZJlwL.1ff1cc14a6aa.js.gz +0 -0
- codex/static_root/assets/task-tab-0t7ZJlwL.js +0 -1
- codex/static_root/assets/task-tab-0t7ZJlwL.js.br +0 -0
- codex/static_root/assets/task-tab-0t7ZJlwL.js.gz +0 -0
- codex/static_root/assets/to-case-BCB751sX.4afb5ae1b77d.js +0 -1
- codex/static_root/assets/to-case-BCB751sX.js +0 -1
- codex/static_root/assets/unauthorized-Ovy_BO5R.598527e8e4fe.js +0 -1
- codex/static_root/assets/unauthorized-Ovy_BO5R.598527e8e4fe.js.br +0 -0
- codex/static_root/assets/unauthorized-Ovy_BO5R.598527e8e4fe.js.gz +0 -0
- codex/static_root/assets/unauthorized-Ovy_BO5R.js +0 -1
- codex/static_root/assets/unauthorized-Ovy_BO5R.js.br +0 -0
- codex/static_root/assets/unauthorized-Ovy_BO5R.js.gz +0 -0
- codex/static_root/assets/user-tab-Cdspo8ie.8e886a1a28e8.js +0 -1
- codex/static_root/assets/user-tab-Cdspo8ie.8e886a1a28e8.js.br +0 -0
- codex/static_root/assets/user-tab-Cdspo8ie.8e886a1a28e8.js.gz +0 -0
- codex/static_root/assets/user-tab-Cdspo8ie.js +0 -1
- codex/static_root/assets/user-tab-Cdspo8ie.js.br +0 -0
- codex/static_root/assets/user-tab-Cdspo8ie.js.gz +0 -0
- codex/static_root/manifest.80e911824648.json.br +0 -0
- codex/static_root/manifest.80e911824648.json.gz +0 -0
- {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/LICENSE +0 -0
- {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/WHEEL +0 -0
- {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""OPDS v1 Entry."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
from contextlib import suppress
|
|
4
5
|
from datetime import datetime, timezone
|
|
5
6
|
from urllib.parse import urlencode
|
|
6
7
|
|
|
8
|
+
from dateutil import parser
|
|
7
9
|
from django.urls import reverse
|
|
8
10
|
|
|
9
11
|
from codex.logger.logging import get_logger
|
|
@@ -24,11 +26,6 @@ LOG = get_logger(__name__)
|
|
|
24
26
|
class OPDS1Entry(OPDS1EntryLinksMixin):
|
|
25
27
|
"""An OPDS entry object."""
|
|
26
28
|
|
|
27
|
-
_DATE_FORMAT_BASE = "%Y-%m-%dT%H:%M:%S"
|
|
28
|
-
_DATE_FORMAT_MS = _DATE_FORMAT_BASE + ".%f%z"
|
|
29
|
-
_DATE_FORMAT = _DATE_FORMAT_BASE + "%z"
|
|
30
|
-
_DATE_FORMATS = (_DATE_FORMAT_MS, _DATE_FORMAT)
|
|
31
|
-
|
|
32
29
|
@property
|
|
33
30
|
def id_tag(self):
|
|
34
31
|
"""GUID is a nav url."""
|
|
@@ -69,9 +66,12 @@ class OPDS1Entry(OPDS1EntryLinksMixin):
|
|
|
69
66
|
@property
|
|
70
67
|
def issued(self):
|
|
71
68
|
"""Return the published date."""
|
|
69
|
+
date = ""
|
|
72
70
|
if self.obj.group == "c":
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
with suppress(Exception):
|
|
72
|
+
date = self.obj.date.isoformat()
|
|
73
|
+
|
|
74
|
+
return date
|
|
75
75
|
|
|
76
76
|
@property
|
|
77
77
|
def publisher(self):
|
|
@@ -81,17 +81,13 @@ class OPDS1Entry(OPDS1EntryLinksMixin):
|
|
|
81
81
|
def _get_datefield(self, key):
|
|
82
82
|
result = None
|
|
83
83
|
if not self.fake and (value := getattr(self.obj, key, None)):
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
result = value.astimezone(timezone.utc).strftime(date_format)
|
|
92
|
-
break
|
|
93
|
-
except ValueError:
|
|
94
|
-
pass
|
|
84
|
+
try:
|
|
85
|
+
if isinstance(value, str):
|
|
86
|
+
result = parser.parse(value)
|
|
87
|
+
if isinstance(value, datetime):
|
|
88
|
+
result = value.astimezone(timezone.utc).isoformat()
|
|
89
|
+
except ValueError:
|
|
90
|
+
pass
|
|
95
91
|
return result
|
|
96
92
|
|
|
97
93
|
@property
|
codex/views/opds/v1/feed.py
CHANGED
|
@@ -17,7 +17,8 @@ from codex.serializers.opds.v1 import (
|
|
|
17
17
|
)
|
|
18
18
|
from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
|
|
19
19
|
from codex.views.opds.auth import OPDSTemplateView
|
|
20
|
-
from codex.views.opds.const import BLANK_TITLE, MimeType
|
|
20
|
+
from codex.views.opds.const import BLANK_TITLE, MimeType, UserAgentNames
|
|
21
|
+
from codex.views.opds.util import get_user_agent_name
|
|
21
22
|
from codex.views.opds.v1.entry.data import OPDS1EntryData
|
|
22
23
|
from codex.views.opds.v1.entry.entry import OPDS1Entry
|
|
23
24
|
from codex.views.opds.v1.links import (
|
|
@@ -41,16 +42,6 @@ class OpdsNs:
|
|
|
41
42
|
ACQUISITION = "http://opds-spec.org/2010/acquisition"
|
|
42
43
|
|
|
43
44
|
|
|
44
|
-
class UserAgentNames:
|
|
45
|
-
"""Control whether to hack in facets with nav links."""
|
|
46
|
-
|
|
47
|
-
CLIENT_REORDERS = frozenset({"Chunky"})
|
|
48
|
-
FACET_SUPPORT = frozenset({"yar"}) # kybooks
|
|
49
|
-
SIMPLE_DOWNLOAD_MIME_TYPES = frozenset({"PocketBook Reader"})
|
|
50
|
-
# Other known valid names:
|
|
51
|
-
# "Panels", "Chunky"
|
|
52
|
-
|
|
53
|
-
|
|
54
45
|
class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
55
46
|
"""OPDS 1 Feed."""
|
|
56
47
|
|
|
@@ -105,10 +96,14 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
105
96
|
@property
|
|
106
97
|
def updated(self):
|
|
107
98
|
"""Use mtime for updated."""
|
|
99
|
+
datestr = ""
|
|
108
100
|
try:
|
|
109
|
-
|
|
101
|
+
mtime = self.obj.get("mtime")
|
|
102
|
+
if mtime:
|
|
103
|
+
datestr = mtime.isoformat() # type: ignore
|
|
110
104
|
except Exception:
|
|
111
105
|
LOG.exception("Getting OPDS v1 updated")
|
|
106
|
+
return datestr
|
|
112
107
|
|
|
113
108
|
@property
|
|
114
109
|
def items_per_page(self):
|
|
@@ -218,19 +213,10 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
218
213
|
|
|
219
214
|
def _set_user_agent_variables(self):
|
|
220
215
|
"""Set User Agent variables."""
|
|
221
|
-
|
|
222
|
-
if not user_agent:
|
|
223
|
-
return
|
|
224
|
-
user_agent_parts = user_agent.split("/", 1)
|
|
225
|
-
if user_agent_parts:
|
|
226
|
-
user_agent_name = user_agent_parts[0]
|
|
227
|
-
else:
|
|
228
|
-
return
|
|
216
|
+
user_agent_name = get_user_agent_name(self.request)
|
|
229
217
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if user_agent_name in UserAgentNames.CLIENT_REORDERS:
|
|
233
|
-
self.skip_order_facets = True
|
|
218
|
+
self.user_facets = user_agent_name in UserAgentNames.FACET_SUPPORT
|
|
219
|
+
self.skip_order_facets = user_agent_name in UserAgentNames.CLIENT_REORDERS
|
|
234
220
|
if user_agent_name in UserAgentNames.SIMPLE_DOWNLOAD_MIME_TYPES:
|
|
235
221
|
self.mime_type_map = MimeType.SIMPLE_FILE_TYPE_MAP
|
|
236
222
|
|
codex/views/opds/v1/links.py
CHANGED
|
@@ -168,6 +168,7 @@ class OPDS1LinksView(OPDS1FacetsView):
|
|
|
168
168
|
reverse("opds:authentication:v1"),
|
|
169
169
|
MimeType.AUTHENTICATION,
|
|
170
170
|
),
|
|
171
|
+
OPDS1Link(Rel.START, reverse("opds:v1:start"), MimeType.NAV),
|
|
171
172
|
OPDS1Link(
|
|
172
173
|
"search", reverse("opds:v1:opensearch_v1"), MimeType.OPENSEARCH
|
|
173
174
|
),
|
codex/views/reader/arcs.py
CHANGED
|
@@ -4,7 +4,10 @@ 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
|
|
7
|
+
from codex.views.const import (
|
|
8
|
+
FOLDER_GROUP,
|
|
9
|
+
STORY_ARC_GROUP,
|
|
10
|
+
)
|
|
8
11
|
from codex.views.reader.books import ReaderBooksView
|
|
9
12
|
|
|
10
13
|
|
|
@@ -16,77 +19,86 @@ class ReaderArcsView(ReaderBooksView):
|
|
|
16
19
|
super().__init__(*args, **kwargs)
|
|
17
20
|
self.params: MappingProxyType = MappingProxyType({})
|
|
18
21
|
|
|
19
|
-
def _get_group_arc(self, book, attr):
|
|
20
|
-
"""
|
|
22
|
+
def _get_group_arc(self, book, attr, browser_arc_group, arcs, max_mtime): # noqa: PLR0913
|
|
23
|
+
"""Append the volume arc."""
|
|
24
|
+
if browser_arc_group == attr[0]:
|
|
25
|
+
return max_mtime
|
|
21
26
|
group = getattr(book, attr, None)
|
|
22
27
|
if not group:
|
|
23
|
-
return
|
|
28
|
+
return max_mtime
|
|
24
29
|
|
|
25
30
|
_, arc_pks = self.get_group_pks_from_breadcrumbs([group])
|
|
26
31
|
if group.pk not in arc_pks:
|
|
27
32
|
arc_pks = (group.pk,)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
arcs.append(
|
|
34
|
+
{
|
|
35
|
+
"group": attr[0],
|
|
36
|
+
"pks": arc_pks,
|
|
37
|
+
"name": group.name,
|
|
38
|
+
"mtime": group.updated_at,
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
return max_none(max_mtime, group.updated_at)
|
|
34
42
|
|
|
35
|
-
def _get_folder_arc(self, book):
|
|
36
|
-
"""
|
|
43
|
+
def _get_folder_arc(self, book, browser_arc_group, arcs, max_mtime):
|
|
44
|
+
"""Append the folder arc."""
|
|
37
45
|
efv_flag = (
|
|
38
46
|
AdminFlag.objects.only("on")
|
|
39
47
|
.get(key=AdminFlag.FlagChoices.FOLDER_VIEW.value)
|
|
40
48
|
.on
|
|
41
49
|
)
|
|
42
|
-
if not efv_flag:
|
|
43
|
-
return
|
|
50
|
+
if not efv_flag or browser_arc_group == FOLDER_GROUP:
|
|
51
|
+
return max_mtime
|
|
44
52
|
|
|
45
53
|
folder = book.parent_folder
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
arcs.append(
|
|
55
|
+
{
|
|
56
|
+
"group": FOLDER_GROUP,
|
|
57
|
+
"pks": (folder.pk,),
|
|
58
|
+
"name": folder.name,
|
|
59
|
+
"mtime": folder.updated_at,
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
return max_none(max_mtime, folder.updated_at)
|
|
63
|
+
|
|
64
|
+
def _get_story_arcs(self, book, browser_arc, arcs, max_mtime):
|
|
65
|
+
"""Append the story arcs."""
|
|
66
|
+
if browser_arc.get("group") == STORY_ARC_GROUP:
|
|
67
|
+
browser_arc_pks = frozenset(browser_arc.get("pks", ()))
|
|
68
|
+
else:
|
|
69
|
+
browser_arc_pks = frozenset()
|
|
52
70
|
|
|
53
|
-
|
|
54
|
-
"""Create story arcs."""
|
|
55
|
-
sas = []
|
|
56
|
-
for san in book.story_arc_numbers.all():
|
|
71
|
+
for san in book.story_arc_numbers.all().order_by("story_arc__name"):
|
|
57
72
|
sa = san.story_arc
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
if not browser_arc_pks or sa.pk not in browser_arc_pks:
|
|
74
|
+
arc = {
|
|
75
|
+
"group": "a",
|
|
76
|
+
"pks": (sa.pk,),
|
|
77
|
+
"name": sa.name,
|
|
78
|
+
"mtime": sa.updated_at,
|
|
79
|
+
}
|
|
80
|
+
arcs.append(arc)
|
|
81
|
+
max_mtime = max_none(max_mtime, sa.updated_at)
|
|
82
|
+
return max_mtime
|
|
66
83
|
|
|
67
84
|
def get_arcs(self, book):
|
|
68
85
|
"""Get all series/folder/story arcs."""
|
|
69
|
-
# create top arcs
|
|
70
86
|
arcs = []
|
|
71
|
-
top_group = self.params.get("top_group")
|
|
72
|
-
arc = None
|
|
73
|
-
if series_arc := self._get_group_arc(book, "series"):
|
|
74
|
-
arcs.append(series_arc)
|
|
75
|
-
arc = series_arc
|
|
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]
|
|
88
|
-
|
|
89
87
|
max_mtime = None
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
|
|
89
|
+
if browser_arc := self.params.get("browser_arc", {}):
|
|
90
|
+
arcs.append(browser_arc)
|
|
91
|
+
browser_arc_mtime = browser_arc.get("mtime")
|
|
92
|
+
max_mtime = max_none(max_mtime, browser_arc_mtime)
|
|
93
|
+
|
|
94
|
+
browser_arc_group = browser_arc.get("group", "")
|
|
95
|
+
max_mtime = self._get_group_arc(
|
|
96
|
+
book, "series", browser_arc_group, arcs, max_mtime
|
|
97
|
+
)
|
|
98
|
+
max_mtime = self._get_group_arc(
|
|
99
|
+
book, "volume", browser_arc_group, arcs, max_mtime
|
|
100
|
+
)
|
|
101
|
+
max_mtime = self._get_folder_arc(book, browser_arc_group, arcs, max_mtime)
|
|
102
|
+
max_mtime = self._get_story_arcs(book, browser_arc, arcs, max_mtime)
|
|
103
|
+
|
|
92
104
|
return arcs, max_mtime
|
codex/views/reader/books.py
CHANGED
|
@@ -6,7 +6,14 @@ 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.
|
|
9
|
+
from codex.views.browser.filters.field import ComicFieldFilterView
|
|
10
|
+
from codex.views.const import (
|
|
11
|
+
FOLDER_GROUP,
|
|
12
|
+
GROUP_MODEL_MAP,
|
|
13
|
+
GROUP_RELATION,
|
|
14
|
+
NONE_INTEGERFIELD,
|
|
15
|
+
STORY_ARC_GROUP,
|
|
16
|
+
)
|
|
10
17
|
from codex.views.mixins import SharedAnnotationsMixin
|
|
11
18
|
from codex.views.reader.init import ReaderInitView
|
|
12
19
|
|
|
@@ -57,45 +64,30 @@ class ReaderBooksView(BookmarkBaseView, ReaderInitView, SharedAnnotationsMixin):
|
|
|
57
64
|
|
|
58
65
|
def _get_comics_list(self):
|
|
59
66
|
"""Get the reader naviation group filter."""
|
|
60
|
-
select_related = ("series", "volume")
|
|
61
|
-
prefetch_related = ()
|
|
62
|
-
|
|
63
67
|
arc: Mapping = self.params.get("arc", {}) # type: ignore
|
|
64
68
|
|
|
65
|
-
arc_group = arc.get("group", "
|
|
66
|
-
if arc_group
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
arc_group = arc.get("group", "")
|
|
70
|
+
if not GROUP_MODEL_MAP.get(arc_group):
|
|
71
|
+
arc_group = "s"
|
|
72
|
+
|
|
73
|
+
rel = GROUP_RELATION[arc_group]
|
|
74
|
+
fields = _COMIC_FIELDS
|
|
75
|
+
arc_pk_rel = rel + "__pk"
|
|
76
|
+
arc_index = NONE_INTEGERFIELD
|
|
77
|
+
ordering = ()
|
|
78
|
+
arc_pk_select_related = (rel,)
|
|
79
|
+
select_related = ()
|
|
80
|
+
prefetch_related = ()
|
|
81
|
+
|
|
82
|
+
if arc_group == STORY_ARC_GROUP:
|
|
83
|
+
prefetch_related = (*prefetch_related, rel)
|
|
72
84
|
arc_index = F("story_arc_numbers__number")
|
|
73
85
|
ordering = ("arc_index", "date", "pk")
|
|
74
86
|
arc_pk_select_related = ()
|
|
75
87
|
elif arc_group == FOLDER_GROUP:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
fields = (*_COMIC_FIELDS, "parent_folder")
|
|
79
|
-
arc_pk_rel = "parent_folder__pk"
|
|
80
|
-
arc_index = NONE_INTEGERFIELD
|
|
88
|
+
select_related = (rel,)
|
|
89
|
+
fields = (*_COMIC_FIELDS, rel)
|
|
81
90
|
ordering = ("path", "pk")
|
|
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",)
|
|
91
|
-
else:
|
|
92
|
-
# series
|
|
93
|
-
rel = "series"
|
|
94
|
-
fields = _COMIC_FIELDS
|
|
95
|
-
arc_pk_rel = "series__pk"
|
|
96
|
-
arc_index = NONE_INTEGERFIELD
|
|
97
|
-
ordering = ()
|
|
98
|
-
arc_pk_select_related = ("series",)
|
|
99
91
|
|
|
100
92
|
arc_pks = self._get_reader_arc_pks(
|
|
101
93
|
arc, arc_pk_select_related, prefetch_related, arc_pk_rel, arc_group
|
|
@@ -103,12 +95,20 @@ class ReaderBooksView(BookmarkBaseView, ReaderInitView, SharedAnnotationsMixin):
|
|
|
103
95
|
nav_filter = {f"{rel}__in": arc_pks}
|
|
104
96
|
group_acl_filter = self.get_group_acl_filter(Comic)
|
|
105
97
|
|
|
98
|
+
qs = Comic.objects.filter(group_acl_filter).filter(**nav_filter)
|
|
99
|
+
if browser_filters := arc.get("filters"):
|
|
100
|
+
# no search at this time.
|
|
101
|
+
field_filters = ComicFieldFilterView.get_all_comic_field_filters(
|
|
102
|
+
"", browser_filters
|
|
103
|
+
)
|
|
104
|
+
qs = qs.filter(field_filters)
|
|
105
|
+
|
|
106
|
+
if select_related:
|
|
107
|
+
qs = qs.select_related(*select_related)
|
|
108
|
+
if prefetch_related:
|
|
109
|
+
qs = qs.prefetch_related(*prefetch_related)
|
|
106
110
|
qs = (
|
|
107
|
-
|
|
108
|
-
.filter(**nav_filter)
|
|
109
|
-
.select_related(*select_related)
|
|
110
|
-
.prefetch_related(*prefetch_related)
|
|
111
|
-
.only(*fields)
|
|
111
|
+
qs.only(*fields)
|
|
112
112
|
.annotate(
|
|
113
113
|
issue_count=F("volume__issue_count"),
|
|
114
114
|
)
|
codex/views/reader/init.py
CHANGED
|
@@ -13,8 +13,8 @@ from codex.views.session import SessionView
|
|
|
13
13
|
from codex.views.util import reparse_json_query_params
|
|
14
14
|
|
|
15
15
|
LOG = get_logger(__name__)
|
|
16
|
-
VALID_ARC_GROUPS =
|
|
17
|
-
_JSON_KEYS = frozenset({"arc", "breadcrumbs", "show"})
|
|
16
|
+
VALID_ARC_GROUPS = "pisvfa"
|
|
17
|
+
_JSON_KEYS = frozenset({"arc", "breadcrumbs", "browser_arc", "show"})
|
|
18
18
|
_BROWSER_SESSION_DEFAULTS = SessionView.SESSION_DEFAULTS[
|
|
19
19
|
SessionView.BROWSER_SESSION_KEY
|
|
20
20
|
]
|
|
@@ -89,12 +89,14 @@ class ReaderInitView(SessionView):
|
|
|
89
89
|
# Can't be in the serializer
|
|
90
90
|
arc = params.get("arc", {})
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
arc_group = arc.get("group", "")
|
|
93
|
+
arc_pks = arc.get("pks", ())
|
|
94
|
+
if arc_group not in VALID_ARC_GROUPS or not arc_pks:
|
|
93
95
|
top_group = params["top_group"]
|
|
94
|
-
if top_group in
|
|
96
|
+
if top_group in FOLDER_GROUP + STORY_ARC_GROUP:
|
|
95
97
|
search_groups = (top_group,)
|
|
96
98
|
else:
|
|
97
|
-
search_groups =
|
|
99
|
+
search_groups = "vsip"
|
|
98
100
|
|
|
99
101
|
group, pks = self.get_group_pks_from_breadcrumbs(search_groups)
|
|
100
102
|
if not group:
|
codex/views/reader/reader.py
CHANGED
|
@@ -12,7 +12,6 @@ from codex.logger.logging import get_logger
|
|
|
12
12
|
from codex.serializers.reader import ReaderComicsSerializer, ReaderViewInputSerializer
|
|
13
13
|
from codex.serializers.redirect import ReaderRedirectSerializer
|
|
14
14
|
from codex.views.reader.arcs import ReaderArcsView
|
|
15
|
-
from codex.views.util import pop_name
|
|
16
15
|
|
|
17
16
|
LOG = get_logger(__name__)
|
|
18
17
|
|
|
@@ -90,7 +89,6 @@ class ReaderView(ReaderArcsView):
|
|
|
90
89
|
arc["count"] = current.arc_count # type: ignore
|
|
91
90
|
|
|
92
91
|
close_route = self.get_last_route()
|
|
93
|
-
close_route = pop_name(close_route)
|
|
94
92
|
|
|
95
93
|
return {
|
|
96
94
|
"books": books,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codex
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.4
|
|
4
4
|
Summary: A comic archive web server.
|
|
5
5
|
Home-page: https://github.com/ajslater/codex
|
|
6
6
|
License: GPL-3.0-only
|
|
@@ -29,7 +29,7 @@ Requires-Dist: ansicolors (>=1.1,<2.0)
|
|
|
29
29
|
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
|
-
Requires-Dist: comicbox[pdf] (>=1.1.
|
|
32
|
+
Requires-Dist: comicbox[pdf] (>=1.1.10,<2.0.0)
|
|
33
33
|
Requires-Dist: django (>=5.0.6,<6.0.0)
|
|
34
34
|
Requires-Dist: django-cachalot (>=2.6.2,<3.0.0)
|
|
35
35
|
Requires-Dist: django-cors-headers (>=4.0,<5.0)
|
|
@@ -42,7 +42,7 @@ Requires-Dist: fnvhash (>=0.1,<0.2)
|
|
|
42
42
|
Requires-Dist: humanfriendly (>=10.0,<11.0)
|
|
43
43
|
Requires-Dist: humanize (>=4.0.0,<5.0.0)
|
|
44
44
|
Requires-Dist: hypercorn[h3] (>=0.17.3,<0.18.0)
|
|
45
|
-
Requires-Dist: psutil (>=
|
|
45
|
+
Requires-Dist: psutil (>=6.0.0,<7.0.0)
|
|
46
46
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
47
47
|
Requires-Dist: requests (>=2.24,<3.0)
|
|
48
48
|
Requires-Dist: setuptools (>=70.0.0,<71.0.0)
|
|
@@ -71,6 +71,7 @@ border-radius: 128px;
|
|
|
71
71
|
## <a name="features">✨ Features</a>
|
|
72
72
|
|
|
73
73
|
- Codex is a web server.
|
|
74
|
+
- GPLv3 Licenced.
|
|
74
75
|
- Full text search of metadata and bookmarks.
|
|
75
76
|
- Filter and sort on all comic metadata and unread status per user.
|
|
76
77
|
- Browse a tree of Publishers, Imprints, Series, Volumes, or your own folder
|
|
@@ -95,8 +96,8 @@ border-radius: 128px;
|
|
|
95
96
|
|
|
96
97
|
## <a name="demonstration">👀 Demonstration</a>
|
|
97
98
|
|
|
98
|
-
You may browse a [live demo server](https://codex.
|
|
99
|
-
Codex.
|
|
99
|
+
You may browse a [live demo server](https://demo.codex-reader.app/) to get a
|
|
100
|
+
feel for Codex.
|
|
100
101
|
|
|
101
102
|
## <a name="news">📜 News</a>
|
|
102
103
|
|
|
@@ -134,12 +135,22 @@ brew install jpeg libffi libyaml libzip openssl python unrar webp
|
|
|
134
135
|
|
|
135
136
|
###### <a href="#debian">Debian</a>
|
|
136
137
|
|
|
137
|
-
...and Ubuntu, Mint, MX and others.
|
|
138
|
+
...and Ubuntu, Mint, MX and others (and WSL).
|
|
138
139
|
|
|
139
140
|
<!-- eslint-skip -->
|
|
140
141
|
|
|
141
142
|
```sh
|
|
142
|
-
apt install build-essential libimagequant0
|
|
143
|
+
apt install build-essential libimagequant0 libjpeg-turbo8 libopenjp2-7 libssl libyaml-0-2 libtiff6 libwebp7 python3-dev python3-pip mupdf unrar zlib1g
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Versions of packages like libjpeg, libssl, libtiff may differ between flavors
|
|
147
|
+
and versions of Debian. If the version above is not available, try searching for
|
|
148
|
+
one that is with apt-cache or aptitude.
|
|
149
|
+
|
|
150
|
+
<!-- eslint-skip -->
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
apt-cache search libjpeg-turbo
|
|
143
154
|
```
|
|
144
155
|
|
|
145
156
|
###### Alpine
|