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
@@ -9,8 +9,13 @@ from comicbox.box import Comicbox
9
9
  from comicbox.box.computed import IDENTIFIERS_KEY
10
10
  from comicbox.exceptions import UnsupportedArchiveTypeError
11
11
  from comicbox.schemas.comicbox_mixin import CONTRIBUTORS_KEY
12
- from django.db.models import CharField
13
- from django.db.models.fields import DecimalField, PositiveSmallIntegerField
12
+ from django.db.models.fields import (
13
+ CharField,
14
+ DecimalField,
15
+ PositiveSmallIntegerField,
16
+ TextField,
17
+ )
18
+ from nh3 import clean
14
19
  from rarfile import BadRarFile
15
20
 
16
21
  from codex.librarian.importer.const import FIS, STORY_ARCS_METADATA_KEY
@@ -36,29 +41,39 @@ _MD_INVALID_KEYS = frozenset(
36
41
  "updated_at",
37
42
  )
38
43
  )
39
- _MD_VALID_KEYS = frozenset(
40
- (
41
- *(
42
- field.name
43
- for field in Comic._meta.get_fields()
44
- if field.name not in _MD_INVALID_KEYS
45
- ),
46
- "story_arcs",
44
+ _MD_VALID_KEYS = tuple(
45
+ sorted(
46
+ frozenset(
47
+ (
48
+ *(
49
+ field.name
50
+ for field in Comic._meta.get_fields()
51
+ if field.name not in _MD_INVALID_KEYS
52
+ ),
53
+ "story_arcs",
54
+ )
55
+ )
47
56
  )
48
57
  )
49
- _MD_CHAR_KEYS = frozenset(
50
- field.name for field in Comic._meta.get_fields() if isinstance(field, CharField)
51
- )
52
- _MD_DECIMAL_KEYS = frozenset(
53
- field.name for field in Comic._meta.get_fields() if isinstance(field, DecimalField)
54
- )
55
- _MD_PSI_KEYS = frozenset(
56
- {
57
- field.name
58
- for field in Comic._meta.get_fields()
59
- if isinstance(field, PositiveSmallIntegerField)
60
- }
61
- )
58
+
59
+
60
+ def _get_keys_by_field_type(field_type):
61
+ return tuple(
62
+ sorted(
63
+ frozenset(
64
+ field.name
65
+ for field in Comic._meta.get_fields()
66
+ if isinstance(field, field_type)
67
+ )
68
+ )
69
+ )
70
+
71
+
72
+ _MD_CHAR_KEYS = _get_keys_by_field_type(CharField)
73
+ _MD_TEXT_KEYS = _get_keys_by_field_type(TextField)
74
+ _MD_STR_KEYS = tuple(sorted(_MD_CHAR_KEYS + _MD_TEXT_KEYS))
75
+ _MD_DECIMAL_KEYS = _get_keys_by_field_type(DecimalField)
76
+ _MD_PSI_KEYS = _get_keys_by_field_type(PositiveSmallIntegerField)
62
77
  _PSI_MAX = 2**31 - 1
63
78
  _SI_MIN = 2**15 * -1
64
79
  _SI_MAX = 2**15 - 1
@@ -94,7 +109,7 @@ class ExtractMetadataImporter(QueryForeignKeysImporter):
94
109
 
95
110
  @classmethod
96
111
  def _clean_decimal(cls, value, field_name: str):
97
- field: DecimalField = Comic._meta.get_field(field_name) # type: ignore
112
+ field: DecimalField = Comic._meta.get_field(field_name) # type: ignore[reportAssignmentType]
98
113
  try:
99
114
  quantize_str = Decimal(f"1e-{field.decimal_places}")
100
115
  value = value.quantize(quantize_str, rounding=ROUND_DOWN)
@@ -145,11 +160,15 @@ class ExtractMetadataImporter(QueryForeignKeysImporter):
145
160
  cls._assign_or_pop(obj, key, value)
146
161
 
147
162
  @staticmethod
148
- def _clean_charfield(model, field_name, value):
163
+ def _clean_strfield(model, field_name, value):
149
164
  try:
150
- field: CharField = model._meta.get_field(field_name) # type: ignore
151
165
  if value:
152
- value = value[: field.max_length].strip()
166
+ value = value.strip()
167
+ field = model._meta.get_field(field_name) # type: ignore[reportAssignmentType]
168
+ max_length = getattr(field, "max_length", 0)
169
+ if max_length:
170
+ value = value[:max_length]
171
+ value = clean(value)
153
172
  if not value:
154
173
  value = None
155
174
  except Exception:
@@ -157,10 +176,10 @@ class ExtractMetadataImporter(QueryForeignKeysImporter):
157
176
  return value
158
177
 
159
178
  @classmethod
160
- def _clean_charfields(cls, md: dict[str, Any]) -> None:
161
- for key in _MD_CHAR_KEYS:
179
+ def _clean_strfields(cls, md: dict[str, Any]) -> None:
180
+ for key in _MD_STR_KEYS:
162
181
  value = md.get(key)
163
- value = cls._clean_charfield(Comic, key, value)
182
+ value = cls._clean_strfield(Comic, key, value)
164
183
  cls._assign_or_pop(md, key, value)
165
184
 
166
185
  @classmethod
@@ -182,10 +201,10 @@ class ExtractMetadataImporter(QueryForeignKeysImporter):
182
201
  return
183
202
  clean_contributors = {}
184
203
  for role, persons in contributors.items():
185
- clean_role = cls._clean_charfield(ContributorRole, "name", role)
204
+ clean_role = cls._clean_strfield(ContributorRole, "name", role)
186
205
  clean_persons = set()
187
206
  for person in persons:
188
- clean_person = cls._clean_charfield(ContributorPerson, "name", person)
207
+ clean_person = cls._clean_strfield(ContributorPerson, "name", person)
189
208
  if clean_person:
190
209
  clean_persons.add(clean_person)
191
210
  clean_contributors[clean_role] = clean_persons
@@ -198,7 +217,7 @@ class ExtractMetadataImporter(QueryForeignKeysImporter):
198
217
  return
199
218
  clean_story_arcs = {}
200
219
  for story_arc, number in story_arcs.items():
201
- clean_story_arc = cls._clean_charfield(StoryArc, "name", story_arc)
220
+ clean_story_arc = cls._clean_strfield(StoryArc, "name", story_arc)
202
221
  if clean_story_arc:
203
222
  clean_story_arcs[clean_story_arc] = number
204
223
  cls._assign_or_pop(md, STORY_ARCS_METADATA_KEY, clean_story_arcs)
@@ -210,11 +229,11 @@ class ExtractMetadataImporter(QueryForeignKeysImporter):
210
229
  return
211
230
  clean_identifiers = {}
212
231
  for nid, identifier in identifiers.items():
213
- clean_id_type = cls._clean_charfield(IdentifierType, "name", nid)
232
+ clean_id_type = cls._clean_strfield(IdentifierType, "name", nid)
214
233
  nss = identifier.get("nss")
215
- clean_nss = cls._clean_charfield(Identifier, "nss", nss)
234
+ clean_nss = cls._clean_strfield(Identifier, "nss", nss)
216
235
  url = identifier.get("url")
217
- clean_url = cls._clean_charfield(Identifier, "url", url)
236
+ clean_url = cls._clean_strfield(Identifier, "url", url)
218
237
  if clean_nss:
219
238
  clean_identifier = {"nss": nss}
220
239
  if clean_url:
@@ -230,7 +249,7 @@ class ExtractMetadataImporter(QueryForeignKeysImporter):
230
249
  cls._clean_comic_positive_small_ints(md)
231
250
  cls._clean_comic_small_ints(md)
232
251
  cls._clean_comic_decimals(md)
233
- cls._clean_charfields(md)
252
+ cls._clean_strfields(md)
234
253
  cls._clean_contributors(md)
235
254
  cls._clean_story_arcs(md)
236
255
  cls._clean_identifiers(md)
@@ -120,7 +120,7 @@ class FailedImportsImporter(DeletedImporter):
120
120
  create_objs,
121
121
  update_conflicts=True,
122
122
  update_fields=_BULK_UPDATE_FAILED_IMPORT_FIELDS,
123
- unique_fields=FailedImport._meta.unique_together[0], # type: ignore
123
+ unique_fields=FailedImport._meta.unique_together[0],
124
124
  )
125
125
  count = len(create_objs)
126
126
  if count:
@@ -43,7 +43,9 @@ class ComicImporter(MovedImporter):
43
43
 
44
44
  # Wait to start the search index update in case more updates are incoming.
45
45
  until = time() + 1
46
- delayed_search_task = DelayedTasks(until, (SearchIndexUpdateTask(False),))
46
+ delayed_search_task = DelayedTasks(
47
+ until, (SearchIndexUpdateTask(rebuild=False),)
48
+ )
47
49
  self.librarian_queue.put(delayed_search_task)
48
50
  else:
49
51
  self.log.info("No updates neccissary.")
@@ -35,7 +35,7 @@ class ComicImporterThread(QueuedThread):
35
35
  import_comics = Comic.objects.filter(pk__in=task.pks).only("path", "library_id")
36
36
  library_path_map = {}
37
37
  for import_comic in import_comics:
38
- library_id = import_comic.library_id # type: ignore
38
+ library_id = import_comic.library_id # type: ignore[reportAttributeAccessIssue]
39
39
  if library_id not in library_path_map:
40
40
  library_path_map[library_id] = set()
41
41
  library_path_map[library_id].add(import_comic.path)
@@ -89,7 +89,7 @@ class ComicImporterThread(QueuedThread):
89
89
  importer.bulk_folders_moved()
90
90
  return True
91
91
 
92
- def _adopt_orphan_folders(self, janitor=False):
92
+ def _adopt_orphan_folders(self, janitor: bool):
93
93
  """Find orphan folders and move them into their correct place."""
94
94
  status = Status(ImportStatusTypes.ADOPT_FOLDERS)
95
95
  moved_status = Status(ImportStatusTypes.DIRS_MOVED)
@@ -118,6 +118,6 @@ class ComicImporterThread(QueuedThread):
118
118
  elif isinstance(task, UpdateGroupsTask):
119
119
  self._update_groups(task)
120
120
  elif isinstance(task, AdoptOrphanFoldersTask):
121
- self._adopt_orphan_folders(task.janitor)
121
+ self._adopt_orphan_folders(janitor=task.janitor)
122
122
  else:
123
123
  self.log.warning(f"Bad task sent to library updater {task}")
@@ -186,7 +186,8 @@ class LinkComicsImporter(LinkCoversImporter):
186
186
  return tms, all_del_pks
187
187
 
188
188
  def bulk_fix_comic_m2m_field(self, field_name, m2m_links, status):
189
- """Recreate an m2m field for a set of comics.
189
+ """
190
+ Recreate an m2m field for a set of comics.
190
191
 
191
192
  Since we can't bulk_update or bulk_create m2m fields use a trick.
192
193
  bulk_create() on the through table:
@@ -46,9 +46,7 @@ class MovedImporter(AggregateMetadataImporter):
46
46
  new_path = self.task.files_moved[comic.path]
47
47
  comic.path = new_path
48
48
  new_path = Path(new_path)
49
- comic.parent_folder = Folder.objects.get( # type: ignore
50
- path=new_path.parent
51
- )
49
+ comic.parent_folder = Folder.objects.get(path=new_path.parent)
52
50
  comic.updated_at = Now()
53
51
  comic.presave()
54
52
  folder_m2m_links[comic.pk] = Folder.objects.filter(
@@ -137,7 +135,7 @@ class MovedImporter(AggregateMetadataImporter):
137
135
  f"Unlinked {len(unlink_groups)} {model.__name__} moved custom covers."
138
136
  )
139
137
 
140
- self._remove_covers(unlink_pks, custom=True) # type: ignore
138
+ self._remove_covers(unlink_pks, custom=True)
141
139
 
142
140
  def _bulk_covers_moved(self, status=None):
143
141
  """Move covers."""
@@ -194,8 +192,6 @@ class MovedImporter(AggregateMetadataImporter):
194
192
  folder.updated_at = Now()
195
193
  update_folders.append(folder)
196
194
 
197
- # delete_folders = Folder.objects.filter(library=self.library, path__in=new_paths)
198
-
199
195
  update_folders = sorted(update_folders, key=lambda x: len(Path(x.path).parts))
200
196
 
201
197
  Folder.objects.bulk_update(update_folders, MOVED_BULK_FOLDER_UPDATE_FIELDS)
@@ -212,7 +208,6 @@ class MovedImporter(AggregateMetadataImporter):
212
208
  """Move folders under existing folders."""
213
209
  while True:
214
210
  # Get existing parent folders
215
- # dest_parent_paths = tuple(str(path) for path in dest_parent_folder_paths_map)
216
211
  dest_parent_paths = tuple(dest_parent_folder_paths_map.keys())
217
212
  extant_parent_folders = Folder.objects.filter(
218
213
  library=self.library, path__in=dest_parent_paths
@@ -229,9 +229,7 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
229
229
 
230
230
  group_filter = Q()
231
231
  for group_tree, count_value in update_group_trees.items():
232
- compare_filter = {}
233
- for field_name, value in zip(compare_fields, group_tree, strict=False):
234
- compare_filter[field_name] = value
232
+ compare_filter = dict(zip(compare_fields, group_tree, strict=False))
235
233
  compare_filter[count_field_name] = count_value
236
234
  group_filter |= Q(**compare_filter)
237
235
  return group_filter
@@ -494,7 +492,6 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
494
492
 
495
493
  def _query_missing_simple_models(self, names, fk_data, status):
496
494
  """Find missing named models and folders."""
497
- # count = 0
498
495
  if not names:
499
496
  return 0
500
497
  create_fks, base_cls, field, fk_field = fk_data
@@ -18,7 +18,7 @@ class ImportDBDiffTask(ImportTask):
18
18
 
19
19
  dirs_moved: Mapping[str, str] = field(default_factory=dict)
20
20
  dirs_modified: frozenset[str] = frozenset()
21
- # dirs_created: frozenset[str] | None = frozenset()
21
+ # dirs_created: frozenset[str] | None = frozenset() # noqa: ERA001
22
22
  dirs_deleted: frozenset[str] = frozenset()
23
23
 
24
24
  files_moved: Mapping[str, str] = field(default_factory=dict)
@@ -2,6 +2,7 @@
2
2
  # Uses app.get_model() because functions may also be called before the models are ready on startup.
3
3
 
4
4
  import re
5
+ from typing import TYPE_CHECKING
5
6
 
6
7
  from django.apps import apps
7
8
  from django.db import DEFAULT_DB_ALIAS, connections
@@ -19,6 +20,13 @@ from codex.settings.settings import (
19
20
  from codex.status import Status
20
21
  from codex.worker_base import WorkerBaseMixin
21
22
 
23
+ if TYPE_CHECKING:
24
+ from django.db.models.manager import BaseManager
25
+
26
+ from codex.models.base import BaseModel
27
+ from codex.models.comic import Comic
28
+
29
+
22
30
  REPAIR_FLAG_PATH = CONFIG_PATH / "rebuild_db"
23
31
  REBUILT_DB_PATH = DB_PATH.parent / (DB_PATH.name + ".rebuilt")
24
32
  BACKUP_DB_PATH = DB_PATH.parent / (DB_PATH.name + ".bak")
@@ -105,7 +113,7 @@ def _null_bad_fk_rels_table(table_name, bad_rows, log):
105
113
  fields = model._meta.fields
106
114
  field_names = {}
107
115
  update_objs = []
108
- objs = model.objects.filter(pk__in=bad_rows.keys())
116
+ objs: BaseManager[BaseModel] = model.objects.filter(pk__in=bad_rows.keys()) # type: ignore[reportAssignmentType]
109
117
  update_fields = {"updated_at"}
110
118
  now = Now()
111
119
  for obj in objs:
@@ -119,7 +127,7 @@ def _null_bad_fk_rels_table(table_name, bad_rows, log):
119
127
  field_name = field_names[fkid]
120
128
  setattr(obj, field_name, None)
121
129
  update_fields.add(field_name)
122
- obj.updated_at = now # type: ignore
130
+ obj.updated_at = now
123
131
  update_objs.append(obj)
124
132
  except Exception as exc:
125
133
  log.warning(
@@ -142,10 +150,10 @@ def _null_bad_fk_rels(bad_fk_rels, log):
142
150
  for table_name, bad_rows in bad_fk_rels.items():
143
151
  try:
144
152
  fix_comic_pks |= _null_bad_fk_rels_table(table_name, bad_rows, log)
145
- except Exception as exc:
153
+ except Exception:
146
154
  pks = sorted(bad_rows.keys())
147
- log.error( # noqa: TRY400
148
- f"Unable to null {len(pks)} {table_name} rows with bad foreign keys - {exc}"
155
+ log.exception(
156
+ f"Unable to null {len(pks)} {table_name} rows with bad foreign keys"
149
157
  )
150
158
  log.error(f"{table_name}: {pks}") # noqa: TRY400
151
159
  return fix_comic_pks
@@ -175,10 +183,8 @@ def _delete_bad_m2m_rows(bad_m2m_rows, log):
175
183
  for table_name, ids in bad_m2m_rows.items():
176
184
  try:
177
185
  count += _delete_bad_m2m_rows_table(table_name, ids, fix_comic_pks, log)
178
- except Exception as exc:
179
- log.error( # noqa: TRY400
180
- f"Could not delete bad foreign keys in table {table_name} - {exc}"
181
- )
186
+ except Exception:
187
+ log.exception(f"Could not delete bad foreign keys in table {table_name}")
182
188
  if count:
183
189
  log.info(f"Removed {count} bad foreign key relations.")
184
190
  return fix_comic_pks
@@ -189,7 +195,9 @@ def _mark_comics_for_update(fix_comic_pks, log):
189
195
  if not fix_comic_pks:
190
196
  return
191
197
  comic_model = apps.get_model(app_label="codex", model_name="comic")
192
- outdated_comics = comic_model.objects.filter(pk__in=fix_comic_pks).only(
198
+ outdated_comics: BaseManager[Comic] = comic_model.objects.filter(
199
+ pk__in=fix_comic_pks
200
+ ).only( # type: ignore[reportAssignmentType]
193
201
  "stat", "updated_at"
194
202
  )
195
203
  if not outdated_comics:
@@ -198,12 +206,12 @@ def _mark_comics_for_update(fix_comic_pks, log):
198
206
  update_comics = []
199
207
  now = Now()
200
208
  for comic in outdated_comics:
201
- stat_list = comic.stat # type: ignore
209
+ stat_list = comic.stat
202
210
  if not stat_list:
203
211
  continue
204
212
  stat_list[8] = 0.0
205
- comic.stat = stat_list # type: ignore
206
- comic.updated_at = now # type: ignore
213
+ comic.stat = stat_list # type: ignore[reportAttributeAccessIssue]
214
+ comic.updated_at = now
207
215
  update_comics.append(comic)
208
216
 
209
217
  if update_comics:
@@ -227,8 +235,8 @@ def fix_foreign_keys(log=None):
227
235
  fix_comic_pks |= _delete_bad_m2m_rows(bad_m2m_rows, log)
228
236
  try:
229
237
  _mark_comics_for_update(fix_comic_pks, log)
230
- except Exception as exc:
231
- LOG.warning(f"Could not mark comics with bad relations for update: {exc}")
238
+ except Exception:
239
+ LOG.exception("Could not mark comics with bad relations for update")
232
240
 
233
241
 
234
242
  def _repair_extra_custom_cover_libraries(library_model, log):
@@ -277,7 +285,7 @@ def _is_integrity_ok(results):
277
285
  )
278
286
 
279
287
 
280
- def integrity_check(long=True, log=None):
288
+ def integrity_check(long, log=None):
281
289
  """Run sqlite3 integrity check."""
282
290
  if not log:
283
291
  log = LOG
@@ -318,9 +326,8 @@ def fts_integrity_check(log=None):
318
326
  if results:
319
327
  # I'm not sure if this raises or puts the error in the results.
320
328
  raise ValueError(results) # noqa: TRY301
321
- except Exception as exc:
322
- log.warning("Full Text Search Index failed integrity check.")
323
- log.warning(exc)
329
+ except Exception:
330
+ log.exception("Full Text Search Index failed integrity check.")
324
331
  else:
325
332
  log.info("Full Text Search Index passed integrity check.")
326
333
  success = True
@@ -340,7 +347,7 @@ class IntegrityMixin(WorkerBaseMixin):
340
347
  finally:
341
348
  self.status_controller.finish(status)
342
349
 
343
- def integrity_check(self, long=False):
350
+ def integrity_check(self, long: bool):
344
351
  """Integrity check task."""
345
352
  subtitle = "" if long else "Quick"
346
353
  status = Status(JanitorStatusTypes.INTEGRITY_CHECK, subtitle=subtitle)
@@ -109,7 +109,7 @@ class Janitor(
109
109
  case JanitorVacuumTask():
110
110
  self.vacuum_db()
111
111
  case JanitorBackupTask():
112
- self.backup_db()
112
+ self.backup_db(show_status=True)
113
113
  case JanitorLatestVersionTask():
114
114
  self.update_latest_version(task.force)
115
115
  case JanitorUpdateTask():
@@ -28,7 +28,7 @@ class LatestVersionMixin(WorkerBaseMixin):
28
28
  response = requests.get(_REPO_URL, timeout=_REPO_TIMEOUT)
29
29
  return json.loads(response.text)["info"]["version"]
30
30
 
31
- def update_latest_version(self, force=False):
31
+ def update_latest_version(self, force: bool):
32
32
  """Get the latest version from a remote repo using a cache."""
33
33
  status = Status(JanitorStatusTypes.CODEX_LATEST_VERSION)
34
34
  try:
@@ -27,7 +27,7 @@ class UpdateMixin(WorkerBaseMixin):
27
27
  versio_latest_version = Version(latest_version)
28
28
 
29
29
  installed_versio_version = Version(VERSION)
30
- if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore
30
+ if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore[reportIndexIssue]
31
31
  pre_blurb = "latest version is a prerelease. But installed version is not."
32
32
  else:
33
33
  result = versio_latest_version > installed_versio_version
@@ -35,7 +35,7 @@ class UpdateMixin(WorkerBaseMixin):
35
35
  self.log.debug(f"{latest_version=} > {VERSION=} = {result}{pre_blurb}")
36
36
  return result
37
37
 
38
- def update_codex(self, force=False):
38
+ def update_codex(self, force: bool):
39
39
  """Update the package and restart everything if the version changed."""
40
40
  status = Status(JanitorStatusTypes.CODEX_UPDATE)
41
41
  try:
@@ -30,7 +30,7 @@ class VacuumMixin(WorkerBaseMixin):
30
30
  finally:
31
31
  self.status_controller.finish(status)
32
32
 
33
- def backup_db(self, backup_path=BACKUP_DB_PATH, show_status=True):
33
+ def backup_db(self, show_status: bool, backup_path=BACKUP_DB_PATH):
34
34
  """Backup the database."""
35
35
  status = Status(JanitorStatusTypes.DB_BACKUP) if show_status else ""
36
36
  try:
@@ -79,7 +79,7 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
79
79
  startup_tasks = (
80
80
  AdoptOrphanFoldersTask(),
81
81
  WatchdogSyncTask(),
82
- SearchIndexUpdateTask(False),
82
+ SearchIndexUpdateTask(rebuild=False),
83
83
  )
84
84
 
85
85
  for task in startup_tasks:
@@ -193,7 +193,8 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
193
193
  self.log_queue.join_thread()
194
194
 
195
195
  def run(self):
196
- """Process tasks from the queue.
196
+ """
197
+ Process tasks from the queue.
197
198
 
198
199
  This process also runs the crond thread and the Watchdog Observer
199
200
  threads.
@@ -16,7 +16,8 @@ class NotifierThread(AggregateMessageQueuedThread):
16
16
  self.cache[item.text] = item
17
17
 
18
18
  def _send_task(self, task):
19
- """Send a group_send message to the mulitprocess broadcast channel.
19
+ """
20
+ Send a group_send message to the mulitprocess broadcast channel.
20
21
 
21
22
  A random consumer awaiting the broadcast channel will consume it,
22
23
  and do a group_send with it's message.
@@ -22,7 +22,7 @@ class OptimizeMixin(QueuedThread):
22
22
  self.abort_event = abort_event
23
23
  super().__init__(*args, **kwargs)
24
24
 
25
- def optimize(self, janitor=False):
25
+ def optimize(self, janitor: bool):
26
26
  """Remove records not in the database from the index, trapping exceptions."""
27
27
  start_time = time()
28
28
  status = Status(SearchIndexStatusTypes.SEARCH_INDEX_OPTIMIZE)
@@ -21,7 +21,7 @@ class RemoveMixin(OptimizeMixin):
21
21
  self.status_controller.finish(clear_status)
22
22
  self.log.info("Old search index cleared.")
23
23
 
24
- def _remove_stale_records(self, status): # type: ignore
24
+ def _remove_stale_records(self, status):
25
25
  """Remove records not in the database from the index."""
26
26
  start_time = time()
27
27
  delete_comicfts = ComicFTS.objects.filter(comic__isnull=True)
@@ -14,7 +14,7 @@ from codex.librarian.search.status import SearchIndexStatusTypes
14
14
  from codex.models import Comic, Library
15
15
  from codex.models.comic import ComicFTS
16
16
  from codex.models.functions import GroupConcat
17
- from codex.serializers.fields import CountryField, LanguageField, PyCountryField
17
+ from codex.serializers.fields.browser import CountryField, LanguageField, PyCountryField
18
18
  from codex.settings.settings import SEARCH_INDEX_BATCH_SIZE
19
19
  from codex.status import Status
20
20
 
@@ -159,7 +159,7 @@ class FTSUpdateMixin(RemoveMixin):
159
159
  obj_list.append(comicfts)
160
160
  self.log.debug(f"{len(obj_list)}/{comics.count()} entries prepped.")
161
161
 
162
- def _get_comicfts_list(self, comics, create=False):
162
+ def _get_comicfts_list(self, comics, create: bool):
163
163
  """Create a ComicFTS object for bulk_create or bulk_update."""
164
164
  country_field = CountryField()
165
165
  language_field = LanguageField()
@@ -178,7 +178,7 @@ class FTSUpdateMixin(RemoveMixin):
178
178
  self.log.debug(f"{verb} no search entries.")
179
179
  self.status_controller.finish(status)
180
180
 
181
- def _update_search_index_operate(self, comics_qs, create=False):
181
+ def _update_search_index_operate(self, comics_qs, create: bool):
182
182
  count = comics_qs.count()
183
183
  verb = "create" if create else "update"
184
184
  if not count:
@@ -285,7 +285,7 @@ class FTSUpdateMixin(RemoveMixin):
285
285
  elapsed = naturaldelta(elapsed_time)
286
286
  self.log.info(f"Search index updated in {elapsed}.")
287
287
 
288
- def update_search_index(self, rebuild=False):
288
+ def update_search_index(self, rebuild: bool):
289
289
  """Update or Rebuild the search index."""
290
290
  start_time = time()
291
291
  self.abort_event.clear()
@@ -62,11 +62,12 @@ class CodexStats:
62
62
  request_model_set = self.params.get(key, {})
63
63
  all_models = _KEY_MODELS_MAP[key]
64
64
  if request_model_set:
65
- models = []
66
- for model_name in request_model_set:
67
- for model in all_models:
68
- if model.__name__.lower() == model_name.lower():
69
- models.append(model)
65
+ models = [
66
+ model
67
+ for model in all_models
68
+ for model_name in request_model_set
69
+ if model.__name__.lower() == model_name.lower()
70
+ ]
70
71
  else:
71
72
  models = all_models
72
73
  return tuple(models)
@@ -149,7 +150,7 @@ class CodexStats:
149
150
 
150
151
  def _add_file_types(self, obj):
151
152
  """Query for file types."""
152
- if self.params and "fileTypes" not in self.params:
153
+ if self.params and "file_types" not in self.params:
153
154
  return
154
155
  file_types = {}
155
156
  qs = (
@@ -68,7 +68,6 @@ def _send_telemetry(log):
68
68
  stats = CodexStats().get()
69
69
  data = {"stats": stats, "uuid": ts.version}
70
70
  _post_stats(log, data)
71
- # log.debug("Sent anonymous stats")
72
71
  ts.save() # update updated_at
73
72
 
74
73
 
@@ -61,12 +61,12 @@ class CodexDatabaseSnapshot(DirectorySnapshot, LoggerBaseMixin):
61
61
  def __init__(
62
62
  self,
63
63
  path,
64
- _recursive=True, # unused, always recursive
64
+ _recursive=True, # noqa: FBT002 unused, always recursive
65
65
  stat=os.stat,
66
66
  _listdir=os.listdir, # unused for database
67
- force=False,
67
+ force=False, # noqa: FBT002
68
68
  log_queue=None,
69
- covers_only=False,
69
+ covers_only=False, # noqa: FBT002
70
70
  ):
71
71
  """Initialize like DirectorySnapshot but use a database walk."""
72
72
  self._covers_only = covers_only
@@ -33,7 +33,8 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
33
33
  return self._get_inode(data.ref, path) == self._get_inode(data.snapshot, path)
34
34
 
35
35
  def _is_stats_equal(self, data, old_path, new_path):
36
- """Return if the mtime and size are equal.
36
+ """
37
+ Return if the mtime and size are equal.
37
38
 
38
39
  For old paths in the ref and new paths in the snapshot.
39
40
  """
@@ -84,9 +85,7 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
84
85
  if not self._is_stats_equal(data, old_path, new_path):
85
86
  data.modified.add(new_path)
86
87
 
87
- def __init__( # C901, PLR0912
88
- self, ref, snapshot, ignore_device=False, inode_only_modified=False
89
- ):
88
+ def __init__(self, ref, snapshot, ignore_device: bool, inode_only_modified: bool):
90
89
  """Create diff object."""
91
90
  self._ignore_device = ignore_device
92
91
  self._inode_only_modified = inode_only_modified