codex 1.7.3a2__py3-none-any.whl → 1.7.5__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.

Files changed (632) hide show
  1. codex/asgi.py +2 -1
  2. codex/choices/__init__.py +1 -0
  3. codex/{choices.py → choices/admin.py} +47 -146
  4. codex/choices/browser.py +85 -0
  5. codex/{choices_to_json.py → choices/choices_to_json.py} +25 -19
  6. codex/choices/notifications.py +16 -0
  7. codex/choices/reader.py +33 -0
  8. codex/db.py +2 -2
  9. codex/exceptions.py +7 -8
  10. codex/librarian/bookmark/bookmarkd.py +8 -4
  11. codex/librarian/bookmark/update.py +84 -49
  12. codex/librarian/covers/create.py +10 -8
  13. codex/librarian/covers/path.py +6 -5
  14. codex/librarian/covers/purge.py +4 -4
  15. codex/librarian/importer/aggregate.py +2 -2
  16. codex/librarian/importer/cache.py +1 -4
  17. codex/librarian/importer/create_comics.py +2 -2
  18. codex/librarian/importer/create_covers.py +1 -1
  19. codex/librarian/importer/create_fks.py +4 -3
  20. codex/librarian/importer/deleted.py +3 -3
  21. codex/librarian/importer/extract.py +56 -37
  22. codex/librarian/importer/failed_imports.py +1 -1
  23. codex/librarian/importer/importer.py +8 -3
  24. codex/librarian/importer/importerd.py +3 -3
  25. codex/librarian/importer/link_comics.py +2 -1
  26. codex/librarian/importer/moved.py +2 -7
  27. codex/librarian/importer/query_fks.py +1 -4
  28. codex/librarian/importer/tasks.py +1 -1
  29. codex/librarian/janitor/integrity.py +27 -20
  30. codex/librarian/janitor/janitor.py +1 -1
  31. codex/librarian/janitor/latest_version.py +1 -1
  32. codex/librarian/janitor/update.py +5 -2
  33. codex/librarian/janitor/vacuum.py +1 -1
  34. codex/librarian/librariand.py +3 -2
  35. codex/librarian/notifier/notifierd.py +2 -1
  36. codex/librarian/notifier/tasks.py +17 -3
  37. codex/librarian/search/optimize.py +1 -1
  38. codex/librarian/search/remove.py +1 -1
  39. codex/librarian/search/update.py +4 -4
  40. codex/librarian/telemeter/stats.py +7 -6
  41. codex/librarian/telemeter/telemeter.py +0 -1
  42. codex/librarian/watchdog/db_snapshot.py +3 -3
  43. codex/librarian/watchdog/dir_snapshot_diff.py +3 -4
  44. codex/librarian/watchdog/emitter.py +4 -4
  45. codex/librarian/watchdog/event_batcherd.py +2 -1
  46. codex/librarian/watchdog/events.py +1 -2
  47. codex/librarian/watchdog/observers.py +15 -10
  48. codex/logger/logging.py +1 -1
  49. codex/memory.py +3 -2
  50. codex/migrations/0001_init.py +1 -2
  51. codex/migrations/0014_pdf_issue_suffix_remove_cover_image_sort_name.py +1 -2
  52. codex/migrations/0018_rename_userbookmark_bookmark.py +1 -2
  53. codex/migrations/0028_telemeter.py +1 -5
  54. codex/models/base.py +3 -1
  55. codex/models/bookmark.py +4 -7
  56. codex/models/comic.py +2 -2
  57. codex/models/functions.py +12 -5
  58. codex/models/groups.py +1 -1
  59. codex/models/library.py +1 -1
  60. codex/models/paths.py +4 -4
  61. codex/models/query.py +8 -4
  62. codex/models/util.py +1 -1
  63. codex/permissions.py +1 -1
  64. codex/run.py +1 -1
  65. codex/serializers/admin/libraries.py +1 -3
  66. codex/serializers/admin/stats.py +4 -4
  67. codex/serializers/admin/tasks.py +12 -2
  68. codex/serializers/auth.py +4 -2
  69. codex/serializers/browser/choices.py +87 -9
  70. codex/serializers/browser/filters.py +9 -37
  71. codex/serializers/browser/metadata.py +2 -1
  72. codex/serializers/browser/mixins.py +7 -3
  73. codex/serializers/browser/mtime.py +3 -5
  74. codex/serializers/browser/page.py +5 -4
  75. codex/serializers/browser/settings.py +8 -6
  76. codex/serializers/fields/__init__.py +37 -0
  77. codex/serializers/fields/auth.py +45 -0
  78. codex/serializers/fields/browser.py +85 -0
  79. codex/serializers/fields/group.py +13 -0
  80. codex/serializers/fields/reader.py +22 -0
  81. codex/serializers/fields/sanitized.py +13 -0
  82. codex/serializers/fields/session.py +16 -0
  83. codex/serializers/fields/stats.py +36 -0
  84. codex/serializers/fields/vuetify.py +62 -0
  85. codex/serializers/models/base.py +1 -1
  86. codex/serializers/models/comic.py +2 -2
  87. codex/serializers/models/groups.py +0 -2
  88. codex/serializers/models/named.py +1 -1
  89. codex/serializers/models/pycountry.py +1 -1
  90. codex/serializers/opds/authentication.py +4 -2
  91. codex/serializers/opds/urls.py +2 -1
  92. codex/serializers/opds/v1.py +4 -3
  93. codex/serializers/opds/v2/feed.py +2 -1
  94. codex/serializers/opds/v2/links.py +23 -21
  95. codex/serializers/opds/v2/metadata.py +4 -8
  96. codex/serializers/opds/v2/progression.py +5 -3
  97. codex/serializers/opds/v2/publication.py +16 -18
  98. codex/serializers/opds/v2/unused.py +15 -8
  99. codex/serializers/reader.py +16 -14
  100. codex/serializers/redirect.py +2 -1
  101. codex/serializers/route.py +6 -4
  102. codex/serializers/settings.py +5 -3
  103. codex/serializers/versions.py +2 -1
  104. codex/settings/settings.py +4 -3
  105. codex/signals/django_signals.py +1 -1
  106. codex/startup.py +2 -2
  107. codex/static_root/assets/{VCheckbox-FaT6MGfu.f6732060f734.js → VCheckbox-CWBDr4kF.6da5bf3a4d90.js} +1 -1
  108. codex/static_root/assets/VCheckbox-CWBDr4kF.6da5bf3a4d90.js.br +0 -0
  109. codex/static_root/assets/VCheckbox-CWBDr4kF.6da5bf3a4d90.js.gz +0 -0
  110. codex/static_root/assets/{VCheckbox-FaT6MGfu.js → VCheckbox-CWBDr4kF.js} +1 -1
  111. codex/static_root/assets/VCheckbox-CWBDr4kF.js.br +0 -0
  112. codex/static_root/assets/VCheckbox-CWBDr4kF.js.gz +0 -0
  113. codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.847452d574cf.js → VCheckboxBtn-COaoh3uF.fc880a386827.js} +1 -1
  114. codex/static_root/assets/VCheckboxBtn-COaoh3uF.fc880a386827.js.br +0 -0
  115. codex/static_root/assets/VCheckboxBtn-COaoh3uF.fc880a386827.js.gz +0 -0
  116. codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.js → VCheckboxBtn-COaoh3uF.js} +1 -1
  117. codex/static_root/assets/VCheckboxBtn-COaoh3uF.js.br +0 -0
  118. codex/static_root/assets/VCheckboxBtn-COaoh3uF.js.gz +0 -0
  119. codex/static_root/assets/{VCombobox-C8QLNlSl.6f431e19a453.js → VCombobox-DkOruNH0.f30f163ee632.js} +1 -1
  120. codex/static_root/assets/VCombobox-DkOruNH0.f30f163ee632.js.br +0 -0
  121. codex/static_root/assets/VCombobox-DkOruNH0.f30f163ee632.js.gz +0 -0
  122. codex/static_root/assets/{VCombobox-C8QLNlSl.js → VCombobox-DkOruNH0.js} +1 -1
  123. codex/static_root/assets/VCombobox-DkOruNH0.js.br +0 -0
  124. codex/static_root/assets/VCombobox-DkOruNH0.js.gz +0 -0
  125. codex/static_root/assets/{VDialog-RVLeW7je.c0c7eef71eec.js → VDialog-Dr7SuC2S.b6ec7df390f4.js} +1 -1
  126. codex/static_root/assets/VDialog-Dr7SuC2S.b6ec7df390f4.js.br +0 -0
  127. codex/static_root/assets/VDialog-Dr7SuC2S.b6ec7df390f4.js.gz +0 -0
  128. codex/static_root/assets/{VDialog-RVLeW7je.js → VDialog-Dr7SuC2S.js} +1 -1
  129. codex/static_root/assets/VDialog-Dr7SuC2S.js.br +0 -0
  130. codex/static_root/assets/VDialog-Dr7SuC2S.js.gz +0 -0
  131. codex/static_root/assets/{VDivider-D1Ot4vR2.9dbee744fb3c.js → VDivider-By4u2EsM.fd5ee9cce906.js} +1 -1
  132. codex/static_root/assets/VDivider-By4u2EsM.fd5ee9cce906.js.br +0 -0
  133. codex/static_root/assets/VDivider-By4u2EsM.fd5ee9cce906.js.gz +0 -0
  134. codex/static_root/assets/{VDivider-D1Ot4vR2.js → VDivider-By4u2EsM.js} +1 -1
  135. codex/static_root/assets/VDivider-By4u2EsM.js.br +0 -0
  136. codex/static_root/assets/VDivider-By4u2EsM.js.gz +0 -0
  137. codex/static_root/assets/{VExpansionPanels-DQilGHZk.43f06d0a45e0.js → VExpansionPanels-BhO9oj0a.e99bea455c78.js} +1 -1
  138. codex/static_root/assets/VExpansionPanels-BhO9oj0a.e99bea455c78.js.br +0 -0
  139. codex/static_root/assets/VExpansionPanels-BhO9oj0a.e99bea455c78.js.gz +0 -0
  140. codex/static_root/assets/{VExpansionPanels-DQilGHZk.js → VExpansionPanels-BhO9oj0a.js} +1 -1
  141. codex/static_root/assets/VExpansionPanels-BhO9oj0a.js.br +0 -0
  142. codex/static_root/assets/VExpansionPanels-BhO9oj0a.js.gz +0 -0
  143. codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js +1 -0
  144. codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js.br +0 -0
  145. codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js.gz +0 -0
  146. codex/static_root/assets/VForm-XhA6T0Lc.js +1 -0
  147. codex/static_root/assets/VForm-XhA6T0Lc.js.br +0 -0
  148. codex/static_root/assets/VForm-XhA6T0Lc.js.gz +0 -0
  149. codex/static_root/assets/{VRadioGroup-DoayJpcS.fe5e0e74ffee.js → VRadioGroup-C1WCG0rO.6ebc88e4f144.js} +1 -1
  150. codex/static_root/assets/VRadioGroup-C1WCG0rO.6ebc88e4f144.js.br +3 -0
  151. codex/static_root/assets/VRadioGroup-C1WCG0rO.6ebc88e4f144.js.gz +0 -0
  152. codex/static_root/assets/{VRadioGroup-DoayJpcS.js → VRadioGroup-C1WCG0rO.js} +1 -1
  153. codex/static_root/assets/VRadioGroup-C1WCG0rO.js.br +3 -0
  154. codex/static_root/assets/VRadioGroup-C1WCG0rO.js.gz +0 -0
  155. codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js +1 -0
  156. codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js.br +0 -0
  157. codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js.gz +0 -0
  158. codex/static_root/assets/VSelect-Bbt1vrBg.js +1 -0
  159. codex/static_root/assets/VSelect-Bbt1vrBg.js.br +0 -0
  160. codex/static_root/assets/VSelect-Bbt1vrBg.js.gz +0 -0
  161. codex/static_root/assets/{VSelectionControl-TaKCeZgb.ad6c96efe57c.js → VSelectionControl-BkZZAsWo.ba6eca944e2e.js} +1 -1
  162. codex/static_root/assets/VSelectionControl-BkZZAsWo.ba6eca944e2e.js.br +0 -0
  163. codex/static_root/assets/VSelectionControl-BkZZAsWo.ba6eca944e2e.js.gz +0 -0
  164. codex/static_root/assets/{VSelectionControl-TaKCeZgb.js → VSelectionControl-BkZZAsWo.js} +1 -1
  165. codex/static_root/assets/VSelectionControl-BkZZAsWo.js.br +0 -0
  166. codex/static_root/assets/VSelectionControl-BkZZAsWo.js.gz +0 -0
  167. codex/static_root/assets/{VTable-BfcOiEpa.77ef3973bb54.js → VTable-D7P2eIc2.5e94bf2a515b.js} +1 -1
  168. codex/static_root/assets/VTable-D7P2eIc2.5e94bf2a515b.js.br +0 -0
  169. codex/static_root/assets/VTable-D7P2eIc2.5e94bf2a515b.js.gz +0 -0
  170. codex/static_root/assets/{VTable-BfcOiEpa.js → VTable-D7P2eIc2.js} +1 -1
  171. codex/static_root/assets/VTable-D7P2eIc2.js.br +0 -0
  172. codex/static_root/assets/VTable-D7P2eIc2.js.gz +0 -0
  173. codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js +1 -0
  174. codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js.br +0 -0
  175. codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js.gz +0 -0
  176. codex/static_root/assets/VWindowItem-Dm2szrjK.js +1 -0
  177. codex/static_root/assets/VWindowItem-Dm2szrjK.js.br +0 -0
  178. codex/static_root/assets/VWindowItem-Dm2szrjK.js.gz +0 -0
  179. codex/static_root/assets/{admin-80eqCqtz.d6deb9edb8cb.js → admin-B4z2nA5P.da0c92500311.js} +1 -1
  180. codex/static_root/assets/admin-B4z2nA5P.da0c92500311.js.br +0 -0
  181. codex/static_root/assets/admin-B4z2nA5P.da0c92500311.js.gz +0 -0
  182. codex/static_root/assets/{admin-80eqCqtz.js → admin-B4z2nA5P.js} +1 -1
  183. codex/static_root/assets/admin-B4z2nA5P.js.br +0 -0
  184. codex/static_root/assets/admin-B4z2nA5P.js.gz +0 -0
  185. codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js +1 -0
  186. codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js.br +0 -0
  187. codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js.gz +0 -0
  188. codex/static_root/assets/admin-BhW3PNO0.js +1 -0
  189. codex/static_root/assets/admin-BhW3PNO0.js.br +0 -0
  190. codex/static_root/assets/admin-BhW3PNO0.js.gz +0 -0
  191. codex/static_root/assets/{admin-menu-_aAGknKO.0c08c9bd2e7d.js → admin-menu-CL7S-xqR.92b6d84c4b70.js} +1 -1
  192. codex/static_root/assets/admin-menu-CL7S-xqR.92b6d84c4b70.js.br +0 -0
  193. codex/static_root/assets/admin-menu-CL7S-xqR.92b6d84c4b70.js.gz +0 -0
  194. codex/static_root/assets/{admin-menu-_aAGknKO.js → admin-menu-CL7S-xqR.js} +1 -1
  195. codex/static_root/assets/admin-menu-CL7S-xqR.js.br +0 -0
  196. codex/static_root/assets/admin-menu-CL7S-xqR.js.gz +0 -0
  197. codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js → admin-settings-button-progress-BgBgdELw.52d052e38bc1.js} +1 -1
  198. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.52d052e38bc1.js.br +0 -0
  199. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.52d052e38bc1.js.gz +0 -0
  200. codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.js → admin-settings-button-progress-BgBgdELw.js} +1 -1
  201. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.js.br +0 -0
  202. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.js.gz +0 -0
  203. codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js +1 -0
  204. codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js.br +0 -0
  205. codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js.gz +0 -0
  206. codex/static_root/assets/browser-Bi5ov5k8.js +1 -0
  207. codex/static_root/assets/browser-Bi5ov5k8.js.br +0 -0
  208. codex/static_root/assets/browser-Bi5ov5k8.js.gz +0 -0
  209. codex/static_root/assets/{browser-chTZ1CM4.521193ab0710.css → browser-DNzQvgkY.aea8b7d7ca40.css} +1 -1
  210. codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.br +0 -0
  211. codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.gz +0 -0
  212. codex/static_root/assets/{browser-chTZ1CM4.css → browser-DNzQvgkY.css} +1 -1
  213. codex/static_root/assets/browser-DNzQvgkY.css.br +0 -0
  214. codex/static_root/assets/browser-DNzQvgkY.css.gz +0 -0
  215. codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js → change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js} +1 -1
  216. codex/static_root/assets/change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js.br +0 -0
  217. codex/static_root/assets/change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js.gz +0 -0
  218. codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.js → change-password-dialog-BKar2Bhb.js} +1 -1
  219. codex/static_root/assets/change-password-dialog-BKar2Bhb.js.br +0 -0
  220. codex/static_root/assets/change-password-dialog-BKar2Bhb.js.gz +0 -0
  221. codex/static_root/assets/{confirm-dialog-DqCey9Iq.e315d7f8f752.js → confirm-dialog-BoBL4OoS.abef9614fea9.js} +1 -1
  222. codex/static_root/assets/confirm-dialog-BoBL4OoS.abef9614fea9.js.br +0 -0
  223. codex/static_root/assets/confirm-dialog-BoBL4OoS.abef9614fea9.js.gz +0 -0
  224. codex/static_root/assets/{confirm-dialog-DqCey9Iq.js → confirm-dialog-BoBL4OoS.js} +1 -1
  225. codex/static_root/assets/confirm-dialog-BoBL4OoS.js.br +0 -0
  226. codex/static_root/assets/confirm-dialog-BoBL4OoS.js.gz +0 -0
  227. codex/static_root/assets/{datetime-column-CBA8bYeO.17a31f189d66.js → datetime-column-BsiYRUKO.cadd764c06f4.js} +1 -1
  228. codex/static_root/assets/datetime-column-BsiYRUKO.cadd764c06f4.js.br +0 -0
  229. codex/static_root/assets/datetime-column-BsiYRUKO.cadd764c06f4.js.gz +0 -0
  230. codex/static_root/assets/{datetime-column-CBA8bYeO.js → datetime-column-BsiYRUKO.js} +1 -1
  231. codex/static_root/assets/datetime-column-BsiYRUKO.js.br +0 -0
  232. codex/static_root/assets/datetime-column-BsiYRUKO.js.gz +0 -0
  233. codex/static_root/assets/{filter-mnO3lLPo.659442401b16.js → filter-HnY0knto.07f807227dcc.js} +1 -1
  234. codex/static_root/assets/filter-HnY0knto.07f807227dcc.js.br +0 -0
  235. codex/static_root/assets/filter-HnY0knto.07f807227dcc.js.gz +0 -0
  236. codex/static_root/assets/{filter-mnO3lLPo.js → filter-HnY0knto.js} +1 -1
  237. codex/static_root/assets/filter-HnY0knto.js.br +0 -0
  238. codex/static_root/assets/filter-HnY0knto.js.gz +0 -0
  239. codex/static_root/assets/{flag-tab-Bc677pNb.1b8a2f7c0dc3.js → flag-tab-CcS5pve2.a1df5d92e222.js} +1 -1
  240. codex/static_root/assets/flag-tab-CcS5pve2.a1df5d92e222.js.br +0 -0
  241. codex/static_root/assets/flag-tab-CcS5pve2.a1df5d92e222.js.gz +0 -0
  242. codex/static_root/assets/{flag-tab-Bc677pNb.js → flag-tab-CcS5pve2.js} +1 -1
  243. codex/static_root/assets/flag-tab-CcS5pve2.js.br +0 -0
  244. codex/static_root/assets/flag-tab-CcS5pve2.js.gz +0 -0
  245. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css +1 -0
  246. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.br +0 -0
  247. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.gz +0 -0
  248. codex/static_root/assets/flag-tab-D2MYXCHk.css +1 -0
  249. codex/static_root/assets/flag-tab-D2MYXCHk.css.br +0 -0
  250. codex/static_root/assets/flag-tab-D2MYXCHk.css.gz +0 -0
  251. codex/static_root/assets/{forwardRefs-DPRKg5wq.6f09e9d22fa6.js → forwardRefs-i80xmxX3.db393aeef392.js} +1 -1
  252. codex/static_root/assets/forwardRefs-i80xmxX3.db393aeef392.js.br +0 -0
  253. codex/static_root/assets/forwardRefs-i80xmxX3.db393aeef392.js.gz +0 -0
  254. codex/static_root/assets/{forwardRefs-DPRKg5wq.js → forwardRefs-i80xmxX3.js} +1 -1
  255. codex/static_root/assets/forwardRefs-i80xmxX3.js.br +0 -0
  256. codex/static_root/assets/forwardRefs-i80xmxX3.js.gz +0 -0
  257. codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js +1 -0
  258. codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js.br +0 -0
  259. codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js.gz +0 -0
  260. codex/static_root/assets/group-tab-SY0gxmHW.js +1 -0
  261. codex/static_root/assets/group-tab-SY0gxmHW.js.br +0 -0
  262. codex/static_root/assets/group-tab-SY0gxmHW.js.gz +0 -0
  263. codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js +1 -0
  264. codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js.br +0 -0
  265. codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js.gz +0 -0
  266. codex/static_root/assets/http-error-EE8gtq5A.js +1 -0
  267. codex/static_root/assets/http-error-EE8gtq5A.js.br +0 -0
  268. codex/static_root/assets/http-error-EE8gtq5A.js.gz +0 -0
  269. codex/static_root/assets/{index-9nBCksDQ.4561d2608773.js → index-gVdawuuE.a940fadbac00.js} +1 -1
  270. codex/static_root/assets/index-gVdawuuE.a940fadbac00.js.br +0 -0
  271. codex/static_root/assets/index-gVdawuuE.a940fadbac00.js.gz +0 -0
  272. codex/static_root/assets/{index-9nBCksDQ.js → index-gVdawuuE.js} +1 -1
  273. codex/static_root/assets/index-gVdawuuE.js.br +0 -0
  274. codex/static_root/assets/index-gVdawuuE.js.gz +0 -0
  275. codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js +1 -0
  276. codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js.br +0 -0
  277. codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js.gz +0 -0
  278. codex/static_root/assets/library-tab-BkDIAvyS.js +1 -0
  279. codex/static_root/assets/library-tab-BkDIAvyS.js.br +0 -0
  280. codex/static_root/assets/library-tab-BkDIAvyS.js.gz +0 -0
  281. codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js +35 -0
  282. codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js.br +0 -0
  283. codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js.gz +0 -0
  284. codex/static_root/assets/main-B5uflU6E.js +35 -0
  285. codex/static_root/assets/main-B5uflU6E.js.br +0 -0
  286. codex/static_root/assets/main-B5uflU6E.js.gz +0 -0
  287. codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js +1 -0
  288. codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js.br +0 -0
  289. codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js.gz +0 -0
  290. codex/static_root/assets/pager-full-pdf-wuQbceLs.js +1 -0
  291. codex/static_root/assets/pager-full-pdf-wuQbceLs.js.br +0 -0
  292. codex/static_root/assets/pager-full-pdf-wuQbceLs.js.gz +0 -0
  293. codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js → pagination-toolbar-D3JQmDiD.8513a4db85f0.js} +1 -1
  294. codex/static_root/assets/pagination-toolbar-D3JQmDiD.8513a4db85f0.js.br +0 -0
  295. codex/static_root/assets/pagination-toolbar-D3JQmDiD.8513a4db85f0.js.gz +0 -0
  296. codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.js → pagination-toolbar-D3JQmDiD.js} +1 -1
  297. codex/static_root/assets/pagination-toolbar-D3JQmDiD.js.br +0 -0
  298. codex/static_root/assets/pagination-toolbar-D3JQmDiD.js.gz +0 -0
  299. codex/static_root/assets/{pdf-doc-6ZE_HmLT.10d1e16abc78.js → pdf-doc-DRBtl9_2.225a6d89e842.js} +1 -1
  300. codex/static_root/assets/pdf-doc-DRBtl9_2.225a6d89e842.js.br +0 -0
  301. codex/static_root/assets/pdf-doc-DRBtl9_2.225a6d89e842.js.gz +0 -0
  302. codex/static_root/assets/{pdf-doc-6ZE_HmLT.js → pdf-doc-DRBtl9_2.js} +1 -1
  303. codex/static_root/assets/pdf-doc-DRBtl9_2.js.br +0 -0
  304. codex/static_root/assets/pdf-doc-DRBtl9_2.js.gz +0 -0
  305. codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js +2 -0
  306. codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js.br +0 -0
  307. codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js.gz +0 -0
  308. codex/static_root/assets/reader-DycvjN42.js +2 -0
  309. codex/static_root/assets/reader-DycvjN42.js.br +0 -0
  310. codex/static_root/assets/reader-DycvjN42.js.gz +0 -0
  311. codex/static_root/assets/{relation-chips-C96hi5jm.js → relation-chips-CeHIEjvZ.04d302f4fa89.js} +1 -1
  312. codex/static_root/assets/relation-chips-CeHIEjvZ.04d302f4fa89.js.br +0 -0
  313. codex/static_root/assets/relation-chips-CeHIEjvZ.04d302f4fa89.js.gz +0 -0
  314. codex/static_root/assets/{relation-chips-C96hi5jm.9f07c9eb533a.js → relation-chips-CeHIEjvZ.js} +1 -1
  315. codex/static_root/assets/relation-chips-CeHIEjvZ.js.br +0 -0
  316. codex/static_root/assets/relation-chips-CeHIEjvZ.js.gz +0 -0
  317. codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js +2 -0
  318. codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js.br +0 -0
  319. codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js.gz +0 -0
  320. codex/static_root/assets/settings-drawer-DY8o-jF3.js +2 -0
  321. codex/static_root/assets/settings-drawer-DY8o-jF3.js.br +0 -0
  322. codex/static_root/assets/settings-drawer-DY8o-jF3.js.gz +0 -0
  323. codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js +1 -0
  324. codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js.br +0 -0
  325. codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js.gz +0 -0
  326. codex/static_root/assets/ssrBoot-DRKt_yWP.js +1 -0
  327. codex/static_root/assets/ssrBoot-DRKt_yWP.js.br +0 -0
  328. codex/static_root/assets/ssrBoot-DRKt_yWP.js.gz +0 -0
  329. codex/static_root/assets/{stats-tab-CkT2ZGUQ.b704fa46dc88.js → stats-tab-C1XYGBKj.812896e1d09c.js} +1 -1
  330. codex/static_root/assets/stats-tab-C1XYGBKj.812896e1d09c.js.br +0 -0
  331. codex/static_root/assets/stats-tab-C1XYGBKj.812896e1d09c.js.gz +0 -0
  332. codex/static_root/assets/{stats-tab-CkT2ZGUQ.js → stats-tab-C1XYGBKj.js} +1 -1
  333. codex/static_root/assets/stats-tab-C1XYGBKj.js.br +0 -0
  334. codex/static_root/assets/stats-tab-C1XYGBKj.js.gz +0 -0
  335. codex/static_root/assets/task-tab-CW4DQ3KN.81812f9208e5.css +1 -0
  336. codex/static_root/assets/{task-tab-DfdUY9Rc.478e74041f80.css.br → task-tab-CW4DQ3KN.81812f9208e5.css.br} +1 -1
  337. codex/static_root/assets/task-tab-CW4DQ3KN.81812f9208e5.css.gz +0 -0
  338. codex/static_root/assets/task-tab-CW4DQ3KN.css +1 -0
  339. codex/static_root/assets/{task-tab-DfdUY9Rc.css.br → task-tab-CW4DQ3KN.css.br} +1 -1
  340. codex/static_root/assets/task-tab-CW4DQ3KN.css.gz +0 -0
  341. codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js +1 -0
  342. codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js.br +0 -0
  343. codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js.gz +0 -0
  344. codex/static_root/assets/task-tab-lFRZ0NMd.js +1 -0
  345. codex/static_root/assets/task-tab-lFRZ0NMd.js.br +0 -0
  346. codex/static_root/assets/task-tab-lFRZ0NMd.js.gz +0 -0
  347. codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js +1 -0
  348. codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js.br +0 -0
  349. codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js.gz +0 -0
  350. codex/static_root/assets/unauthorized-Dld9cV8d.js +1 -0
  351. codex/static_root/assets/unauthorized-Dld9cV8d.js.br +0 -0
  352. codex/static_root/assets/unauthorized-Dld9cV8d.js.gz +0 -0
  353. codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js +1 -0
  354. codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js.br +0 -0
  355. codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js.gz +0 -0
  356. codex/static_root/assets/user-tab-CN9nBOMS.js +1 -0
  357. codex/static_root/assets/user-tab-CN9nBOMS.js.br +0 -0
  358. codex/static_root/assets/user-tab-CN9nBOMS.js.gz +0 -0
  359. codex/static_root/{manifest.225989a17f49.json → manifest.66d7229aa07a.json} +297 -277
  360. codex/static_root/manifest.66d7229aa07a.json.br +0 -0
  361. codex/static_root/manifest.66d7229aa07a.json.gz +0 -0
  362. codex/static_root/manifest.json +297 -277
  363. codex/static_root/manifest.json.br +0 -0
  364. codex/static_root/manifest.json.gz +0 -0
  365. codex/static_root/staticfiles.json +1 -1
  366. codex/status_controller.py +10 -13
  367. codex/urls/root.py +4 -3
  368. codex/views/admin/api_key.py +1 -4
  369. codex/views/admin/flag.py +2 -2
  370. codex/views/admin/group.py +2 -2
  371. codex/views/admin/library.py +4 -4
  372. codex/views/admin/stats.py +10 -10
  373. codex/views/admin/tasks.py +32 -13
  374. codex/views/admin/user.py +36 -10
  375. codex/views/auth.py +3 -8
  376. codex/views/bookmark.py +3 -3
  377. codex/views/browser/annotate/card.py +2 -2
  378. codex/views/browser/annotate/order.py +6 -7
  379. codex/views/browser/bookmark.py +3 -1
  380. codex/views/browser/breadcrumbs.py +6 -5
  381. codex/views/browser/browser.py +2 -2
  382. codex/views/browser/choices.py +7 -8
  383. codex/views/browser/cover.py +6 -3
  384. codex/views/browser/download.py +2 -1
  385. codex/views/browser/filters/bookmark.py +1 -1
  386. codex/views/browser/filters/field.py +1 -1
  387. codex/views/browser/filters/filter.py +6 -8
  388. codex/views/browser/filters/group.py +8 -8
  389. codex/views/browser/filters/search/field/expression.py +1 -2
  390. codex/views/browser/filters/search/field/optimize.py +1 -1
  391. codex/views/browser/filters/search/field/parse.py +2 -2
  392. codex/views/browser/filters/search/parse.py +4 -19
  393. codex/views/browser/group_mtime.py +1 -1
  394. codex/views/browser/metadata/copy_intersections.py +1 -1
  395. codex/views/browser/metadata/metadata.py +1 -1
  396. codex/views/browser/mtime.py +1 -1
  397. codex/views/browser/order_by.py +3 -5
  398. codex/views/browser/params.py +1 -1
  399. codex/views/browser/settings.py +1 -1
  400. codex/views/browser/validate.py +9 -6
  401. codex/views/const.py +0 -3
  402. codex/views/frontend.py +1 -1
  403. codex/views/mixins.py +3 -2
  404. codex/views/opds/authentication_v1.py +1 -1
  405. codex/views/opds/const.py +4 -4
  406. codex/views/opds/urls.py +1 -1
  407. codex/views/opds/util.py +4 -4
  408. codex/views/opds/v1/entry/entry.py +4 -2
  409. codex/views/opds/v1/entry/links.py +4 -5
  410. codex/views/opds/v1/facets.py +8 -4
  411. codex/views/opds/v1/feed.py +5 -5
  412. codex/views/opds/v1/links.py +11 -6
  413. codex/views/opds/v2/const.py +5 -2
  414. codex/views/opds/v2/feed.py +3 -3
  415. codex/views/opds/v2/links.py +18 -11
  416. codex/views/opds/v2/progression.py +8 -9
  417. codex/views/opds/v2/publications.py +10 -20
  418. codex/views/public.py +2 -2
  419. codex/views/reader/books.py +3 -2
  420. codex/views/reader/page.py +3 -3
  421. codex/views/reader/params.py +2 -2
  422. codex/views/reader/reader.py +6 -6
  423. codex/views/reader/settings.py +1 -1
  424. codex/views/session.py +10 -3
  425. codex/views/settings.py +2 -2
  426. codex/views/timezone.py +2 -2
  427. codex/views/util.py +10 -9
  428. codex/views/version.py +1 -1
  429. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/METADATA +3 -1
  430. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/RECORD +433 -415
  431. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/WHEEL +1 -1
  432. codex/serializers/fields.py +0 -183
  433. codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.br +0 -0
  434. codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.gz +0 -0
  435. codex/static_root/assets/VCheckbox-FaT6MGfu.js.br +0 -0
  436. codex/static_root/assets/VCheckbox-FaT6MGfu.js.gz +0 -0
  437. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.br +0 -0
  438. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.gz +0 -0
  439. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.br +0 -0
  440. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.gz +0 -0
  441. codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.br +0 -0
  442. codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.gz +0 -0
  443. codex/static_root/assets/VCombobox-C8QLNlSl.js.br +0 -0
  444. codex/static_root/assets/VCombobox-C8QLNlSl.js.gz +0 -0
  445. codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.br +0 -0
  446. codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.gz +0 -0
  447. codex/static_root/assets/VDialog-RVLeW7je.js.br +0 -0
  448. codex/static_root/assets/VDialog-RVLeW7je.js.gz +0 -0
  449. codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.br +0 -0
  450. codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.gz +0 -0
  451. codex/static_root/assets/VDivider-D1Ot4vR2.js.br +0 -0
  452. codex/static_root/assets/VDivider-D1Ot4vR2.js.gz +0 -0
  453. codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.br +0 -0
  454. codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.gz +0 -0
  455. codex/static_root/assets/VExpansionPanels-DQilGHZk.js.br +0 -0
  456. codex/static_root/assets/VExpansionPanels-DQilGHZk.js.gz +0 -0
  457. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js +0 -1
  458. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.br +0 -0
  459. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.gz +0 -0
  460. codex/static_root/assets/VForm-DNmY-qFJ.js +0 -1
  461. codex/static_root/assets/VForm-DNmY-qFJ.js.br +0 -0
  462. codex/static_root/assets/VForm-DNmY-qFJ.js.gz +0 -0
  463. codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.br +0 -0
  464. codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.gz +0 -0
  465. codex/static_root/assets/VRadioGroup-DoayJpcS.js.br +0 -0
  466. codex/static_root/assets/VRadioGroup-DoayJpcS.js.gz +0 -0
  467. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js +0 -1
  468. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.br +0 -0
  469. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.gz +0 -0
  470. codex/static_root/assets/VSelect-BSLmHX_P.js +0 -1
  471. codex/static_root/assets/VSelect-BSLmHX_P.js.br +0 -0
  472. codex/static_root/assets/VSelect-BSLmHX_P.js.gz +0 -0
  473. codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.br +0 -0
  474. codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.gz +0 -0
  475. codex/static_root/assets/VSelectionControl-TaKCeZgb.js.br +0 -0
  476. codex/static_root/assets/VSelectionControl-TaKCeZgb.js.gz +0 -0
  477. codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.br +0 -0
  478. codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.gz +0 -0
  479. codex/static_root/assets/VTable-BfcOiEpa.js.br +0 -0
  480. codex/static_root/assets/VTable-BfcOiEpa.js.gz +0 -0
  481. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js +0 -1
  482. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.br +0 -0
  483. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.gz +0 -0
  484. codex/static_root/assets/VWindowItem-D8lWcbQY.js +0 -1
  485. codex/static_root/assets/VWindowItem-D8lWcbQY.js.br +0 -0
  486. codex/static_root/assets/VWindowItem-D8lWcbQY.js.gz +0 -0
  487. codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.br +0 -0
  488. codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.gz +0 -0
  489. codex/static_root/assets/admin-80eqCqtz.js.br +0 -0
  490. codex/static_root/assets/admin-80eqCqtz.js.gz +0 -0
  491. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js +0 -1
  492. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.br +0 -0
  493. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.gz +0 -0
  494. codex/static_root/assets/admin-CBLHOrM9.js +0 -1
  495. codex/static_root/assets/admin-CBLHOrM9.js.br +0 -0
  496. codex/static_root/assets/admin-CBLHOrM9.js.gz +0 -0
  497. codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.br +0 -0
  498. codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.gz +0 -0
  499. codex/static_root/assets/admin-menu-_aAGknKO.js.br +0 -0
  500. codex/static_root/assets/admin-menu-_aAGknKO.js.gz +0 -0
  501. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.br +0 -0
  502. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.gz +0 -0
  503. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.br +0 -0
  504. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.gz +0 -0
  505. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js +0 -1
  506. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.br +0 -0
  507. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.gz +0 -0
  508. codex/static_root/assets/browser-DAprXYuP.js +0 -1
  509. codex/static_root/assets/browser-DAprXYuP.js.br +0 -0
  510. codex/static_root/assets/browser-DAprXYuP.js.gz +0 -0
  511. codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.br +0 -0
  512. codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.gz +0 -0
  513. codex/static_root/assets/browser-chTZ1CM4.css.br +0 -0
  514. codex/static_root/assets/browser-chTZ1CM4.css.gz +0 -0
  515. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.br +0 -0
  516. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.gz +0 -0
  517. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.br +0 -0
  518. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.gz +0 -0
  519. codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.br +0 -0
  520. codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.gz +0 -0
  521. codex/static_root/assets/confirm-dialog-DqCey9Iq.js.br +0 -0
  522. codex/static_root/assets/confirm-dialog-DqCey9Iq.js.gz +0 -0
  523. codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.br +0 -0
  524. codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.gz +0 -0
  525. codex/static_root/assets/datetime-column-CBA8bYeO.js.br +0 -0
  526. codex/static_root/assets/datetime-column-CBA8bYeO.js.gz +0 -0
  527. codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.br +0 -0
  528. codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.gz +0 -0
  529. codex/static_root/assets/filter-mnO3lLPo.js.br +0 -0
  530. codex/static_root/assets/filter-mnO3lLPo.js.gz +0 -0
  531. codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.br +0 -0
  532. codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.gz +0 -0
  533. codex/static_root/assets/flag-tab-Bc677pNb.js.br +0 -0
  534. codex/static_root/assets/flag-tab-Bc677pNb.js.gz +0 -0
  535. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css +0 -1
  536. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.br +0 -0
  537. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.gz +0 -0
  538. codex/static_root/assets/flag-tab-glFvEgEl.css +0 -1
  539. codex/static_root/assets/flag-tab-glFvEgEl.css.br +0 -0
  540. codex/static_root/assets/flag-tab-glFvEgEl.css.gz +0 -0
  541. codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.br +0 -0
  542. codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.gz +0 -0
  543. codex/static_root/assets/forwardRefs-DPRKg5wq.js.br +0 -0
  544. codex/static_root/assets/forwardRefs-DPRKg5wq.js.gz +0 -0
  545. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js +0 -1
  546. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.br +0 -0
  547. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.gz +0 -0
  548. codex/static_root/assets/group-tab-BVlBrMwo.js +0 -1
  549. codex/static_root/assets/group-tab-BVlBrMwo.js.br +0 -0
  550. codex/static_root/assets/group-tab-BVlBrMwo.js.gz +0 -0
  551. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js +0 -1
  552. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.br +0 -0
  553. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.gz +0 -0
  554. codex/static_root/assets/http-error-C9-_aEBF.js +0 -1
  555. codex/static_root/assets/http-error-C9-_aEBF.js.br +0 -0
  556. codex/static_root/assets/http-error-C9-_aEBF.js.gz +0 -0
  557. codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.br +0 -0
  558. codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.gz +0 -0
  559. codex/static_root/assets/index-9nBCksDQ.js.br +0 -0
  560. codex/static_root/assets/index-9nBCksDQ.js.gz +0 -0
  561. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js +0 -1
  562. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.br +0 -0
  563. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.gz +0 -0
  564. codex/static_root/assets/library-tab-BMe3dKzy.js +0 -1
  565. codex/static_root/assets/library-tab-BMe3dKzy.js.br +0 -0
  566. codex/static_root/assets/library-tab-BMe3dKzy.js.gz +0 -0
  567. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js +0 -35
  568. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.br +0 -0
  569. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.gz +0 -0
  570. codex/static_root/assets/main-BbNUiWEb.js +0 -35
  571. codex/static_root/assets/main-BbNUiWEb.js.br +0 -0
  572. codex/static_root/assets/main-BbNUiWEb.js.gz +0 -0
  573. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js +0 -1
  574. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.br +0 -0
  575. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.gz +0 -0
  576. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js +0 -1
  577. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.br +0 -0
  578. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.gz +0 -0
  579. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.br +0 -0
  580. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.gz +0 -0
  581. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.br +0 -0
  582. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.gz +0 -0
  583. codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.br +0 -0
  584. codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.gz +0 -0
  585. codex/static_root/assets/pdf-doc-6ZE_HmLT.js.br +0 -0
  586. codex/static_root/assets/pdf-doc-6ZE_HmLT.js.gz +0 -0
  587. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js +0 -2
  588. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.br +0 -0
  589. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.gz +0 -0
  590. codex/static_root/assets/reader-DdPKDuh5.js +0 -2
  591. codex/static_root/assets/reader-DdPKDuh5.js.br +0 -0
  592. codex/static_root/assets/reader-DdPKDuh5.js.gz +0 -0
  593. codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.br +0 -0
  594. codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.gz +0 -0
  595. codex/static_root/assets/relation-chips-C96hi5jm.js.br +0 -0
  596. codex/static_root/assets/relation-chips-C96hi5jm.js.gz +0 -0
  597. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js +0 -2
  598. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.br +0 -0
  599. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.gz +0 -0
  600. codex/static_root/assets/settings-drawer-CqbT1rid.js +0 -2
  601. codex/static_root/assets/settings-drawer-CqbT1rid.js.br +0 -0
  602. codex/static_root/assets/settings-drawer-CqbT1rid.js.gz +0 -0
  603. codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.br +0 -0
  604. codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.gz +0 -0
  605. codex/static_root/assets/stats-tab-CkT2ZGUQ.js.br +0 -0
  606. codex/static_root/assets/stats-tab-CkT2ZGUQ.js.gz +0 -0
  607. codex/static_root/assets/task-tab-DfdUY9Rc.478e74041f80.css +0 -1
  608. codex/static_root/assets/task-tab-DfdUY9Rc.478e74041f80.css.gz +0 -0
  609. codex/static_root/assets/task-tab-DfdUY9Rc.css +0 -1
  610. codex/static_root/assets/task-tab-DfdUY9Rc.css.gz +0 -0
  611. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js +0 -1
  612. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.br +0 -0
  613. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.gz +0 -0
  614. codex/static_root/assets/task-tab-a-DcU3Tj.js +0 -1
  615. codex/static_root/assets/task-tab-a-DcU3Tj.js.br +0 -0
  616. codex/static_root/assets/task-tab-a-DcU3Tj.js.gz +0 -0
  617. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js +0 -1
  618. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.br +0 -0
  619. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.gz +0 -0
  620. codex/static_root/assets/unauthorized-BOl5YpL3.js +0 -1
  621. codex/static_root/assets/unauthorized-BOl5YpL3.js.br +0 -0
  622. codex/static_root/assets/unauthorized-BOl5YpL3.js.gz +0 -0
  623. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js +0 -1
  624. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.br +0 -0
  625. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.gz +0 -0
  626. codex/static_root/assets/user-tab-BBDZvD8G.js +0 -1
  627. codex/static_root/assets/user-tab-BBDZvD8G.js.br +0 -0
  628. codex/static_root/assets/user-tab-BBDZvD8G.js.gz +0 -0
  629. codex/static_root/manifest.225989a17f49.json.br +0 -0
  630. codex/static_root/manifest.225989a17f49.json.gz +0 -0
  631. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/LICENSE +0 -0
  632. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/entry_points.txt +0 -0
@@ -173,13 +173,13 @@ class BrowserView(BrowserTitleView):
173
173
  else:
174
174
  zero_pad = 1
175
175
 
176
- # self._debug_queries(page_group_count, page_book_count, group_qs, book_qs)
176
+ # self._debug_queries(page_group_count, page_book_count, group_qs, book_qs) # noqa: ERA001
177
177
 
178
178
  total_page_count = page_group_count + page_book_count
179
179
  mtime = self._get_page_mtime()
180
180
  return group_qs, book_qs, num_pages, total_page_count, zero_pad, mtime
181
181
 
182
- def get_object(self): # type: ignore
182
+ def get_object(self):
183
183
  """Validate settings and get the querysets."""
184
184
  group_qs, book_qs, num_pages, total_count, zero_pad, mtime = (
185
185
  self._get_group_and_books()
@@ -8,7 +8,7 @@ from django.db.models import QuerySet
8
8
  from drf_spectacular.utils import extend_schema
9
9
  from rest_framework.response import Response
10
10
 
11
- from codex.choices import DUMMY_NULL_NAME, VUETIFY_NULL_CODE
11
+ from codex.choices.browser import DUMMY_NULL_NAME, VUETIFY_NULL_CODE
12
12
  from codex.logger.logging import get_logger
13
13
  from codex.models import (
14
14
  Comic,
@@ -18,8 +18,6 @@ from codex.models import (
18
18
  from codex.models.named import IdentifierType
19
19
  from codex.serializers.browser.choices import (
20
20
  BrowserChoicesFilterSerializer,
21
- )
22
- from codex.serializers.browser.filters import (
23
21
  BrowserFilterChoicesSerializer,
24
22
  )
25
23
  from codex.serializers.browser.settings import BrowserFilterChoicesInputSerilalizer
@@ -98,7 +96,7 @@ class BrowserChoicesViewBase(BrowserFilterView):
98
96
 
99
97
  return rel, model
100
98
 
101
- def get_object(self) -> QuerySet: # type: ignore
99
+ def get_object(self) -> QuerySet:
102
100
  """Get the comic subquery use for the choices."""
103
101
  return self.get_filtered_queryset(Comic)
104
102
 
@@ -136,12 +134,13 @@ class BrowserChoicesAvailableView(BrowserChoicesViewBase):
136
134
 
137
135
  return count
138
136
 
139
- def get_object(self) -> dict[str, Any]: # type: ignore
137
+ def get_object(self) -> dict[str, Any]: # type: ignore[reportIncompatibleMethodOverride]
140
138
  """Get choice counts."""
141
139
  qs = super().get_object()
142
140
  filters = self.params.get("filters", {})
143
141
  data = {}
144
- for field_name in self.serializer_class().get_fields(): # type: ignore
142
+ serializer: BrowserFilterChoicesSerializer = self.serializer_class() # type: ignore[reportOptionalCall, reportAssignmentType]
143
+ for field_name in serializer.get_fields():
145
144
  if field_name == "story_arcs" and qs.model is StoryArc:
146
145
  # don't allow filtering on story arc in story arc view.
147
146
  continue
@@ -154,7 +153,7 @@ class BrowserChoicesAvailableView(BrowserChoicesViewBase):
154
153
 
155
154
  try:
156
155
  is_filter_set = bool(filters.get(field_name))
157
- flag = is_filter_set or count > 1 # type: ignore
156
+ flag = is_filter_set or count > 1
158
157
  except TypeError:
159
158
  flag = False
160
159
  data[field_name] = flag
@@ -185,7 +184,7 @@ class BrowserChoicesView(BrowserChoicesViewBase):
185
184
  field_name = self.kwargs.get("field_name", "")
186
185
  return snakecase(field_name)
187
186
 
188
- def get_object(self) -> dict[str, Any]: # type: ignore
187
+ def get_object(self) -> dict[str, Any]: # type: ignore[reportIncompatibleMethodOverride]
189
188
  """Return choices with more than one choice."""
190
189
  qs = super().get_object()
191
190
  field_name = self._get_field_name()
@@ -52,7 +52,7 @@ class CoverView(BrowserAnnotateOrderView):
52
52
  BrowserAnnotateOrderView.REPARSE_JSON_FIELDS | {"parent"}
53
53
  )
54
54
 
55
- def get_group_filter(self, group=None, pks=None, page_mtime=False):
55
+ def get_group_filter(self, group=None, pks=None, page_mtime=False): # noqa: FBT002
56
56
  """Get group filter for First Cover View."""
57
57
  if self.params.get("dynamic_covers") or self.model in (Volume, Folder):
58
58
  return super().get_group_filter(group=group, pks=pks, page_mtime=page_mtime)
@@ -60,7 +60,10 @@ class CoverView(BrowserAnnotateOrderView):
60
60
  # First cover group filter relies on sort names to look outside the browser supplied pks
61
61
  # For multi_groups not in the browser query.
62
62
  pks = self.kwargs["pks"]
63
- qs = self.model.objects.filter(pk__in=pks) # type: ignore
63
+ if not self.model:
64
+ qs = Comic.objects.none()
65
+ else:
66
+ qs = self.model.objects.filter(pk__in=pks)
64
67
  sort_names = qs.values_list("sort_name", flat=True).distinct()
65
68
  model_rel = GROUP_RELATION[self.model_group]
66
69
  group_filter = {f"{model_rel}__sort_name__in": sort_names}
@@ -142,7 +145,7 @@ class CoverView(BrowserAnnotateOrderView):
142
145
  parameters=[BrowserAnnotateOrderView.input_serializer_class],
143
146
  responses={(200, content_type): OpenApiTypes.BINARY},
144
147
  )
145
- def get(self, *args, **kwargs): # type: ignore
148
+ def get(self, *args, **kwargs):
146
149
  """Get comic cover."""
147
150
  try:
148
151
  try:
@@ -54,7 +54,8 @@ class GroupDownloadView(BrowserFilterView):
54
54
  filename = kwargs.get("filename")
55
55
  if not filename:
56
56
  pks = self.kwargs.get("pks")
57
- filename = f"{self.model.__name__} {pks} Comics.zip" # type: ignore
57
+ name = self.model.__name__ if self.model else "No Model"
58
+ filename = f"{name} {pks} Comics.zip"
58
59
 
59
60
  headers = {"Content-Length": len(zs), "Last-Modified": zs.last_modified}
60
61
  return FileResponse(
@@ -11,7 +11,7 @@ class BrowserFilterBookmarkView(BookmarkFilterMixin, BrowserValidateView):
11
11
 
12
12
  def get_bookmark_filter(self, model):
13
13
  """Build bookmark query."""
14
- choice: str = self.params.get("filters", {}).get("bookmark", "") # type: ignore
14
+ choice: str = self.params.get("filters", {}).get("bookmark", "")
15
15
  if choice:
16
16
  bm_rel = self.get_bm_rel(model)
17
17
  my_bookmark_filter = self.get_my_bookmark_filter(bm_rel)
@@ -55,5 +55,5 @@ class ComicFieldFilterView(GroupFilterView):
55
55
  def get_comic_field_filter(self, model):
56
56
  """Filter the comics based on the form filters."""
57
57
  rel_prefix = self.get_rel_prefix(model)
58
- filters = self.params["filters"] # type: ignore
58
+ filters = self.params["filters"]
59
59
  return self.get_all_comic_field_filters(rel_prefix, filters)
@@ -13,8 +13,6 @@ LOG = get_logger(__name__)
13
13
  class BrowserFilterView(BrowserFilterBookmarkView):
14
14
  """Browser Filters."""
15
15
 
16
- TARGET = ""
17
-
18
16
  def force_inner_joins(self, qs):
19
17
  """Force INNER JOINS to filter empty groups."""
20
18
  demote_tables = {"codex_library"}
@@ -28,10 +26,10 @@ class BrowserFilterView(BrowserFilterBookmarkView):
28
26
  def _get_query_filters(
29
27
  self,
30
28
  model,
29
+ page_mtime,
30
+ bookmark_filter,
31
31
  group=None,
32
32
  pks=None,
33
- page_mtime=False,
34
- bookmark_filter=True,
35
33
  ) -> Q:
36
34
  """Return all the filters except the group filter."""
37
35
  big_include_filter = Q()
@@ -54,16 +52,16 @@ class BrowserFilterView(BrowserFilterBookmarkView):
54
52
  model,
55
53
  group=None,
56
54
  pks=None,
57
- page_mtime=False,
58
- bookmark_filter=True,
55
+ page_mtime=False, # noqa: FBT002
56
+ bookmark_filter=True, # noqa: FBT002
59
57
  ) -> QuerySet:
60
58
  """Get a filtered queryset for the model."""
61
59
  query_filters = self._get_query_filters(
62
60
  model,
63
- group=group,
64
- pks=pks,
65
61
  page_mtime=page_mtime,
66
62
  bookmark_filter=bookmark_filter,
63
+ group=group,
64
+ pks=pks,
67
65
  )
68
66
  # Distinct necissary for folder view with search
69
67
  return model.objects.filter(query_filters).distinct()
@@ -9,7 +9,7 @@ from codex.views.const import (
9
9
  GROUP_RELATION,
10
10
  )
11
11
 
12
- _GROUP_REL_TARGETS = frozenset({"cover", "choices"})
12
+ _GROUP_REL_TARGETS = frozenset({"cover", "choices", "bookmark"})
13
13
  _PK_REL_TARGETS = frozenset({"metadata", "mtime"})
14
14
 
15
15
 
@@ -18,16 +18,16 @@ class GroupFilterView(BrowserParamsView):
18
18
 
19
19
  SESSION_KEY = BrowserParamsView.BROWSER_SESSION_KEY
20
20
 
21
- def _get_rel_for_pks(self, group, page_mtime=False):
21
+ TARGET = ""
22
+
23
+ def _get_rel_for_pks(self, group, page_mtime: bool):
22
24
  """Get the relation from the model to the pks."""
23
- # XXX these TARGET refs might be better as subclass get rel methods.
24
- target: str = self.TARGET # type: ignore
25
- if target in _GROUP_REL_TARGETS:
25
+ if self.TARGET in _GROUP_REL_TARGETS:
26
26
  rel = FILTER_ONLY_GROUP_RELATION[group]
27
- elif target in _PK_REL_TARGETS or page_mtime:
27
+ elif self.TARGET in _PK_REL_TARGETS or page_mtime:
28
28
  # metadata, mtime, browser.page_mtime
29
29
  rel = "pk"
30
- elif target == "download":
30
+ elif self.TARGET == "download":
31
31
  rel = "comic__folders" if group == "f" else "pk"
32
32
  else:
33
33
  # browser.group, opds
@@ -36,7 +36,7 @@ class GroupFilterView(BrowserParamsView):
36
36
  rel += "__in"
37
37
  return rel
38
38
 
39
- def get_group_filter(self, group=None, pks=None, page_mtime=False):
39
+ def get_group_filter(self, group=None, pks=None, page_mtime=False): # noqa: FBT002
40
40
  """Get filter for the displayed group."""
41
41
  if group is None:
42
42
  group = self.kwargs["group"]
@@ -79,8 +79,7 @@ def _cast_value(rel, rel_class, value):
79
79
  value = parse(value)
80
80
  if rel_class == DateField and value:
81
81
  value = value.date()
82
- # elif rel_class in (CharField, TextField):
83
- # value = value
82
+ # (CharField, TextField):
84
83
  return value
85
84
 
86
85
 
@@ -1,5 +1,5 @@
1
1
  """Optimize Text Lookups."""
2
- # XXX Unused XXX multiple like queries test better
2
+ # UNUSED CODE multiple like queries test better
3
3
 
4
4
  import re
5
5
 
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
22
22
  from pyparsing.helpers import InfixNotationOperatorSpec
23
23
 
24
24
  _QUOTES_REXP = r"(?:\".*?\")"
25
- _OPERATORS_REXP = "|".join(("and not", "or not", "and", "or"))
25
+ _OPERATORS_REXP = "and not|or not|and|or"
26
26
  _BEGIN_NOT_REXP = r"^\s*\(?\s*(?P<not>not)"
27
27
  _IMPLICIT_AND_REXP = (
28
28
  rf"{_QUOTES_REXP}|\ (?P<ok>{_OPERATORS_REXP})\ |(?P<bare>(?:\ not)?\ )\S"
@@ -183,5 +183,5 @@ def get_field_query(rel, rel_class, exp, model, many_to_many):
183
183
  bool_expr = _create_context_expression(context)
184
184
 
185
185
  parsed_result = bool_expr.parse_string(exp)
186
- root_bool_operand: BoolOperand = parsed_result[0] # type:ignore
186
+ root_bool_operand: BoolOperand = parsed_result[0] # type:ignore[reportAssignmentType]
187
187
  return root_bool_operand.to_query()
@@ -149,7 +149,7 @@ class SearchFilterView(BrowserFTSFilter):
149
149
 
150
150
  def _preparse_search_query(self):
151
151
  """Preparse search fields out of query text."""
152
- text = self.params.get("q") # type: ignore
152
+ text = self.params.get("q")
153
153
  field_tokens = {}
154
154
  if not text:
155
155
  return field_tokens, text
@@ -177,7 +177,7 @@ class SearchFilterView(BrowserFTSFilter):
177
177
  )
178
178
  else:
179
179
  # AND and OR
180
- # XXX cannot do OR queries with MATCH, it decontextualizes MATCH somehow.
180
+ # Cannot do OR queries with MATCH, it decontextualizes MATCH somehow.
181
181
  if preop == "or":
182
182
  self.search_error = "OR preceding column tokens with operator expressions will act as AND"
183
183
  include_q_list, exclude_q_list = self.get_search_field_filters(
@@ -227,24 +227,9 @@ class SearchFilterView(BrowserFTSFilter):
227
227
 
228
228
  return include_q, exclude_q, fts_q
229
229
 
230
- def _is_search_results_limited(self) -> bool:
231
- """Get search result limit from params."""
232
- # user = self.request.user # type: ignore
233
- # if user and user.is_authenticated:
234
- # limited = bool(
235
- # self.params.get( # type: ignore
236
- # "search_results_limit",
237
- # MAX_OBJ_PER_PAGE,
238
- # )
239
- # )
240
- # else:
241
- # limited = True
242
- # return limited
243
- return True
244
-
245
230
  def get_search_limit(self):
246
231
  """Get search scores for choices and metadata."""
247
- if not self.search_mode or not self._is_search_results_limited():
232
+ if not self.search_mode:
248
233
  return 0
249
- page = self.kwargs.get("page", 1) # type: ignore
234
+ page = self.kwargs.get("page", 1)
250
235
  return page * MAX_OBJ_PER_PAGE + 1
@@ -54,7 +54,7 @@ class BrowserGroupMtimeView(BrowserFilterView):
54
54
  args["distinct"] = True
55
55
  return agg_func(bmua_rel, **args)
56
56
 
57
- def get_group_mtime(self, model, group=None, pks=None, page_mtime=False):
57
+ def get_group_mtime(self, model, group=None, pks=None, page_mtime=False): # noqa: FBT002
58
58
  """Get a filtered mtime for browser pages and mtime checker."""
59
59
  qs = self.get_filtered_queryset(
60
60
  model,
@@ -47,7 +47,7 @@ class MetadataCopyIntersectionsView(MetadataQueryIntersectionsView):
47
47
  @classmethod
48
48
  def _copy_m2m_intersections(cls, obj, m2m_intersections):
49
49
  """Copy the m2m intersections into the object."""
50
- # XXX It might even be faster to copy everything to a dict and not use the obj.
50
+ # It might even be faster to copy everything to a dict and not use the obj.
51
51
  for key, qs in m2m_intersections.items():
52
52
  serializer_key = (
53
53
  f"{PREFETCH_PREFIX}{key}"
@@ -63,7 +63,7 @@ class MetadataView(MetadataCopyIntersectionsView):
63
63
  obj = qs[0]
64
64
  if not obj:
65
65
  reason = "Empty obj"
66
- raise ValueError(reason) # noqa TRY301
66
+ raise ValueError(reason) # noqa: TRY301
67
67
  except (IndexError, ValueError) as exc:
68
68
  return self._raise_not_found(exc)
69
69
 
@@ -37,7 +37,7 @@ class MtimeView(BrowserGroupMtimeView):
37
37
  """Get max mtime for all groups."""
38
38
  max_mtime = None
39
39
 
40
- for item in self.params["groups"]: # type: ignore
40
+ for item in self.params["groups"]:
41
41
  mtime = self._get_group_mtime(item)
42
42
  max_mtime = max_none(max_mtime, mtime)
43
43
  return max_mtime
@@ -55,7 +55,7 @@ class BrowserOrderByView(BrowserGroupMtimeView):
55
55
  order_fields_head += ["bookmark_updated_at"]
56
56
  return order_fields_head
57
57
 
58
- def add_order_by(self, qs, order_key="", do_reverse=True, comic_sort_names=None):
58
+ def add_order_by(self, qs, order_key="", comic_sort_names=None):
59
59
  """Create the order_by list."""
60
60
  order_fields_head = ()
61
61
  if qs.model is Comic:
@@ -65,11 +65,9 @@ class BrowserOrderByView(BrowserGroupMtimeView):
65
65
 
66
66
  order_fields = (*order_fields_head, "pk")
67
67
 
68
- prefix = "-" if do_reverse and self.params.get("order_reverse") else ""
69
- order_by = []
68
+ prefix = "-" if self.params.get("order_reverse") else ""
70
69
  if prefix:
71
- for field in order_fields:
72
- order_by.append(prefix + field)
70
+ order_by = [prefix + field for field in order_fields]
73
71
  else:
74
72
  order_by = order_fields
75
73
 
@@ -58,7 +58,7 @@ class BrowserParamsView(SessionView):
58
58
  validated_data = serializer.validated_data
59
59
 
60
60
  if validated_data:
61
- params.update(validated_data) # type: ignore
61
+ params.update(validated_data)
62
62
  self._params = MappingProxyType(params)
63
63
  self.validate_settings()
64
64
  return self._params
@@ -9,7 +9,7 @@ class BrowserSettingsView(SettingsView):
9
9
  """Get Browser Settings."""
10
10
 
11
11
  # Put Browser Settings is normally done through BrowserView.get()
12
- serializer_class = BrowserSettingsSerializer # type: ignore
12
+ serializer_class = BrowserSettingsSerializer
13
13
 
14
14
  SESSION_KEY = SettingsView.BROWSER_SESSION_KEY
15
15
 
@@ -3,10 +3,11 @@
3
3
  from collections.abc import Mapping
4
4
  from copy import deepcopy
5
5
  from types import MappingProxyType
6
+ from typing import Any
6
7
 
7
8
  from rest_framework.exceptions import NotFound
8
9
 
9
- from codex.choices import DEFAULT_BROWSER_ROUTE
10
+ from codex.choices.browser import DEFAULT_BROWSER_ROUTE
10
11
  from codex.exceptions import SeeOtherRedirectError
11
12
  from codex.logger.logging import get_logger
12
13
  from codex.models.groups import BrowserGroupModel
@@ -73,17 +74,18 @@ class BrowserValidateView(SearchFilterView):
73
74
  self, reason, route_mask=None, settings_mask: Mapping | None = None
74
75
  ):
75
76
  """Redirect the client to a valid group url."""
76
- route = mapping_to_dict(self.DEFAULT_ROUTE)
77
+ route: dict[str, Any] = mapping_to_dict(self.DEFAULT_ROUTE) # type:ignore[reportAssignmentType]
77
78
  if route_mask:
78
- route["params"].update(route_mask) # type: ignore
79
- settings: dict = deepcopy(mapping_to_dict(self.params)) # type: ignore
79
+ route["params"].update(route_mask)
80
+ settings: dict[str, Any] = deepcopy(mapping_to_dict(self.params)) # type: ignore[reportAssignmentType]
80
81
  if settings_mask:
81
82
  settings.update(settings_mask)
82
83
  detail = {"route": route, "settings": settings, "reason": reason}
83
84
  raise SeeOtherRedirectError(detail=detail)
84
85
 
85
86
  def _get_valid_browse_top_groups(self):
86
- """Get valid top groups for the current settings.
87
+ """
88
+ Get valid top groups for the current settings.
87
89
 
88
90
  Valid top groups are determined by the Browser Settings.
89
91
  """
@@ -118,7 +120,8 @@ class BrowserValidateView(SearchFilterView):
118
120
  self.raise_redirect(reason, route, settings_mask)
119
121
 
120
122
  def _get_valid_browse_nav_groups(self, valid_top_groups):
121
- """Get valid nav groups for the current settings.
123
+ """
124
+ Get valid nav groups for the current settings.
122
125
 
123
126
  Valid nav groups are the top group and below that are also
124
127
  enabled in browser settings.
codex/views/const.py CHANGED
@@ -76,9 +76,6 @@ METADATA_GROUP_RELATION = MappingProxyType(
76
76
  {
77
77
  **GROUP_NAME_MAP,
78
78
  COMIC_GROUP: "comic",
79
- # Possible to list all groups in a non group view, but could get crazy.
80
- # FOLDER_GROUP: "comic__folder",
81
- # STORY_ARC_GROUP: "comic__story_arc_number__story_arc"
82
79
  }
83
80
  )
84
81
  CUSTOM_COVER_GROUP_RELATION = MappingProxyType(
codex/views/frontend.py CHANGED
@@ -17,7 +17,7 @@ class IndexView(SessionView):
17
17
  SESSION_KEY = SessionView.BROWSER_SESSION_KEY
18
18
 
19
19
  permission_classes = (AllowAny,)
20
- renderer_classes: ClassVar[list] = [TemplateHTMLRenderer] # type: ignore
20
+ renderer_classes: ClassVar[list] = [TemplateHTMLRenderer] # type: ignore[reportIncompatibleVariableOverride]
21
21
  template_name = "index.html"
22
22
 
23
23
  def _get_last_route(self):
codex/views/mixins.py CHANGED
@@ -4,6 +4,7 @@ from django.db.models.expressions import F
4
4
 
5
5
  from codex.logger.logging import get_logger
6
6
  from codex.models.comic import Comic, Imprint, Volume
7
+ from codex.views.browser.filters.filter import BrowserFilterView
7
8
  from codex.views.const import GROUP_NAME_MAP
8
9
 
9
10
  LOG = get_logger(__name__)
@@ -12,7 +13,7 @@ _GROUP_NAME_TARGETS = frozenset({"browser", "opds1", "opds2", "reader"})
12
13
  _VARIABLE_SHOW = "pi"
13
14
 
14
15
 
15
- class SharedAnnotationsMixin:
16
+ class SharedAnnotationsMixin(BrowserFilterView):
16
17
  """Cross view annotation methods."""
17
18
 
18
19
  @staticmethod
@@ -54,7 +55,7 @@ class SharedAnnotationsMixin:
54
55
  def annotate_group_names(cls, qs):
55
56
  """Annotate name fields by hoisting them up."""
56
57
  # Optimized to only lookup what is used on the frontend
57
- target = cls.TARGET # type: ignore
58
+ target = cls.TARGET
58
59
  if target not in _GROUP_NAME_TARGETS:
59
60
  return qs
60
61
  group_names = {}
@@ -40,7 +40,7 @@ _DOC = MappingProxyType(
40
40
  "labels": {"login": "Username", "password": "Password"},
41
41
  },
42
42
  {
43
- # XXX Out of spec type
43
+ # Out of spec type
44
44
  "type": "cookie",
45
45
  "links": [
46
46
  {
codex/views/opds/const.py CHANGED
@@ -48,9 +48,9 @@ class MimeType:
48
48
 
49
49
  ATOM = "application/atom+xml"
50
50
  _PROFILE_CATALOG = "profile=opds-catalog"
51
- NAV = ";".join((ATOM, _PROFILE_CATALOG, "kind=navigation"))
52
- ACQUISITION = ";".join((ATOM, _PROFILE_CATALOG, "kind=acquisition"))
53
- ENTRY_CATALOG = ";".join((ATOM, "type=entry", _PROFILE_CATALOG))
51
+ NAV = f"{ATOM};{_PROFILE_CATALOG};kind=navigation"
52
+ ACQUISITION = f"{ATOM};{_PROFILE_CATALOG};kind=acquisition"
53
+ ENTRY_CATALOG = f"{ATOM};type=entry;{_PROFILE_CATALOG}"
54
54
  AUTHENTICATION = "application/opds-authentication+json"
55
55
  OPENSEARCH = "application/opensearchdescription+xml"
56
56
  STREAM = "image/jpeg"
@@ -58,7 +58,7 @@ class MimeType:
58
58
  OPDS_PUB = "application/opds-publication+json"
59
59
  PROGRESSION = "application/vnd.readium.progression+json"
60
60
  BOOK = "http://schema.org/Book"
61
- # COMIC = "https://schema.org/ComicStory"
61
+ # COMIC = "https://schema.org/ComicStory" unused
62
62
  JPEG = "image/jpeg"
63
63
  WEBP = "image/webp"
64
64
  HTML = "text/html"
codex/views/opds/urls.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from django.urls import reverse
4
4
  from rest_framework.response import Response
5
5
 
6
- from codex.choices import DEFAULT_BROWSER_ROUTE
6
+ from codex.choices.browser import DEFAULT_BROWSER_ROUTE
7
7
  from codex.serializers.opds.urls import OPDSURLsSerializer
8
8
  from codex.views.auth import AuthGenericAPIView
9
9
  from codex.views.util import pop_name
codex/views/opds/util.py CHANGED
@@ -5,7 +5,7 @@ from django.http.response import HttpResponseRedirect
5
5
  from django.urls import reverse
6
6
  from django.utils.http import urlencode
7
7
 
8
- from codex.choices import DEFAULT_BROWSER_ROUTE
8
+ from codex.choices.browser import DEFAULT_BROWSER_ROUTE
9
9
  from codex.models import (
10
10
  Contributor,
11
11
  ContributorPerson,
@@ -31,7 +31,7 @@ def update_href_query_params(href, old_query_params, new_query_params=None):
31
31
  return href
32
32
 
33
33
 
34
- def get_contributor_people(comic_pks, roles, exclude=False):
34
+ def get_contributor_people(comic_pks, roles, exclude):
35
35
  """Get contributors that are not authors."""
36
36
  people = ContributorPerson.objects.filter(
37
37
  contributor__comic__in=comic_pks,
@@ -43,7 +43,7 @@ def get_contributor_people(comic_pks, roles, exclude=False):
43
43
  return people.distinct().only("name")
44
44
 
45
45
 
46
- def get_contributors(comic_pks, roles, exclude=False):
46
+ def get_contributors(comic_pks, roles, exclude):
47
47
  """Get credits that are not part of other roles."""
48
48
  contributors = Contributor.objects.filter(comic__in=comic_pks)
49
49
  if exclude:
@@ -78,7 +78,7 @@ def full_redirect_view(url_name):
78
78
  path = request.get_full_path()
79
79
  if path:
80
80
  parts = path.split("?")
81
- if len(parts) >= 2: # noqa PLR2004
81
+ if len(parts) >= 2: # noqa: PLR2004
82
82
  parts[0] = url
83
83
  url = "?".join(parts)
84
84
 
@@ -47,8 +47,10 @@ class OPDS1Entry(OPDS1EntryLinksMixin):
47
47
  elif group == "c":
48
48
  title = Comic.get_title(
49
49
  self.obj,
50
- zero_pad=self.zero_pad,
50
+ volume=True,
51
+ name=True,
51
52
  filename_fallback=self.title_filename_fallback,
53
+ zero_pad=self.zero_pad,
52
54
  )
53
55
  parts.append(title)
54
56
 
@@ -133,7 +135,7 @@ class OPDS1Entry(OPDS1EntryLinksMixin):
133
135
  """Get Author names."""
134
136
  if not self.metadata:
135
137
  return []
136
- people = get_contributor_people(self.obj.ids, AUTHOR_ROLES)
138
+ people = get_contributor_people(self.obj.ids, AUTHOR_ROLES, exclude=False)
137
139
  return self._add_url_to_obj(people, "contributors")
138
140
 
139
141
  @property
@@ -20,7 +20,7 @@ class OPDS1EntryLinksMixin:
20
20
  """OPDS v1 Entry Links Methods."""
21
21
 
22
22
  def __init__(
23
- self, obj, query_params, data: OPDS1EntryData, title_filename_fallback=False
23
+ self, obj, query_params, data: OPDS1EntryData, title_filename_fallback: bool
24
24
  ):
25
25
  """Initialize params."""
26
26
  self.obj = obj
@@ -35,7 +35,6 @@ class OPDS1EntryLinksMixin:
35
35
  def _cover_link(self, rel):
36
36
  if self.fake:
37
37
  return None
38
- # cover_pk = getattr(self.obj, "cover_pk", None)
39
38
  try:
40
39
  kwargs = {"group": self.obj.group, "pks": self.obj.ids}
41
40
  ts = floor(datetime.timestamp(self.obj.updated_at))
@@ -50,7 +49,7 @@ class OPDS1EntryLinksMixin:
50
49
  except Exception:
51
50
  LOG.exception("create thumb")
52
51
 
53
- def _nav_href(self, metadata=False):
52
+ def _nav_href(self, metadata: bool):
54
53
  try:
55
54
  pks = sorted(self.obj.ids)
56
55
  kwargs = {"group": self.obj.group, "pks": pks, "page": 1}
@@ -72,7 +71,7 @@ class OPDS1EntryLinksMixin:
72
71
  LOG.exception(msg)
73
72
  raise
74
73
 
75
- def _nav_link(self, metadata=False):
74
+ def _nav_link(self, metadata: bool):
76
75
  href = self._nav_href(metadata)
77
76
 
78
77
  group = self.obj.group
@@ -154,7 +153,7 @@ class OPDS1EntryLinksMixin:
154
153
 
155
154
  if self.obj.group == "c" and not self.fake:
156
155
  result += self._links_comic()
157
- elif nav := self._nav_link():
156
+ elif nav := self._nav_link(metadata=False):
158
157
  result += [nav]
159
158
 
160
159
  except Exception:
@@ -169,11 +169,14 @@ class OPDS1FacetsView(BrowserView):
169
169
  )
170
170
  qps = {**self.request.GET}
171
171
  qps.update(query_params)
172
- zero_pad: int = self.obj["zero_pad"] # type: ignore
172
+ zero_pad: int = self.obj["zero_pad"]
173
173
  data = OPDS1EntryData(
174
- self.opds_acquisition_groups, zero_pad, False, self.mime_type_map
174
+ self.opds_acquisition_groups,
175
+ zero_pad,
176
+ metadata=False,
177
+ mime_type_map=self.mime_type_map,
175
178
  )
176
- return OPDS1Entry(entry_obj, qps, data)
179
+ return OPDS1Entry(entry_obj, qps, data, title_filename_fallback=False)
177
180
 
178
181
  def _is_facet_active(self, facet_group, facet):
179
182
  compare = [facet.value]
@@ -231,7 +234,8 @@ class OPDS1FacetsView(BrowserView):
231
234
  facets += [facet_obj]
232
235
  return facets
233
236
 
234
- def facets(self, entries=False, root=True):
237
+ def facets(self, entries: bool, root: bool):
238
+ # entries false, root true
235
239
  """Return facets."""
236
240
  facets = []
237
241
  group = self.kwargs.get("group")