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

Files changed (578) hide show
  1. codex/asgi.py +2 -1
  2. codex/choices.py +12 -12
  3. codex/choices_to_json.py +8 -8
  4. codex/db.py +2 -2
  5. codex/exceptions.py +6 -7
  6. codex/librarian/bookmark/bookmarkd.py +8 -4
  7. codex/librarian/bookmark/update.py +82 -48
  8. codex/librarian/covers/create.py +10 -8
  9. codex/librarian/covers/path.py +6 -5
  10. codex/librarian/covers/purge.py +2 -2
  11. codex/librarian/importer/aggregate.py +2 -2
  12. codex/librarian/importer/cache.py +1 -4
  13. codex/librarian/importer/create_comics.py +2 -2
  14. codex/librarian/importer/create_covers.py +1 -1
  15. codex/librarian/importer/create_fks.py +4 -3
  16. codex/librarian/importer/deleted.py +3 -3
  17. codex/librarian/importer/extract.py +56 -37
  18. codex/librarian/importer/failed_imports.py +1 -1
  19. codex/librarian/importer/importer.py +3 -1
  20. codex/librarian/importer/importerd.py +3 -3
  21. codex/librarian/importer/link_comics.py +2 -1
  22. codex/librarian/importer/moved.py +2 -7
  23. codex/librarian/importer/query_fks.py +1 -4
  24. codex/librarian/importer/tasks.py +1 -1
  25. codex/librarian/janitor/integrity.py +27 -20
  26. codex/librarian/janitor/janitor.py +1 -1
  27. codex/librarian/janitor/latest_version.py +1 -1
  28. codex/librarian/janitor/update.py +2 -2
  29. codex/librarian/janitor/vacuum.py +1 -1
  30. codex/librarian/librariand.py +3 -2
  31. codex/librarian/notifier/notifierd.py +2 -1
  32. codex/librarian/search/optimize.py +1 -1
  33. codex/librarian/search/remove.py +1 -1
  34. codex/librarian/search/update.py +4 -4
  35. codex/librarian/telemeter/stats.py +7 -6
  36. codex/librarian/telemeter/telemeter.py +0 -1
  37. codex/librarian/watchdog/db_snapshot.py +3 -3
  38. codex/librarian/watchdog/dir_snapshot_diff.py +3 -4
  39. codex/librarian/watchdog/emitter.py +4 -4
  40. codex/librarian/watchdog/event_batcherd.py +2 -1
  41. codex/librarian/watchdog/events.py +1 -2
  42. codex/librarian/watchdog/observers.py +15 -10
  43. codex/logger/logging.py +1 -1
  44. codex/memory.py +3 -2
  45. codex/migrations/0001_init.py +1 -2
  46. codex/migrations/0014_pdf_issue_suffix_remove_cover_image_sort_name.py +1 -2
  47. codex/migrations/0018_rename_userbookmark_bookmark.py +1 -2
  48. codex/migrations/0028_telemeter.py +1 -5
  49. codex/models/base.py +3 -1
  50. codex/models/bookmark.py +4 -7
  51. codex/models/comic.py +2 -2
  52. codex/models/functions.py +12 -5
  53. codex/models/groups.py +1 -1
  54. codex/models/library.py +1 -1
  55. codex/models/paths.py +4 -4
  56. codex/models/query.py +8 -4
  57. codex/models/util.py +1 -1
  58. codex/permissions.py +1 -1
  59. codex/run.py +1 -1
  60. codex/serializers/admin/libraries.py +1 -3
  61. codex/serializers/admin/stats.py +4 -4
  62. codex/serializers/auth.py +4 -2
  63. codex/serializers/browser/choices.py +87 -9
  64. codex/serializers/browser/filters.py +9 -37
  65. codex/serializers/browser/metadata.py +2 -1
  66. codex/serializers/browser/mixins.py +7 -3
  67. codex/serializers/browser/mtime.py +3 -5
  68. codex/serializers/browser/page.py +5 -4
  69. codex/serializers/browser/settings.py +7 -5
  70. codex/serializers/fields/__init__.py +37 -0
  71. codex/serializers/fields/auth.py +45 -0
  72. codex/serializers/fields/browser.py +85 -0
  73. codex/serializers/fields/group.py +13 -0
  74. codex/serializers/fields/reader.py +22 -0
  75. codex/serializers/fields/sanitized.py +13 -0
  76. codex/serializers/fields/session.py +15 -0
  77. codex/serializers/fields/stats.py +36 -0
  78. codex/serializers/fields/vuetify.py +62 -0
  79. codex/serializers/models/base.py +1 -1
  80. codex/serializers/models/comic.py +2 -2
  81. codex/serializers/models/groups.py +0 -2
  82. codex/serializers/models/named.py +1 -1
  83. codex/serializers/models/pycountry.py +1 -1
  84. codex/serializers/opds/authentication.py +4 -2
  85. codex/serializers/opds/urls.py +2 -1
  86. codex/serializers/opds/v1.py +4 -3
  87. codex/serializers/opds/v2/feed.py +2 -1
  88. codex/serializers/opds/v2/links.py +23 -21
  89. codex/serializers/opds/v2/metadata.py +4 -8
  90. codex/serializers/opds/v2/progression.py +5 -3
  91. codex/serializers/opds/v2/publication.py +16 -18
  92. codex/serializers/opds/v2/unused.py +15 -8
  93. codex/serializers/reader.py +16 -14
  94. codex/serializers/redirect.py +2 -1
  95. codex/serializers/route.py +6 -4
  96. codex/serializers/settings.py +5 -3
  97. codex/serializers/versions.py +2 -1
  98. codex/settings/settings.py +4 -3
  99. codex/signals/django_signals.py +1 -1
  100. codex/static_root/assets/{VCheckbox-FaT6MGfu.f6732060f734.js → VCheckbox-CtTi6T7d.87331fa7e77f.js} +1 -1
  101. codex/static_root/assets/VCheckbox-CtTi6T7d.87331fa7e77f.js.br +0 -0
  102. codex/static_root/assets/VCheckbox-CtTi6T7d.87331fa7e77f.js.gz +0 -0
  103. codex/static_root/assets/{VCheckbox-FaT6MGfu.js → VCheckbox-CtTi6T7d.js} +1 -1
  104. codex/static_root/assets/VCheckbox-CtTi6T7d.js.br +0 -0
  105. codex/static_root/assets/VCheckbox-CtTi6T7d.js.gz +0 -0
  106. codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.847452d574cf.js → VCheckboxBtn-DQafuWTH.dada5e0634ef.js} +1 -1
  107. codex/static_root/assets/VCheckboxBtn-DQafuWTH.dada5e0634ef.js.br +0 -0
  108. codex/static_root/assets/VCheckboxBtn-DQafuWTH.dada5e0634ef.js.gz +0 -0
  109. codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.js → VCheckboxBtn-DQafuWTH.js} +1 -1
  110. codex/static_root/assets/VCheckboxBtn-DQafuWTH.js.br +0 -0
  111. codex/static_root/assets/VCheckboxBtn-DQafuWTH.js.gz +0 -0
  112. codex/static_root/assets/{VCombobox-C8QLNlSl.6f431e19a453.js → VCombobox-C6TX3Dhp.e619423a6463.js} +1 -1
  113. codex/static_root/assets/VCombobox-C6TX3Dhp.e619423a6463.js.br +0 -0
  114. codex/static_root/assets/VCombobox-C6TX3Dhp.e619423a6463.js.gz +0 -0
  115. codex/static_root/assets/{VCombobox-C8QLNlSl.js → VCombobox-C6TX3Dhp.js} +1 -1
  116. codex/static_root/assets/VCombobox-C6TX3Dhp.js.br +0 -0
  117. codex/static_root/assets/VCombobox-C6TX3Dhp.js.gz +0 -0
  118. codex/static_root/assets/{VDialog-RVLeW7je.c0c7eef71eec.js → VDialog-CknWq2kd.b2d301ec6ac5.js} +1 -1
  119. codex/static_root/assets/VDialog-CknWq2kd.b2d301ec6ac5.js.br +0 -0
  120. codex/static_root/assets/VDialog-CknWq2kd.b2d301ec6ac5.js.gz +0 -0
  121. codex/static_root/assets/{VDialog-RVLeW7je.js → VDialog-CknWq2kd.js} +1 -1
  122. codex/static_root/assets/VDialog-CknWq2kd.js.br +0 -0
  123. codex/static_root/assets/VDialog-CknWq2kd.js.gz +0 -0
  124. codex/static_root/assets/{VDivider-D1Ot4vR2.9dbee744fb3c.js → VDivider-F4DUgr74.62bc92cab97d.js} +1 -1
  125. codex/static_root/assets/VDivider-F4DUgr74.62bc92cab97d.js.br +0 -0
  126. codex/static_root/assets/VDivider-F4DUgr74.62bc92cab97d.js.gz +0 -0
  127. codex/static_root/assets/{VDivider-D1Ot4vR2.js → VDivider-F4DUgr74.js} +1 -1
  128. codex/static_root/assets/VDivider-F4DUgr74.js.br +0 -0
  129. codex/static_root/assets/VDivider-F4DUgr74.js.gz +0 -0
  130. codex/static_root/assets/{VExpansionPanels-DQilGHZk.43f06d0a45e0.js → VExpansionPanels-CprYUTGT.39b8a50cde43.js} +1 -1
  131. codex/static_root/assets/VExpansionPanels-CprYUTGT.39b8a50cde43.js.br +0 -0
  132. codex/static_root/assets/VExpansionPanels-CprYUTGT.39b8a50cde43.js.gz +0 -0
  133. codex/static_root/assets/{VExpansionPanels-DQilGHZk.js → VExpansionPanels-CprYUTGT.js} +1 -1
  134. codex/static_root/assets/VExpansionPanels-CprYUTGT.js.br +0 -0
  135. codex/static_root/assets/VExpansionPanels-CprYUTGT.js.gz +0 -0
  136. codex/static_root/assets/{VForm-DNmY-qFJ.81f3a5ff7ec3.js → VForm-DnWAUsrl.4ab0786d658e.js} +1 -1
  137. codex/static_root/assets/VForm-DnWAUsrl.4ab0786d658e.js.br +0 -0
  138. codex/static_root/assets/VForm-DnWAUsrl.4ab0786d658e.js.gz +0 -0
  139. codex/static_root/assets/{VForm-DNmY-qFJ.js → VForm-DnWAUsrl.js} +1 -1
  140. codex/static_root/assets/VForm-DnWAUsrl.js.br +0 -0
  141. codex/static_root/assets/VForm-DnWAUsrl.js.gz +0 -0
  142. codex/static_root/assets/{VRadioGroup-DoayJpcS.fe5e0e74ffee.js → VRadioGroup-D71Rszum.befc87b0c476.js} +1 -1
  143. codex/static_root/assets/VRadioGroup-D71Rszum.befc87b0c476.js.br +0 -0
  144. codex/static_root/assets/VRadioGroup-D71Rszum.befc87b0c476.js.gz +0 -0
  145. codex/static_root/assets/{VRadioGroup-DoayJpcS.js → VRadioGroup-D71Rszum.js} +1 -1
  146. codex/static_root/assets/VRadioGroup-D71Rszum.js.br +0 -0
  147. codex/static_root/assets/VRadioGroup-D71Rszum.js.gz +0 -0
  148. codex/static_root/assets/{VSelect-BSLmHX_P.9ab865ec0fad.js → VSelect-CbPDrHNj.4809fbbf00f0.js} +1 -1
  149. codex/static_root/assets/VSelect-CbPDrHNj.4809fbbf00f0.js.br +0 -0
  150. codex/static_root/assets/VSelect-CbPDrHNj.4809fbbf00f0.js.gz +0 -0
  151. codex/static_root/assets/{VSelect-BSLmHX_P.js → VSelect-CbPDrHNj.js} +1 -1
  152. codex/static_root/assets/VSelect-CbPDrHNj.js.br +0 -0
  153. codex/static_root/assets/VSelect-CbPDrHNj.js.gz +0 -0
  154. codex/static_root/assets/{VSelectionControl-TaKCeZgb.ad6c96efe57c.js → VSelectionControl-It389cbo.ac75aee480a0.js} +1 -1
  155. codex/static_root/assets/VSelectionControl-It389cbo.ac75aee480a0.js.br +0 -0
  156. codex/static_root/assets/VSelectionControl-It389cbo.ac75aee480a0.js.gz +0 -0
  157. codex/static_root/assets/{VSelectionControl-TaKCeZgb.js → VSelectionControl-It389cbo.js} +1 -1
  158. codex/static_root/assets/VSelectionControl-It389cbo.js.br +0 -0
  159. codex/static_root/assets/VSelectionControl-It389cbo.js.gz +0 -0
  160. codex/static_root/assets/{VTable-BfcOiEpa.77ef3973bb54.js → VTable-D9e3lKk6.7cd0760008c7.js} +1 -1
  161. codex/static_root/assets/VTable-D9e3lKk6.7cd0760008c7.js.br +0 -0
  162. codex/static_root/assets/VTable-D9e3lKk6.7cd0760008c7.js.gz +0 -0
  163. codex/static_root/assets/{VTable-BfcOiEpa.js → VTable-D9e3lKk6.js} +1 -1
  164. codex/static_root/assets/VTable-D9e3lKk6.js.br +0 -0
  165. codex/static_root/assets/VTable-D9e3lKk6.js.gz +0 -0
  166. codex/static_root/assets/{VWindowItem-D8lWcbQY.e4c75ad78718.js → VWindowItem-B4VZ37cq.e45c5018cc73.js} +1 -1
  167. codex/static_root/assets/VWindowItem-B4VZ37cq.e45c5018cc73.js.br +0 -0
  168. codex/static_root/assets/VWindowItem-B4VZ37cq.e45c5018cc73.js.gz +0 -0
  169. codex/static_root/assets/{VWindowItem-D8lWcbQY.js → VWindowItem-B4VZ37cq.js} +1 -1
  170. codex/static_root/assets/VWindowItem-B4VZ37cq.js.br +0 -0
  171. codex/static_root/assets/VWindowItem-B4VZ37cq.js.gz +0 -0
  172. codex/static_root/assets/{admin-CBLHOrM9.8c3c2268bc96.js → admin-Bkd8zuSF.2a29386f3488.js} +1 -1
  173. codex/static_root/assets/admin-Bkd8zuSF.2a29386f3488.js.br +0 -0
  174. codex/static_root/assets/admin-Bkd8zuSF.2a29386f3488.js.gz +0 -0
  175. codex/static_root/assets/{admin-CBLHOrM9.js → admin-Bkd8zuSF.js} +1 -1
  176. codex/static_root/assets/admin-Bkd8zuSF.js.br +0 -0
  177. codex/static_root/assets/admin-Bkd8zuSF.js.gz +0 -0
  178. codex/static_root/assets/{admin-80eqCqtz.d6deb9edb8cb.js → admin-CFTKGwmx.18915c5df4d3.js} +1 -1
  179. codex/static_root/assets/admin-CFTKGwmx.18915c5df4d3.js.br +0 -0
  180. codex/static_root/assets/admin-CFTKGwmx.18915c5df4d3.js.gz +0 -0
  181. codex/static_root/assets/{admin-80eqCqtz.js → admin-CFTKGwmx.js} +1 -1
  182. codex/static_root/assets/admin-CFTKGwmx.js.br +0 -0
  183. codex/static_root/assets/admin-CFTKGwmx.js.gz +0 -0
  184. codex/static_root/assets/{admin-menu-_aAGknKO.0c08c9bd2e7d.js → admin-menu-CxZqtPES.7b3a06ce4b92.js} +1 -1
  185. codex/static_root/assets/admin-menu-CxZqtPES.7b3a06ce4b92.js.br +0 -0
  186. codex/static_root/assets/admin-menu-CxZqtPES.7b3a06ce4b92.js.gz +0 -0
  187. codex/static_root/assets/{admin-menu-_aAGknKO.js → admin-menu-CxZqtPES.js} +1 -1
  188. codex/static_root/assets/admin-menu-CxZqtPES.js.br +0 -0
  189. codex/static_root/assets/admin-menu-CxZqtPES.js.gz +0 -0
  190. codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js → admin-settings-button-progress-D2TfW_5T.8a9d411ee79d.js} +1 -1
  191. codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.8a9d411ee79d.js.br +0 -0
  192. codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.8a9d411ee79d.js.gz +0 -0
  193. codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.js → admin-settings-button-progress-D2TfW_5T.js} +1 -1
  194. codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.js.br +0 -0
  195. codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.js.gz +0 -0
  196. codex/static_root/assets/browser-Br2DgAdi.433cca495d61.js +1 -0
  197. codex/static_root/assets/browser-Br2DgAdi.433cca495d61.js.br +0 -0
  198. codex/static_root/assets/browser-Br2DgAdi.433cca495d61.js.gz +0 -0
  199. codex/static_root/assets/browser-Br2DgAdi.js +1 -0
  200. codex/static_root/assets/browser-Br2DgAdi.js.br +0 -0
  201. codex/static_root/assets/browser-Br2DgAdi.js.gz +0 -0
  202. codex/static_root/assets/{browser-chTZ1CM4.521193ab0710.css → browser-DNzQvgkY.aea8b7d7ca40.css} +1 -1
  203. codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.br +0 -0
  204. codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.gz +0 -0
  205. codex/static_root/assets/{browser-chTZ1CM4.css → browser-DNzQvgkY.css} +1 -1
  206. codex/static_root/assets/browser-DNzQvgkY.css.br +0 -0
  207. codex/static_root/assets/browser-DNzQvgkY.css.gz +0 -0
  208. codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js → change-password-dialog-CSylx5Qb.319466c891c9.js} +1 -1
  209. codex/static_root/assets/change-password-dialog-CSylx5Qb.319466c891c9.js.br +0 -0
  210. codex/static_root/assets/change-password-dialog-CSylx5Qb.319466c891c9.js.gz +0 -0
  211. codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.js → change-password-dialog-CSylx5Qb.js} +1 -1
  212. codex/static_root/assets/change-password-dialog-CSylx5Qb.js.br +0 -0
  213. codex/static_root/assets/change-password-dialog-CSylx5Qb.js.gz +0 -0
  214. codex/static_root/assets/{confirm-dialog-DqCey9Iq.e315d7f8f752.js → confirm-dialog-Bx2R2vgR.d42b6449c495.js} +1 -1
  215. codex/static_root/assets/confirm-dialog-Bx2R2vgR.d42b6449c495.js.br +0 -0
  216. codex/static_root/assets/confirm-dialog-Bx2R2vgR.d42b6449c495.js.gz +0 -0
  217. codex/static_root/assets/{confirm-dialog-DqCey9Iq.js → confirm-dialog-Bx2R2vgR.js} +1 -1
  218. codex/static_root/assets/confirm-dialog-Bx2R2vgR.js.br +0 -0
  219. codex/static_root/assets/confirm-dialog-Bx2R2vgR.js.gz +0 -0
  220. codex/static_root/assets/{datetime-column-CBA8bYeO.17a31f189d66.js → datetime-column-sOrWMDcU.9ad225af18db.js} +1 -1
  221. codex/static_root/assets/datetime-column-sOrWMDcU.9ad225af18db.js.br +2 -0
  222. codex/static_root/assets/datetime-column-sOrWMDcU.9ad225af18db.js.gz +0 -0
  223. codex/static_root/assets/{datetime-column-CBA8bYeO.js → datetime-column-sOrWMDcU.js} +1 -1
  224. codex/static_root/assets/datetime-column-sOrWMDcU.js.br +2 -0
  225. codex/static_root/assets/datetime-column-sOrWMDcU.js.gz +0 -0
  226. codex/static_root/assets/{filter-mnO3lLPo.659442401b16.js → filter-SgjvoAvL.094069d6978d.js} +1 -1
  227. codex/static_root/assets/filter-SgjvoAvL.094069d6978d.js.br +0 -0
  228. codex/static_root/assets/filter-SgjvoAvL.094069d6978d.js.gz +0 -0
  229. codex/static_root/assets/{filter-mnO3lLPo.js → filter-SgjvoAvL.js} +1 -1
  230. codex/static_root/assets/filter-SgjvoAvL.js.br +0 -0
  231. codex/static_root/assets/filter-SgjvoAvL.js.gz +0 -0
  232. codex/static_root/assets/{flag-tab-Bc677pNb.1b8a2f7c0dc3.js → flag-tab-B_0tGWPg.e46d1782b327.js} +1 -1
  233. codex/static_root/assets/flag-tab-B_0tGWPg.e46d1782b327.js.br +0 -0
  234. codex/static_root/assets/flag-tab-B_0tGWPg.e46d1782b327.js.gz +0 -0
  235. codex/static_root/assets/{flag-tab-Bc677pNb.js → flag-tab-B_0tGWPg.js} +1 -1
  236. codex/static_root/assets/flag-tab-B_0tGWPg.js.br +0 -0
  237. codex/static_root/assets/flag-tab-B_0tGWPg.js.gz +0 -0
  238. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css +1 -0
  239. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.br +0 -0
  240. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.gz +0 -0
  241. codex/static_root/assets/flag-tab-D2MYXCHk.css +1 -0
  242. codex/static_root/assets/flag-tab-D2MYXCHk.css.br +0 -0
  243. codex/static_root/assets/flag-tab-D2MYXCHk.css.gz +0 -0
  244. codex/static_root/assets/{forwardRefs-DPRKg5wq.6f09e9d22fa6.js → forwardRefs-DqbPK1-k.bb9d6863ac16.js} +1 -1
  245. codex/static_root/assets/forwardRefs-DqbPK1-k.bb9d6863ac16.js.br +0 -0
  246. codex/static_root/assets/forwardRefs-DqbPK1-k.bb9d6863ac16.js.gz +0 -0
  247. codex/static_root/assets/{forwardRefs-DPRKg5wq.js → forwardRefs-DqbPK1-k.js} +1 -1
  248. codex/static_root/assets/forwardRefs-DqbPK1-k.js.br +0 -0
  249. codex/static_root/assets/forwardRefs-DqbPK1-k.js.gz +0 -0
  250. codex/static_root/assets/{group-tab-BVlBrMwo.1127b51be6e7.js → group-tab-DXxQUa6C.f1f12f39b167.js} +1 -1
  251. codex/static_root/assets/group-tab-DXxQUa6C.f1f12f39b167.js.br +0 -0
  252. codex/static_root/assets/group-tab-DXxQUa6C.f1f12f39b167.js.gz +0 -0
  253. codex/static_root/assets/{group-tab-BVlBrMwo.js → group-tab-DXxQUa6C.js} +1 -1
  254. codex/static_root/assets/group-tab-DXxQUa6C.js.br +0 -0
  255. codex/static_root/assets/group-tab-DXxQUa6C.js.gz +0 -0
  256. codex/static_root/assets/{http-error-C9-_aEBF.d29b164e80de.js → http-error-BVMwwrFo.a7be6a26c9eb.js} +1 -1
  257. codex/static_root/assets/http-error-BVMwwrFo.a7be6a26c9eb.js.br +0 -0
  258. codex/static_root/assets/http-error-BVMwwrFo.a7be6a26c9eb.js.gz +0 -0
  259. codex/static_root/assets/{http-error-C9-_aEBF.js → http-error-BVMwwrFo.js} +1 -1
  260. codex/static_root/assets/http-error-BVMwwrFo.js.br +0 -0
  261. codex/static_root/assets/http-error-BVMwwrFo.js.gz +0 -0
  262. codex/static_root/assets/{index-9nBCksDQ.4561d2608773.js → index-DK0lSHDP.86f18c078fa9.js} +1 -1
  263. codex/static_root/assets/index-DK0lSHDP.86f18c078fa9.js.br +0 -0
  264. codex/static_root/assets/index-DK0lSHDP.86f18c078fa9.js.gz +0 -0
  265. codex/static_root/assets/{index-9nBCksDQ.js → index-DK0lSHDP.js} +1 -1
  266. codex/static_root/assets/index-DK0lSHDP.js.br +0 -0
  267. codex/static_root/assets/index-DK0lSHDP.js.gz +0 -0
  268. codex/static_root/assets/{library-tab-BMe3dKzy.22b6e123ecfb.js → library-tab-NhXuvZ4F.1e8ac3b7c168.js} +1 -1
  269. codex/static_root/assets/library-tab-NhXuvZ4F.1e8ac3b7c168.js.br +0 -0
  270. codex/static_root/assets/library-tab-NhXuvZ4F.1e8ac3b7c168.js.gz +0 -0
  271. codex/static_root/assets/{library-tab-BMe3dKzy.js → library-tab-NhXuvZ4F.js} +1 -1
  272. codex/static_root/assets/library-tab-NhXuvZ4F.js.br +0 -0
  273. codex/static_root/assets/library-tab-NhXuvZ4F.js.gz +0 -0
  274. codex/static_root/assets/{main-BbNUiWEb.565b616eb122.js → main-CYgubU7H.50aa39cf9565.js} +3 -3
  275. codex/static_root/assets/main-CYgubU7H.50aa39cf9565.js.br +0 -0
  276. codex/static_root/assets/main-CYgubU7H.50aa39cf9565.js.gz +0 -0
  277. codex/static_root/assets/{main-BbNUiWEb.js → main-CYgubU7H.js} +3 -3
  278. codex/static_root/assets/main-CYgubU7H.js.br +0 -0
  279. codex/static_root/assets/main-CYgubU7H.js.gz +0 -0
  280. codex/static_root/assets/pager-full-pdf---TZiAf5.a6269ab85085.js +1 -0
  281. codex/static_root/assets/pager-full-pdf---TZiAf5.a6269ab85085.js.br +0 -0
  282. codex/static_root/assets/pager-full-pdf---TZiAf5.a6269ab85085.js.gz +0 -0
  283. codex/static_root/assets/pager-full-pdf---TZiAf5.js +1 -0
  284. codex/static_root/assets/pager-full-pdf---TZiAf5.js.br +0 -0
  285. codex/static_root/assets/pager-full-pdf---TZiAf5.js.gz +0 -0
  286. codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js → pagination-toolbar-CeWXU_qA.abe0805da1cc.js} +1 -1
  287. codex/static_root/assets/pagination-toolbar-CeWXU_qA.abe0805da1cc.js.br +0 -0
  288. codex/static_root/assets/pagination-toolbar-CeWXU_qA.abe0805da1cc.js.gz +0 -0
  289. codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.js → pagination-toolbar-CeWXU_qA.js} +1 -1
  290. codex/static_root/assets/pagination-toolbar-CeWXU_qA.js.br +0 -0
  291. codex/static_root/assets/pagination-toolbar-CeWXU_qA.js.gz +0 -0
  292. codex/static_root/assets/{pdf-doc-6ZE_HmLT.10d1e16abc78.js → pdf-doc-Dws_otkd.d215eca27da5.js} +1 -1
  293. codex/static_root/assets/pdf-doc-Dws_otkd.d215eca27da5.js.br +0 -0
  294. codex/static_root/assets/pdf-doc-Dws_otkd.d215eca27da5.js.gz +0 -0
  295. codex/static_root/assets/{pdf-doc-6ZE_HmLT.js → pdf-doc-Dws_otkd.js} +1 -1
  296. codex/static_root/assets/pdf-doc-Dws_otkd.js.br +0 -0
  297. codex/static_root/assets/pdf-doc-Dws_otkd.js.gz +0 -0
  298. codex/static_root/assets/{reader-DdPKDuh5.0c55cef1e98b.js → reader-BCxYgZHF.dde3c4ac39a5.js} +2 -2
  299. codex/static_root/assets/reader-BCxYgZHF.dde3c4ac39a5.js.br +0 -0
  300. codex/static_root/assets/reader-BCxYgZHF.dde3c4ac39a5.js.gz +0 -0
  301. codex/static_root/assets/{reader-DdPKDuh5.js → reader-BCxYgZHF.js} +2 -2
  302. codex/static_root/assets/reader-BCxYgZHF.js.br +0 -0
  303. codex/static_root/assets/reader-BCxYgZHF.js.gz +0 -0
  304. codex/static_root/assets/{relation-chips-C96hi5jm.js → relation-chips-DNRRCvrw.b1bc75fb9349.js} +1 -1
  305. codex/static_root/assets/relation-chips-DNRRCvrw.b1bc75fb9349.js.br +0 -0
  306. codex/static_root/assets/relation-chips-DNRRCvrw.b1bc75fb9349.js.gz +0 -0
  307. codex/static_root/assets/{relation-chips-C96hi5jm.9f07c9eb533a.js → relation-chips-DNRRCvrw.js} +1 -1
  308. codex/static_root/assets/relation-chips-DNRRCvrw.js.br +0 -0
  309. codex/static_root/assets/relation-chips-DNRRCvrw.js.gz +0 -0
  310. codex/static_root/assets/{settings-drawer-CqbT1rid.js → settings-drawer-BuB29L5p.fe2eeea0058f.js} +2 -2
  311. codex/static_root/assets/settings-drawer-BuB29L5p.fe2eeea0058f.js.br +0 -0
  312. codex/static_root/assets/settings-drawer-BuB29L5p.fe2eeea0058f.js.gz +0 -0
  313. codex/static_root/assets/{settings-drawer-CqbT1rid.5639fa00b301.js → settings-drawer-BuB29L5p.js} +2 -2
  314. codex/static_root/assets/settings-drawer-BuB29L5p.js.br +0 -0
  315. codex/static_root/assets/settings-drawer-BuB29L5p.js.gz +0 -0
  316. codex/static_root/assets/{stats-tab-CkT2ZGUQ.b704fa46dc88.js → stats-tab-D0zymxU6.d5267113b51e.js} +1 -1
  317. codex/static_root/assets/stats-tab-D0zymxU6.d5267113b51e.js.br +0 -0
  318. codex/static_root/assets/stats-tab-D0zymxU6.d5267113b51e.js.gz +0 -0
  319. codex/static_root/assets/{stats-tab-CkT2ZGUQ.js → stats-tab-D0zymxU6.js} +1 -1
  320. codex/static_root/assets/stats-tab-D0zymxU6.js.br +0 -0
  321. codex/static_root/assets/stats-tab-D0zymxU6.js.gz +0 -0
  322. codex/static_root/assets/{task-tab-a-DcU3Tj.8c2f7bf3c4c9.js → task-tab-CI9RLdMS.120f395dc9c6.js} +1 -1
  323. codex/static_root/assets/task-tab-CI9RLdMS.120f395dc9c6.js.br +0 -0
  324. codex/static_root/assets/task-tab-CI9RLdMS.120f395dc9c6.js.gz +0 -0
  325. codex/static_root/assets/{task-tab-a-DcU3Tj.js → task-tab-CI9RLdMS.js} +1 -1
  326. codex/static_root/assets/task-tab-CI9RLdMS.js.br +0 -0
  327. codex/static_root/assets/task-tab-CI9RLdMS.js.gz +0 -0
  328. codex/static_root/assets/{unauthorized-BOl5YpL3.f3c4b52718d5.js → unauthorized-BNwg_Xvu.a894b96156e0.js} +1 -1
  329. codex/static_root/assets/unauthorized-BNwg_Xvu.a894b96156e0.js.br +0 -0
  330. codex/static_root/assets/unauthorized-BNwg_Xvu.a894b96156e0.js.gz +0 -0
  331. codex/static_root/assets/{unauthorized-BOl5YpL3.js → unauthorized-BNwg_Xvu.js} +1 -1
  332. codex/static_root/assets/unauthorized-BNwg_Xvu.js.br +0 -0
  333. codex/static_root/assets/unauthorized-BNwg_Xvu.js.gz +0 -0
  334. codex/static_root/assets/{user-tab-BBDZvD8G.aa4263a68b22.js → user-tab-w9LgIMMi.4a89c7b37486.js} +1 -1
  335. codex/static_root/assets/user-tab-w9LgIMMi.4a89c7b37486.js.br +0 -0
  336. codex/static_root/assets/user-tab-w9LgIMMi.4a89c7b37486.js.gz +0 -0
  337. codex/static_root/assets/{user-tab-BBDZvD8G.js → user-tab-w9LgIMMi.js} +1 -1
  338. codex/static_root/assets/user-tab-w9LgIMMi.js.br +0 -0
  339. codex/static_root/assets/user-tab-w9LgIMMi.js.gz +0 -0
  340. codex/static_root/{manifest.225989a17f49.json → manifest.514e63b9f805.json} +253 -253
  341. codex/static_root/manifest.514e63b9f805.json.br +0 -0
  342. codex/static_root/manifest.514e63b9f805.json.gz +0 -0
  343. codex/static_root/manifest.json +253 -253
  344. codex/static_root/manifest.json.br +0 -0
  345. codex/static_root/manifest.json.gz +0 -0
  346. codex/static_root/staticfiles.json +1 -1
  347. codex/status_controller.py +9 -12
  348. codex/urls/root.py +4 -3
  349. codex/views/admin/api_key.py +1 -4
  350. codex/views/admin/library.py +4 -4
  351. codex/views/admin/stats.py +10 -10
  352. codex/views/admin/tasks.py +9 -9
  353. codex/views/auth.py +3 -8
  354. codex/views/bookmark.py +3 -3
  355. codex/views/browser/annotate/card.py +2 -2
  356. codex/views/browser/annotate/order.py +6 -7
  357. codex/views/browser/bookmark.py +3 -1
  358. codex/views/browser/breadcrumbs.py +6 -5
  359. codex/views/browser/browser.py +2 -2
  360. codex/views/browser/choices.py +6 -7
  361. codex/views/browser/cover.py +6 -3
  362. codex/views/browser/download.py +2 -1
  363. codex/views/browser/filters/bookmark.py +1 -1
  364. codex/views/browser/filters/field.py +1 -1
  365. codex/views/browser/filters/filter.py +6 -8
  366. codex/views/browser/filters/group.py +8 -8
  367. codex/views/browser/filters/search/field/expression.py +1 -2
  368. codex/views/browser/filters/search/field/optimize.py +1 -1
  369. codex/views/browser/filters/search/field/parse.py +2 -2
  370. codex/views/browser/filters/search/parse.py +4 -19
  371. codex/views/browser/group_mtime.py +1 -1
  372. codex/views/browser/metadata/copy_intersections.py +1 -1
  373. codex/views/browser/metadata/metadata.py +1 -1
  374. codex/views/browser/mtime.py +1 -1
  375. codex/views/browser/order_by.py +3 -5
  376. codex/views/browser/params.py +1 -1
  377. codex/views/browser/settings.py +1 -1
  378. codex/views/browser/validate.py +8 -5
  379. codex/views/const.py +0 -3
  380. codex/views/frontend.py +1 -1
  381. codex/views/mixins.py +3 -2
  382. codex/views/opds/authentication_v1.py +1 -1
  383. codex/views/opds/const.py +4 -4
  384. codex/views/opds/util.py +3 -3
  385. codex/views/opds/v1/entry/entry.py +4 -2
  386. codex/views/opds/v1/entry/links.py +4 -5
  387. codex/views/opds/v1/facets.py +8 -4
  388. codex/views/opds/v1/feed.py +5 -5
  389. codex/views/opds/v1/links.py +11 -6
  390. codex/views/opds/v2/const.py +5 -2
  391. codex/views/opds/v2/feed.py +3 -3
  392. codex/views/opds/v2/progression.py +8 -9
  393. codex/views/opds/v2/publications.py +10 -20
  394. codex/views/public.py +1 -1
  395. codex/views/reader/books.py +3 -2
  396. codex/views/reader/page.py +3 -3
  397. codex/views/reader/params.py +2 -2
  398. codex/views/reader/reader.py +6 -6
  399. codex/views/reader/settings.py +1 -1
  400. codex/views/session.py +8 -2
  401. codex/views/settings.py +2 -2
  402. codex/views/timezone.py +2 -2
  403. codex/views/util.py +10 -9
  404. codex/views/version.py +1 -1
  405. {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/METADATA +3 -1
  406. {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/RECORD +409 -401
  407. {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/WHEEL +1 -1
  408. codex/serializers/fields.py +0 -183
  409. codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.br +0 -0
  410. codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.gz +0 -0
  411. codex/static_root/assets/VCheckbox-FaT6MGfu.js.br +0 -0
  412. codex/static_root/assets/VCheckbox-FaT6MGfu.js.gz +0 -0
  413. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.br +0 -0
  414. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.gz +0 -0
  415. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.br +0 -0
  416. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.gz +0 -0
  417. codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.br +0 -0
  418. codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.gz +0 -0
  419. codex/static_root/assets/VCombobox-C8QLNlSl.js.br +0 -0
  420. codex/static_root/assets/VCombobox-C8QLNlSl.js.gz +0 -0
  421. codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.br +0 -0
  422. codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.gz +0 -0
  423. codex/static_root/assets/VDialog-RVLeW7je.js.br +0 -0
  424. codex/static_root/assets/VDialog-RVLeW7je.js.gz +0 -0
  425. codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.br +0 -0
  426. codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.gz +0 -0
  427. codex/static_root/assets/VDivider-D1Ot4vR2.js.br +0 -0
  428. codex/static_root/assets/VDivider-D1Ot4vR2.js.gz +0 -0
  429. codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.br +0 -0
  430. codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.gz +0 -0
  431. codex/static_root/assets/VExpansionPanels-DQilGHZk.js.br +0 -0
  432. codex/static_root/assets/VExpansionPanels-DQilGHZk.js.gz +0 -0
  433. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.br +0 -0
  434. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.gz +0 -0
  435. codex/static_root/assets/VForm-DNmY-qFJ.js.br +0 -0
  436. codex/static_root/assets/VForm-DNmY-qFJ.js.gz +0 -0
  437. codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.br +0 -0
  438. codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.gz +0 -0
  439. codex/static_root/assets/VRadioGroup-DoayJpcS.js.br +0 -0
  440. codex/static_root/assets/VRadioGroup-DoayJpcS.js.gz +0 -0
  441. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.br +0 -0
  442. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.gz +0 -0
  443. codex/static_root/assets/VSelect-BSLmHX_P.js.br +0 -0
  444. codex/static_root/assets/VSelect-BSLmHX_P.js.gz +0 -0
  445. codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.br +0 -0
  446. codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.gz +0 -0
  447. codex/static_root/assets/VSelectionControl-TaKCeZgb.js.br +0 -0
  448. codex/static_root/assets/VSelectionControl-TaKCeZgb.js.gz +0 -0
  449. codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.br +0 -0
  450. codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.gz +0 -0
  451. codex/static_root/assets/VTable-BfcOiEpa.js.br +0 -0
  452. codex/static_root/assets/VTable-BfcOiEpa.js.gz +0 -0
  453. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.br +0 -0
  454. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.gz +0 -0
  455. codex/static_root/assets/VWindowItem-D8lWcbQY.js.br +0 -0
  456. codex/static_root/assets/VWindowItem-D8lWcbQY.js.gz +0 -0
  457. codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.br +0 -0
  458. codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.gz +0 -0
  459. codex/static_root/assets/admin-80eqCqtz.js.br +0 -0
  460. codex/static_root/assets/admin-80eqCqtz.js.gz +0 -0
  461. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.br +0 -0
  462. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.gz +0 -0
  463. codex/static_root/assets/admin-CBLHOrM9.js.br +0 -0
  464. codex/static_root/assets/admin-CBLHOrM9.js.gz +0 -0
  465. codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.br +0 -0
  466. codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.gz +0 -0
  467. codex/static_root/assets/admin-menu-_aAGknKO.js.br +0 -0
  468. codex/static_root/assets/admin-menu-_aAGknKO.js.gz +0 -0
  469. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.br +0 -0
  470. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.gz +0 -0
  471. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.br +0 -0
  472. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.gz +0 -0
  473. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js +0 -1
  474. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.br +0 -0
  475. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.gz +0 -0
  476. codex/static_root/assets/browser-DAprXYuP.js +0 -1
  477. codex/static_root/assets/browser-DAprXYuP.js.br +0 -0
  478. codex/static_root/assets/browser-DAprXYuP.js.gz +0 -0
  479. codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.br +0 -0
  480. codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.gz +0 -0
  481. codex/static_root/assets/browser-chTZ1CM4.css.br +0 -0
  482. codex/static_root/assets/browser-chTZ1CM4.css.gz +0 -0
  483. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.br +0 -0
  484. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.gz +0 -0
  485. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.br +0 -0
  486. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.gz +0 -0
  487. codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.br +0 -0
  488. codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.gz +0 -0
  489. codex/static_root/assets/confirm-dialog-DqCey9Iq.js.br +0 -0
  490. codex/static_root/assets/confirm-dialog-DqCey9Iq.js.gz +0 -0
  491. codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.br +0 -0
  492. codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.gz +0 -0
  493. codex/static_root/assets/datetime-column-CBA8bYeO.js.br +0 -0
  494. codex/static_root/assets/datetime-column-CBA8bYeO.js.gz +0 -0
  495. codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.br +0 -0
  496. codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.gz +0 -0
  497. codex/static_root/assets/filter-mnO3lLPo.js.br +0 -0
  498. codex/static_root/assets/filter-mnO3lLPo.js.gz +0 -0
  499. codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.br +0 -0
  500. codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.gz +0 -0
  501. codex/static_root/assets/flag-tab-Bc677pNb.js.br +0 -0
  502. codex/static_root/assets/flag-tab-Bc677pNb.js.gz +0 -0
  503. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css +0 -1
  504. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.br +0 -0
  505. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.gz +0 -0
  506. codex/static_root/assets/flag-tab-glFvEgEl.css +0 -1
  507. codex/static_root/assets/flag-tab-glFvEgEl.css.br +0 -0
  508. codex/static_root/assets/flag-tab-glFvEgEl.css.gz +0 -0
  509. codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.br +0 -0
  510. codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.gz +0 -0
  511. codex/static_root/assets/forwardRefs-DPRKg5wq.js.br +0 -0
  512. codex/static_root/assets/forwardRefs-DPRKg5wq.js.gz +0 -0
  513. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.br +0 -0
  514. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.gz +0 -0
  515. codex/static_root/assets/group-tab-BVlBrMwo.js.br +0 -0
  516. codex/static_root/assets/group-tab-BVlBrMwo.js.gz +0 -0
  517. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.br +0 -0
  518. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.gz +0 -0
  519. codex/static_root/assets/http-error-C9-_aEBF.js.br +0 -0
  520. codex/static_root/assets/http-error-C9-_aEBF.js.gz +0 -0
  521. codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.br +0 -0
  522. codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.gz +0 -0
  523. codex/static_root/assets/index-9nBCksDQ.js.br +0 -0
  524. codex/static_root/assets/index-9nBCksDQ.js.gz +0 -0
  525. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.br +0 -0
  526. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.gz +0 -0
  527. codex/static_root/assets/library-tab-BMe3dKzy.js.br +0 -0
  528. codex/static_root/assets/library-tab-BMe3dKzy.js.gz +0 -0
  529. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.br +0 -0
  530. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.gz +0 -0
  531. codex/static_root/assets/main-BbNUiWEb.js.br +0 -0
  532. codex/static_root/assets/main-BbNUiWEb.js.gz +0 -0
  533. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js +0 -1
  534. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.br +0 -0
  535. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.gz +0 -0
  536. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js +0 -1
  537. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.br +0 -0
  538. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.gz +0 -0
  539. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.br +0 -0
  540. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.gz +0 -0
  541. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.br +0 -0
  542. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.gz +0 -0
  543. codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.br +0 -0
  544. codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.gz +0 -0
  545. codex/static_root/assets/pdf-doc-6ZE_HmLT.js.br +0 -0
  546. codex/static_root/assets/pdf-doc-6ZE_HmLT.js.gz +0 -0
  547. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.br +0 -0
  548. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.gz +0 -0
  549. codex/static_root/assets/reader-DdPKDuh5.js.br +0 -0
  550. codex/static_root/assets/reader-DdPKDuh5.js.gz +0 -0
  551. codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.br +0 -0
  552. codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.gz +0 -0
  553. codex/static_root/assets/relation-chips-C96hi5jm.js.br +0 -0
  554. codex/static_root/assets/relation-chips-C96hi5jm.js.gz +0 -0
  555. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.br +0 -0
  556. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.gz +0 -0
  557. codex/static_root/assets/settings-drawer-CqbT1rid.js.br +0 -0
  558. codex/static_root/assets/settings-drawer-CqbT1rid.js.gz +0 -0
  559. codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.br +0 -0
  560. codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.gz +0 -0
  561. codex/static_root/assets/stats-tab-CkT2ZGUQ.js.br +0 -0
  562. codex/static_root/assets/stats-tab-CkT2ZGUQ.js.gz +0 -0
  563. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.br +0 -0
  564. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.gz +0 -0
  565. codex/static_root/assets/task-tab-a-DcU3Tj.js.br +0 -0
  566. codex/static_root/assets/task-tab-a-DcU3Tj.js.gz +0 -0
  567. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.br +0 -0
  568. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.gz +0 -0
  569. codex/static_root/assets/unauthorized-BOl5YpL3.js.br +0 -0
  570. codex/static_root/assets/unauthorized-BOl5YpL3.js.gz +0 -0
  571. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.br +0 -0
  572. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.gz +0 -0
  573. codex/static_root/assets/user-tab-BBDZvD8G.js.br +0 -0
  574. codex/static_root/assets/user-tab-BBDZvD8G.js.gz +0 -0
  575. codex/static_root/manifest.225989a17f49.json.br +0 -0
  576. codex/static_root/manifest.225989a17f49.json.gz +0 -0
  577. {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/LICENSE +0 -0
  578. {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/entry_points.txt +0 -0
codex/asgi.py CHANGED
@@ -1,4 +1,5 @@
1
- """ASGI config for codex project.
1
+ """
2
+ ASGI config for codex project.
2
3
 
3
4
  It exposes the ASGI callable as a module-level variable named ``DJANGO_APPLICATION``.
4
5
 
codex/choices.py CHANGED
@@ -28,12 +28,14 @@ BROWSER_ORDER_BY_CHOICES = MappingProxyType(
28
28
  "updated_at": "Updated Time",
29
29
  }
30
30
  )
31
- _GROUP_NAMES = {
32
- "p": "Publishers",
33
- "i": "Imprints",
34
- "s": "Series",
35
- "v": "Volumes",
36
- }
31
+ _GROUP_NAMES = MappingProxyType(
32
+ {
33
+ "p": "Publishers",
34
+ "i": "Imprints",
35
+ "s": "Series",
36
+ "v": "Volumes",
37
+ }
38
+ )
37
39
  BROWSER_TOP_GROUP_CHOICES = MappingProxyType(
38
40
  {
39
41
  **_GROUP_NAMES,
@@ -42,6 +44,7 @@ BROWSER_TOP_GROUP_CHOICES = MappingProxyType(
42
44
  "a": "Story Arcs",
43
45
  },
44
46
  )
47
+ BROWSER_GROUP_CHOICES = MappingProxyType({**BROWSER_TOP_GROUP_CHOICES, "r": "Root"})
45
48
  VUETIFY_NULL_CODE = -1
46
49
  BROWSER_CHOICES = MappingProxyType(
47
50
  {
@@ -93,7 +96,6 @@ BROWSER_DEFAULTS = MappingProxyType(
93
96
  "order_by": "sort_name",
94
97
  "order_reverse": False,
95
98
  "q": "",
96
- # "search_results_limit": 100,
97
99
  "show": _DEFAULT_SHOW,
98
100
  "top_group": "p",
99
101
  "twenty_four_hour_time": False,
@@ -391,11 +393,9 @@ ADMIN_TASK_GROUPS = MappingProxyType(
391
393
 
392
394
  def _group_task_values(groups):
393
395
  """Extract values into sorted tuple."""
394
- values = []
395
- for group in groups["tasks"]:
396
- for item in group["tasks"]:
397
- values.append(item["value"])
398
- return tuple(sorted(values))
396
+ return tuple(
397
+ sorted([item["value"] for group in groups["tasks"] for item in group["tasks"]])
398
+ )
399
399
 
400
400
 
401
401
  ADMIN_TASKS = _group_task_values(ADMIN_TASK_GROUPS)
codex/choices_to_json.py CHANGED
@@ -18,6 +18,7 @@ from codex.choices import (
18
18
  READER_DEFAULTS,
19
19
  WEBSOCKET_MESSAGES,
20
20
  )
21
+ from codex.serializers.route import RouteSerializer
21
22
 
22
23
  _DEFAULTS = MappingProxyType(
23
24
  {"browser-choices.json": BROWSER_DEFAULTS, "reader-choices.json": READER_DEFAULTS}
@@ -68,12 +69,11 @@ def _make_json_serializable(data):
68
69
  if isinstance(data, Mapping):
69
70
  json_dict = {}
70
71
  for key, value in data.items():
71
- if key == "pks":
72
- # Special route serializer
73
- # XXX possibly should use actual route serializer for route not just pks
74
- json_dict[key] = ",".join(str(pk) for pk in sorted(value))
72
+ if key == "breadcrumbs":
73
+ json_value = tuple(RouteSerializer(dict(route)).data for route in value)
75
74
  else:
76
- json_dict[_json_key(key)] = _make_json_serializable(value)
75
+ json_value = _make_json_serializable(value)
76
+ json_dict[_json_key(key)] = json_value
77
77
  return json_dict
78
78
  if isinstance(data, list | tuple | frozenset | set):
79
79
  return [_make_json_serializable(item) for item in data]
@@ -93,7 +93,7 @@ def _to_vuetify_dict(fn, data):
93
93
  return vuetify_data
94
94
 
95
95
 
96
- def _dump(parent_path, fn, data, vuetify=True):
96
+ def _dump(parent_path, fn, data, vuetify):
97
97
  """Dump data to json file."""
98
98
  vuetify_data = (
99
99
  _to_vuetify_dict(fn, data) if vuetify else _make_json_serializable(data)
@@ -112,10 +112,10 @@ def main():
112
112
  parent_path = Path(parent_path)
113
113
  parent_path.mkdir(exist_ok=True)
114
114
  for fn, data in _DUMPS.items():
115
- _dump(parent_path, fn, data)
115
+ _dump(parent_path, fn, data, vuetify=True)
116
116
 
117
117
  for fn, data in _MAP_DUMPS.items():
118
- _dump(parent_path, fn, data, False)
118
+ _dump(parent_path, fn, data, vuetify=False)
119
119
 
120
120
 
121
121
  if __name__ == "__main__":
codex/db.py CHANGED
@@ -62,7 +62,7 @@ def _backup_db_before_migration():
62
62
  """If there are migrations to do, backup the db."""
63
63
  backup_path = _get_backup_db_path(f"before-v{VERSION}")
64
64
  janitor = Janitor(LOG_QUEUE, LIBRARIAN_QUEUE)
65
- janitor.backup_db(backup_path, show_status=False)
65
+ janitor.backup_db(show_status=False, backup_path=backup_path)
66
66
  LOG.info("Backed up database before migrations")
67
67
 
68
68
 
@@ -71,7 +71,7 @@ def _repair_db():
71
71
  if FIX_FOREIGN_KEYS:
72
72
  fix_foreign_keys()
73
73
  if INTEGRITY_CHECK:
74
- integrity_check()
74
+ integrity_check(long=True)
75
75
  success = fts_integrity_check() if FTS_INTEGRITY_CHECK else True
76
76
  if FTS_REBUILD or not success:
77
77
  fts_rebuild()
codex/exceptions.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """Special Redirect Error."""
2
2
 
3
+ from collections.abc import Mapping
3
4
  from copy import deepcopy
4
5
  from pprint import pformat
5
- from typing import TYPE_CHECKING
6
6
 
7
7
  from django.core.validators import EMPTY_VALUES
8
8
  from django.shortcuts import redirect
@@ -13,14 +13,10 @@ from rest_framework.status import HTTP_303_SEE_OTHER
13
13
 
14
14
  from codex.choices import DEFAULT_BROWSER_ROUTE
15
15
  from codex.logger.logging import get_logger
16
- from codex.serializers.fields import BreadcrumbsField
16
+ from codex.serializers.fields.browser import BreadcrumbsField
17
17
  from codex.serializers.route import RouteSerializer
18
18
  from codex.views.util import pop_name
19
19
 
20
- if TYPE_CHECKING:
21
- from collections.abc import Mapping
22
-
23
-
24
20
  LOG = get_logger(__name__)
25
21
  _OPDS_REDIRECT_SETTINGS_KEYS = ("order_by", "top_group")
26
22
  _REDIRECT_SETTINGS_KEYS = ("breadcrumbs", *_OPDS_REDIRECT_SETTINGS_KEYS)
@@ -71,7 +67,10 @@ class SeeOtherRedirectError(APIException):
71
67
  def _add_query_params(self, url):
72
68
  """Change OPDS settings like the frontend does with error.detail."""
73
69
  query_params = {}
74
- settings: Mapping = self.detail.get("settings", {}) # type: ignore
70
+ if isinstance(self.detail, Mapping):
71
+ settings = self.detail.get("settings", {})
72
+ else:
73
+ settings = {}
75
74
  for key in _OPDS_REDIRECT_SETTINGS_KEYS:
76
75
  value = settings.get(key)
77
76
  if value not in EMPTY_VALUES:
@@ -21,15 +21,19 @@ class BookmarkThread(AggregateMessageQueuedThread, BookmarkUpdate):
21
21
  else:
22
22
  self.log.warning(f"Unknown Bookmark task {item}")
23
23
 
24
+ def _send_item(self, filters, updates):
25
+ """Process one item."""
26
+ auth_filter, comic_pks = filters
27
+ auth_filter = dict(auth_filter)
28
+ self.update_bookmarks(auth_filter, comic_pks, updates, self.log)
29
+ self.log.debug(f"updated {auth_filter} pk__in={comic_pks}")
30
+
24
31
  def send_all_items(self):
25
32
  """Run the task method."""
26
33
  cleanup = set()
27
34
  for filters, updates in self.cache.items():
28
35
  try:
29
- auth_filter, comic_pks = filters
30
- auth_filter = dict(auth_filter)
31
- self.update_bookmarks(auth_filter, comic_pks, updates, self.log)
32
- self.log.debug(f"updated {auth_filter} pk__in={comic_pks}")
36
+ self._send_item(filters, updates)
33
37
  cleanup.add(filters)
34
38
  except Exception:
35
39
  self.log.exception("Updating bookmarks")
@@ -28,6 +28,35 @@ class BookmarkUpdate(GroupACLMixin):
28
28
 
29
29
  # Used by Bookmarkd and view.bookmark.
30
30
 
31
+ @classmethod
32
+ def _get_existing_bookmarks_for_update(cls, auth_filter, comic_pks, updates):
33
+ # Get existing bookmarks
34
+ query_filter = Q(**auth_filter) & Q(comic__in=comic_pks)
35
+ existing_bookmarks = Bookmark.objects.filter(query_filter)
36
+ if updates.get("page") is not None:
37
+ existing_bookmarks = existing_bookmarks.annotate(
38
+ page_count=F("comic__page_count")
39
+ )
40
+
41
+ update_fields = (set(updates.keys()) & _BOOKMARK_UPDATE_FIELDS) | {"updated_at"}
42
+ only_fields = (*update_fields, "pk")
43
+
44
+ existing_bookmarks = existing_bookmarks.only(*only_fields)
45
+ return existing_bookmarks, update_fields
46
+
47
+ @classmethod
48
+ def _prepare_bookmark_updates(cls, existing_bookmarks, updates):
49
+ # Prepare updates
50
+ update_bookmarks = []
51
+ for bm in existing_bookmarks:
52
+ cls._update_bookmarks_validate_page(bm, updates)
53
+ cls._update_bookmarks_validate_two_pages(bm, updates)
54
+ for key, value in updates.items():
55
+ setattr(bm, key, value)
56
+ bm.updated_at = Now()
57
+ update_bookmarks.append(bm)
58
+ return update_bookmarks
59
+
31
60
  @staticmethod
32
61
  def _update_bookmarks_validate_page(bm, updates):
33
62
  """Force bookmark page into valid range."""
@@ -44,9 +73,10 @@ class BookmarkUpdate(GroupACLMixin):
44
73
  @staticmethod
45
74
  def _update_bookmarks_validate_two_pages(bm, updates):
46
75
  """Force vertical view to not use two pages."""
47
- rd = updates.get("reading_direction")
48
76
  if bm.two_pages and bool(
49
- {rd, bm.reading_direction}.intersection(_VERTICAL_READING_DIRECTIONS)
77
+ {bm.reading_direction, updates.get("reading_direction")}.intersection(
78
+ _VERTICAL_READING_DIRECTIONS
79
+ )
50
80
  ):
51
81
  updates["two_pages"] = None
52
82
 
@@ -58,37 +88,41 @@ class BookmarkUpdate(GroupACLMixin):
58
88
  LIBRARIAN_QUEUE.put(task)
59
89
 
60
90
  @classmethod
61
- def _update_bookmarks(cls, auth_filter, comic_pks, updates, user) -> int:
91
+ def _update_bookmarks(cls, auth_filter, comic_pks, updates) -> int:
62
92
  """Update existing bookmarks."""
63
93
  count = 0
64
94
  if not updates:
65
95
  return count
66
- group_acl_filter = cls.get_group_acl_filter(Bookmark, user)
67
- query_filter = group_acl_filter & Q(**auth_filter) & Q(comic__in=comic_pks)
68
- existing_bookmarks = Bookmark.objects.filter(query_filter)
69
- if updates.get("page") is not None:
70
- existing_bookmarks = existing_bookmarks.annotate(
71
- page_count=F("comic__page_count")
72
- )
73
96
 
74
- update_fields = set(updates.keys()) & _BOOKMARK_UPDATE_FIELDS
75
- update_fields.add("updated_at")
76
- only_fields = (*update_fields, "pk")
77
-
78
- existing_bookmarks = existing_bookmarks.only(*only_fields)
79
- update_bookmarks = []
80
- for bm in existing_bookmarks:
81
- cls._update_bookmarks_validate_page(bm, updates)
82
- cls._update_bookmarks_validate_two_pages(bm, updates)
83
- for key, value in updates.items():
84
- setattr(bm, key, value)
85
- bm.updated_at = Now()
86
- update_bookmarks.append(bm)
97
+ existing_bookmarks, update_fields = cls._get_existing_bookmarks_for_update(
98
+ auth_filter, comic_pks, updates
99
+ )
100
+ update_bookmarks = cls._prepare_bookmark_updates(existing_bookmarks, updates)
87
101
  count = len(update_bookmarks)
102
+
103
+ # Bulk update
88
104
  if count:
89
105
  Bookmark.objects.bulk_update(update_bookmarks, tuple(update_fields))
90
106
  return count
91
107
 
108
+ @classmethod
109
+ def _get_comics_without_bookmarks(cls, auth_filter, comic_pks):
110
+ """Get comics without bookmarks."""
111
+ exclude = {}
112
+ for key, value in auth_filter.items():
113
+ exclude["bookmark__" + key] = value
114
+ query_filter = Q(pk__in=comic_pks) & ~Q(**exclude)
115
+ return Comic.objects.filter(query_filter).only("pk")
116
+
117
+ @classmethod
118
+ def _prepare_bookmark_creates(cls, create_bookmark_comics, auth_filter, updates):
119
+ # Prepare creates
120
+ create_bookmarks = []
121
+ for comic in create_bookmark_comics:
122
+ bm = Bookmark(comic=comic, **auth_filter, **updates)
123
+ create_bookmarks.append(bm)
124
+ return create_bookmarks
125
+
92
126
  @staticmethod
93
127
  def _create_bookmarks_validate_two_pages(updates):
94
128
  """Force vertical view to not use two pages."""
@@ -99,54 +133,54 @@ class BookmarkUpdate(GroupACLMixin):
99
133
  updates.pop("two_pages", None)
100
134
 
101
135
  @classmethod
102
- def _create_bookmarks(cls, auth_filter, comic_pks, updates, user) -> int:
136
+ def _create_bookmarks(cls, auth_filter, comic_pks, updates) -> int:
103
137
  """Create new bookmarks for comics that don't exist yet."""
104
138
  count = 0
105
139
  cls._create_bookmarks_validate_two_pages(updates)
106
140
  if not updates:
107
141
  return count
108
- create_bookmarks = []
109
- group_acl_filter = cls.get_group_acl_filter(Comic, user)
110
- exclude = {}
111
- for key, value in auth_filter.items():
112
- exclude["bookmark__" + key] = value
113
- query_filter = group_acl_filter & Q(pk__in=comic_pks) & ~Q(**exclude)
114
- create_bookmark_comics = Comic.objects.filter(query_filter).only("pk")
115
- for comic in create_bookmark_comics:
116
- bm = Bookmark(comic=comic, **auth_filter, **updates)
117
- create_bookmarks.append(bm)
142
+
143
+ create_bookmark_comics = cls._get_comics_without_bookmarks(
144
+ auth_filter, comic_pks
145
+ )
146
+ create_bookmarks = cls._prepare_bookmark_creates(
147
+ create_bookmark_comics, auth_filter, updates
148
+ )
118
149
  count = len(create_bookmarks)
150
+
151
+ # Bulk create
119
152
  if count:
120
153
  Bookmark.objects.bulk_create(
121
154
  create_bookmarks,
122
155
  update_fields=tuple(_BOOKMARK_UPDATE_FIELDS),
123
- unique_fields=Bookmark._meta.unique_together[0], # type: ignore
156
+ unique_fields=Bookmark._meta.unique_together[0],
124
157
  )
125
158
  return count
126
159
 
127
160
  @classmethod
128
- def _update_user_active(cls, user, log):
161
+ def _update_user_active(cls, auth_filter, log):
129
162
  """Update user active."""
130
163
  # Offline because profile gets hit rapidly in succession.
131
164
  try:
132
- UserActive.objects.update_or_create(user=user)
165
+ user = None
166
+ for key, value in auth_filter.items():
167
+ if key == "user":
168
+ user = User.objects.get(pk=value)
169
+ if user:
170
+ UserActive.objects.update_or_create(user=user)
171
+ break
172
+ except User.DoesNotExist:
173
+ pass
133
174
  except Exception as exc:
134
- reason = f"update user activity {exc}"
175
+ reason = f"Update user activity {exc}"
135
176
  log.warning(reason)
136
177
 
137
178
  @classmethod
138
179
  def update_bookmarks(cls, auth_filter, comic_pks, updates, log):
139
180
  """Update a user bookmark."""
140
- user = None
141
- for key, value in auth_filter.items():
142
- if key == "user":
143
- user = User.objects.get(pk=value)
144
- break
145
-
146
- count = cls._update_bookmarks(auth_filter, comic_pks, updates, user)
147
- count += cls._create_bookmarks(auth_filter, comic_pks, updates, user)
148
- if user:
149
- cls._update_user_active(user, log)
181
+ count = cls._update_bookmarks(auth_filter, comic_pks, updates)
182
+ count += cls._create_bookmarks(auth_filter, comic_pks, updates)
183
+ cls._update_user_active(auth_filter, log)
150
184
  if count:
151
185
  uid = next(iter(auth_filter.values()))
152
186
  cls._notify_library_changed(uid)
@@ -32,7 +32,7 @@ class CoverCreateThread(QueuedThread, CoverPathMixin):
32
32
  with Image.open(image_io) as cover_image:
33
33
  cover_image.thumbnail(
34
34
  _THUMBNAIL_SIZE,
35
- Image.Resampling.LANCZOS, # type: ignore
35
+ Image.Resampling.LANCZOS,
36
36
  reducing_gap=3.0,
37
37
  )
38
38
  cover_image.save(cover_thumb_buffer, "WEBP", method=6)
@@ -41,7 +41,8 @@ class CoverCreateThread(QueuedThread, CoverPathMixin):
41
41
 
42
42
  @classmethod
43
43
  def _get_comic_cover_image(cls, comic_path):
44
- """Create comic cover if none exists.
44
+ """
45
+ Create comic cover if none exists.
45
46
 
46
47
  Return image thumb data or path to missing file thumb.
47
48
  """
@@ -59,8 +60,9 @@ class CoverCreateThread(QueuedThread, CoverPathMixin):
59
60
  return f.read()
60
61
 
61
62
  @classmethod
62
- def create_cover_from_path(cls, pk, cover_path, log, librarian_queue, custom=False):
63
- """Create cover for path.
63
+ def create_cover_from_path(cls, pk, cover_path, log, librarian_queue, custom):
64
+ """
65
+ Create cover for path.
64
66
 
65
67
  Called from views/cover.
66
68
  """
@@ -95,7 +97,7 @@ class CoverCreateThread(QueuedThread, CoverPathMixin):
95
97
  # zero length file is code for missing.
96
98
  cover_path.touch()
97
99
 
98
- def _bulk_create_comic_covers(self, pks, custom=False):
100
+ def _bulk_create_comic_covers(self, pks, custom):
99
101
  """Create bulk comic covers."""
100
102
  num_comics = len(pks)
101
103
  if not num_comics:
@@ -115,7 +117,7 @@ class CoverCreateThread(QueuedThread, CoverPathMixin):
115
117
  else:
116
118
  # bulk contributor creates covers inline
117
119
  data = self.create_cover_from_path(
118
- pk, cover_path, self.log, self.librarian_queue
120
+ pk, cover_path, self.log, self.librarian_queue, custom=False
119
121
  )
120
122
  if data:
121
123
  data.close()
@@ -134,6 +136,6 @@ class CoverCreateThread(QueuedThread, CoverPathMixin):
134
136
  def create_all_covers(self):
135
137
  """Create all covers for all libraries."""
136
138
  pks = CustomCover.objects.values_list("pk", flat=True)
137
- self._bulk_create_comic_covers(pks, True)
139
+ self._bulk_create_comic_covers(pks, custom=True)
138
140
  pks = Comic.objects.values_list("pk", flat=True)
139
- self._bulk_create_comic_covers(pks, False)
141
+ self._bulk_create_comic_covers(pks, custom=False)
@@ -21,20 +21,21 @@ class CoverPathMixin:
21
21
  """Translate an integer into an efficient filesystem path."""
22
22
  fnv = fnv1a_32(bytes(str(pk).zfill(cls._ZFILL), "utf-8"))
23
23
  hex_str = format(fnv, f"0{cls._ZFILL}x")
24
- parts = []
25
- for i in range(0, len(hex_str), cls._PATH_STEP):
26
- parts.append(hex_str[i : i + cls._PATH_STEP])
24
+ parts = [
25
+ hex_str[i : i + cls._PATH_STEP]
26
+ for i in range(0, len(hex_str), cls._PATH_STEP)
27
+ ]
27
28
  return Path("/".join(parts))
28
29
 
29
30
  @classmethod
30
- def get_cover_path(cls, pk, custom=False):
31
+ def get_cover_path(cls, pk, custom):
31
32
  """Get cover path for comic pk."""
32
33
  cover_path = cls._hex_path(pk)
33
34
  root = cls.CUSTOM_COVERS_ROOT if custom else cls.COVERS_ROOT
34
35
  return root / cover_path.with_suffix(".webp")
35
36
 
36
37
  @classmethod
37
- def get_cover_paths(cls, pks, custom=False):
38
+ def get_cover_paths(cls, pks, custom):
38
39
  """Get cover paths for many comic pks."""
39
40
  cover_paths = set()
40
41
  for pk in pks:
@@ -67,7 +67,7 @@ class CoverPurgeThread(CoverCreateThread):
67
67
  self.log.info("Removed entire comic cover cache.")
68
68
  shutil.rmtree(self.CUSTOM_COVERS_ROOT)
69
69
  self.log.info("Removed entire custom cover cache.")
70
- except Exception as exc:
70
+ except OSError as exc:
71
71
  self.log.warning(exc)
72
72
  librarian_queue.put(LIBRARY_CHANGED_TASK)
73
73
 
@@ -77,7 +77,7 @@ class CoverPurgeThread(CoverCreateThread):
77
77
  self.log.debug(f"Removing covers from missing {name}.")
78
78
  self.status_controller.start_many(self._CLEANUP_STATUS_MAP)
79
79
  pks = cover_class.objects.all().values_list("pk", flat=True)
80
- db_cover_paths = self.get_cover_paths(pks)
80
+ db_cover_paths = self.get_cover_paths(pks, custom=False)
81
81
 
82
82
  orphan_cover_paths = set()
83
83
  for root, _, filenames in os.walk(cover_root):
@@ -222,10 +222,10 @@ class AggregateMetadataImporter(ExtractMetadataImporter):
222
222
  if self.task.force_import_metadata:
223
223
  import_metadata = True
224
224
  else:
225
- key = AdminFlag.FlagChoices.IMPORT_METADATA.value # type: ignore
225
+ key = AdminFlag.FlagChoices.IMPORT_METADATA.value
226
226
  import_metadata = AdminFlag.objects.get(key=key).on
227
227
  if not import_metadata:
228
- self.log.warn("Admin flag set to NOT import metadata.")
228
+ self.log.warning("Admin flag set to NOT import metadata.")
229
229
 
230
230
  # Init metadata, extract and aggregate
231
231
  self.metadata[MDS] = {}
@@ -73,7 +73,6 @@ class CacheUpdateImporter(InitImporter):
73
73
  try:
74
74
  log_list = []
75
75
  for model in GROUP_MODELS:
76
- # self.log.debug(f"Updating timestamps for {model.__name__}s...")
77
76
  count = self._update_group_model(
78
77
  force_update_group_map, model, start_time, log_list
79
78
  )
@@ -85,9 +84,7 @@ class CacheUpdateImporter(InitImporter):
85
84
 
86
85
  if total_count:
87
86
  groups_log = ", ".join(log_list)
88
- self.log.info( # type: ignore
89
- f"Updated timestamps for {groups_log}."
90
- )
87
+ self.log.info(f"Updated timestamps for {groups_log}.")
91
88
  self.changed += total_count
92
89
  finally:
93
90
  self.status_controller.finish(status)
@@ -78,7 +78,7 @@ class CreateComicsImporter(LinkComicsImporter):
78
78
  Comic.objects.bulk_update(update_comics, BULK_UPDATE_COMIC_FIELDS)
79
79
  count = len(update_comics)
80
80
 
81
- self._remove_covers(comic_pks, False) # type: ignore
81
+ self._remove_covers(comic_pks, custom=False)
82
82
  self.log.debug(f"Purging covers for {len(comic_pks)} updated comics.")
83
83
  if count:
84
84
  self.log.info(f"Updated {count} comics.")
@@ -126,7 +126,7 @@ class CreateComicsImporter(LinkComicsImporter):
126
126
  create_comics,
127
127
  update_conflicts=True,
128
128
  update_fields=BULK_CREATE_COMIC_FIELDS,
129
- unique_fields=Comic._meta.unique_together[0], # type: ignore
129
+ unique_fields=Comic._meta.unique_together[0],
130
130
  )
131
131
  count = len(create_comics)
132
132
  if count:
@@ -43,7 +43,7 @@ class CreateCoversImporter(CreateComicsImporter):
43
43
  if LINK_COVER_PKS not in self.metadata:
44
44
  self.metadata[LINK_COVER_PKS] = set()
45
45
  self.metadata[LINK_COVER_PKS].update(update_cover_pks)
46
- self._remove_covers(update_cover_pks, custom=True) # type: ignore
46
+ self._remove_covers(update_cover_pks, custom=True)
47
47
  count = len(update_covers)
48
48
  if status:
49
49
  status.add_complete(count)
@@ -1,4 +1,5 @@
1
- """Create all missing comic foreign keys for an import.
1
+ """
2
+ Create all missing comic foreign keys for an import.
2
3
 
3
4
  So we may safely create the comics next.
4
5
  """
@@ -189,7 +190,7 @@ class CreateForeignKeysImporter(CreateCoversImporter):
189
190
  create_folders,
190
191
  update_conflicts=True,
191
192
  update_fields=BULK_UPDATE_FOLDER_FIELDS,
192
- unique_fields=Folder._meta.unique_together[0], # type: ignore
193
+ unique_fields=Folder._meta.unique_together[0],
193
194
  )
194
195
  count = len(create_folders)
195
196
  status.add_complete(count)
@@ -296,7 +297,7 @@ class CreateForeignKeysImporter(CreateCoversImporter):
296
297
  create_objs,
297
298
  update_conflicts=True,
298
299
  update_fields=CREATE_DICT_UPDATE_FIELDS[model],
299
- unique_fields=model._meta.unique_together[0], # type: ignore
300
+ unique_fields=model._meta.unique_together[0],
300
301
  )
301
302
  count = len(create_objs)
302
303
  if count:
@@ -13,7 +13,7 @@ from codex.status import Status
13
13
  class DeletedImporter(CacheUpdateImporter):
14
14
  """Clean up database methods."""
15
15
 
16
- def _remove_covers(self, delete_pks, custom=False):
16
+ def _remove_covers(self, delete_pks, custom: bool):
17
17
  task = CoverRemoveTask(delete_pks, custom)
18
18
  self.librarian_queue.put(task)
19
19
 
@@ -34,7 +34,7 @@ class DeletedImporter(CacheUpdateImporter):
34
34
  )
35
35
  folders.delete()
36
36
 
37
- self._remove_covers(delete_comic_pks)
37
+ self._remove_covers(delete_comic_pks, custom=False)
38
38
 
39
39
  count = len(delete_comic_pks)
40
40
  if count:
@@ -96,7 +96,7 @@ class DeletedImporter(CacheUpdateImporter):
96
96
  delete_comic_pks = frozenset(delete_qs.values_list("pk", flat=True))
97
97
  delete_qs.delete()
98
98
 
99
- self._remove_covers(delete_comic_pks)
99
+ self._remove_covers(delete_comic_pks, custom=False)
100
100
 
101
101
  count = len(delete_comic_pks)
102
102
  if count: