codex 1.6.0__py3-none-any.whl → 1.6.0a0__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 (769) hide show
  1. codex/exceptions.py +18 -22
  2. codex/integrity.py +3 -66
  3. codex/librarian/covers/coverd.py +2 -2
  4. codex/librarian/covers/create.py +29 -50
  5. codex/librarian/covers/path.py +6 -8
  6. codex/librarian/covers/purge.py +18 -28
  7. codex/librarian/covers/tasks.py +2 -3
  8. codex/librarian/importer/{aggregate.py → aggregate_metadata.py} +88 -58
  9. codex/librarian/importer/{extract.py → clean_metadata.py} +4 -33
  10. codex/librarian/importer/const.py +13 -46
  11. codex/librarian/importer/create_comics.py +17 -101
  12. codex/librarian/importer/create_fks.py +134 -99
  13. codex/librarian/importer/deleted.py +28 -103
  14. codex/librarian/importer/failed_imports.py +64 -42
  15. codex/librarian/importer/importerd.py +290 -71
  16. codex/librarian/importer/link_comics.py +26 -26
  17. codex/librarian/importer/moved.py +81 -128
  18. codex/librarian/importer/query_fks.py +122 -142
  19. codex/librarian/importer/status.py +39 -8
  20. codex/librarian/importer/tasks.py +9 -26
  21. codex/librarian/importer/update_comics.py +89 -0
  22. codex/librarian/janitor/cleanup.py +2 -26
  23. codex/librarian/janitor/failed_imports.py +1 -1
  24. codex/librarian/janitor/janitor.py +1 -13
  25. codex/librarian/janitor/janitord.py +1 -1
  26. codex/librarian/janitor/status.py +0 -1
  27. codex/librarian/janitor/tasks.py +0 -5
  28. codex/librarian/librariand.py +5 -9
  29. codex/librarian/notifier/notifierd.py +1 -1
  30. codex/librarian/notifier/tasks.py +4 -4
  31. codex/librarian/search/update.py +1 -1
  32. codex/librarian/watchdog/db_snapshot.py +6 -9
  33. codex/librarian/watchdog/emitter.py +0 -3
  34. codex/librarian/watchdog/event_batcherd.py +3 -19
  35. codex/librarian/watchdog/events.py +36 -176
  36. codex/librarian/watchdog/observers.py +3 -13
  37. codex/middleware.py +2 -1
  38. codex/migrations/0027_sort_name.py +149 -0
  39. codex/models/admin.py +2 -0
  40. codex/models/base.py +0 -3
  41. codex/models/comic.py +9 -13
  42. codex/models/{util.py → const.py} +2 -15
  43. codex/models/groups.py +14 -28
  44. codex/models/library.py +3 -11
  45. codex/models/paths.py +14 -61
  46. codex/permissions.py +1 -1
  47. codex/search/backend_search.py +8 -5
  48. codex/search/query.py +2 -25
  49. codex/search/writing.py +1 -1
  50. codex/serializers/admin.py +44 -53
  51. codex/serializers/browser/choices.py +42 -37
  52. codex/serializers/browser/filters.py +131 -32
  53. codex/serializers/browser/metadata.py +1 -45
  54. codex/serializers/browser/mixins.py +4 -37
  55. codex/serializers/browser/page.py +6 -3
  56. codex/serializers/browser/settings.py +11 -42
  57. codex/serializers/choices.py +1 -14
  58. codex/serializers/models/pycountry.py +51 -11
  59. codex/serializers/opds/v2.py +16 -10
  60. codex/serializers/opds/v2_unused.py +3 -5
  61. codex/serializers/reader.py +7 -21
  62. codex/serializers/redirect.py +2 -3
  63. codex/serializers/route.py +7 -16
  64. codex/settings/settings.py +8 -23
  65. codex/startup.py +3 -77
  66. codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js +1 -0
  67. codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.br +0 -0
  68. codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.gz +0 -0
  69. codex/static_root/assets/VCheckbox-Aq_ImMNC.js +1 -0
  70. codex/static_root/assets/VCheckbox-Aq_ImMNC.js.br +0 -0
  71. codex/static_root/assets/VCheckbox-Aq_ImMNC.js.gz +0 -0
  72. codex/static_root/assets/{VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js → VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js} +1 -1
  73. codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.br +0 -0
  74. codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.gz +0 -0
  75. codex/static_root/assets/{VCheckboxBtn-DxNOKONG.js → VCheckboxBtn-D3izCBa7.js} +1 -1
  76. codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.br +0 -0
  77. codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.gz +0 -0
  78. codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js +1 -0
  79. codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.br +0 -0
  80. codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.gz +0 -0
  81. codex/static_root/assets/VCombobox-DLL1EsSN.js +1 -0
  82. codex/static_root/assets/VCombobox-DLL1EsSN.js.br +0 -0
  83. codex/static_root/assets/VCombobox-DLL1EsSN.js.gz +0 -0
  84. codex/static_root/assets/{VDialog-lQ13FnRR.1c801ad756b2.css → VDialog-C5Nxd_zP.css} +1 -1
  85. codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
  86. codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
  87. codex/static_root/assets/{VDialog-lQ13FnRR.css → VDialog-C5Nxd_zP.d327f505ef0e.css} +1 -1
  88. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
  89. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
  90. codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js +1 -0
  91. codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.br +0 -0
  92. codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.gz +0 -0
  93. codex/static_root/assets/VDialog-yDKzaGEj.js +1 -0
  94. codex/static_root/assets/VDialog-yDKzaGEj.js.br +0 -0
  95. codex/static_root/assets/VDialog-yDKzaGEj.js.gz +0 -0
  96. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js +1 -0
  97. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.br +0 -0
  98. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.gz +0 -0
  99. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js +1 -0
  100. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.br +0 -0
  101. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.gz +0 -0
  102. codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js +1 -0
  103. codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.br +0 -0
  104. codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.gz +0 -0
  105. codex/static_root/assets/VRadioGroup-DEezv8bL.js +1 -0
  106. codex/static_root/assets/VRadioGroup-DEezv8bL.js.br +0 -0
  107. codex/static_root/assets/VRadioGroup-DEezv8bL.js.gz +0 -0
  108. codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js +1 -0
  109. codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.br +0 -0
  110. codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.gz +0 -0
  111. codex/static_root/assets/VSelect-BKXcujPy.js +1 -0
  112. codex/static_root/assets/VSelect-BKXcujPy.js.br +0 -0
  113. codex/static_root/assets/VSelect-BKXcujPy.js.gz +0 -0
  114. codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js +1 -0
  115. codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.br +0 -0
  116. codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.gz +0 -0
  117. codex/static_root/assets/VSelectionControl-DJAPiluk.js +1 -0
  118. codex/static_root/assets/VSelectionControl-DJAPiluk.js.br +0 -0
  119. codex/static_root/assets/VSelectionControl-DJAPiluk.js.gz +0 -0
  120. codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js +1 -0
  121. codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.br +0 -0
  122. codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.gz +0 -0
  123. codex/static_root/assets/VSlideGroup-C-kO93Va.js +1 -0
  124. codex/static_root/assets/VSlideGroup-C-kO93Va.js.br +0 -0
  125. codex/static_root/assets/VSlideGroup-C-kO93Va.js.gz +0 -0
  126. codex/static_root/assets/{VTable-2RHFqfg6.f0f2106b158b.js → VTable-9vsIQObs.0a135029e924.js} +1 -1
  127. codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.br +0 -0
  128. codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.gz +0 -0
  129. codex/static_root/assets/{VTable-2RHFqfg6.js → VTable-9vsIQObs.js} +1 -1
  130. codex/static_root/assets/VTable-9vsIQObs.js.br +0 -0
  131. codex/static_root/assets/VTable-9vsIQObs.js.gz +0 -0
  132. codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js +1 -0
  133. codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.br +0 -0
  134. codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.gz +0 -0
  135. codex/static_root/assets/VTextField-DI9HAmWh.js +1 -0
  136. codex/static_root/assets/VTextField-DI9HAmWh.js.br +0 -0
  137. codex/static_root/assets/VTextField-DI9HAmWh.js.gz +0 -0
  138. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css +1 -0
  139. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
  140. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
  141. codex/static_root/assets/VTextField-DuIIp-VY.css +1 -0
  142. codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
  143. codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
  144. codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js +1 -0
  145. codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.br +0 -0
  146. codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.gz +0 -0
  147. codex/static_root/assets/VWindowItem-DpnYkF3y.js +1 -0
  148. codex/static_root/assets/VWindowItem-DpnYkF3y.js.br +0 -0
  149. codex/static_root/assets/VWindowItem-DpnYkF3y.js.gz +0 -0
  150. codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js +1 -0
  151. codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.br +0 -0
  152. codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.gz +0 -0
  153. codex/static_root/assets/admin-2BTkfSQA.js +1 -0
  154. codex/static_root/assets/admin-2BTkfSQA.js.br +0 -0
  155. codex/static_root/assets/admin-2BTkfSQA.js.gz +0 -0
  156. codex/static_root/assets/{admin-B8Bp2Uih.82c98714bcf1.css → admin-BbrOU9Y9.29936748af9e.css} +1 -1
  157. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
  158. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
  159. codex/static_root/assets/{admin-B8Bp2Uih.css → admin-BbrOU9Y9.css} +1 -1
  160. codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
  161. codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
  162. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +1 -0
  163. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
  164. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
  165. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +1 -0
  166. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
  167. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
  168. codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js +30 -0
  169. codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.br +0 -0
  170. codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.gz +0 -0
  171. codex/static_root/assets/admin-drawer-panel-mtdde7vY.js +30 -0
  172. codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.br +0 -0
  173. codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.gz +0 -0
  174. codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js +1 -0
  175. codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.br +0 -0
  176. codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.gz +0 -0
  177. codex/static_root/assets/browser-BA-AwUn-.js +1 -0
  178. codex/static_root/assets/browser-BA-AwUn-.js.br +0 -0
  179. codex/static_root/assets/browser-BA-AwUn-.js.gz +0 -0
  180. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css +1 -0
  181. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.br +0 -0
  182. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.gz +0 -0
  183. codex/static_root/assets/browser-CZeVqRNy.css +1 -0
  184. codex/static_root/assets/browser-CZeVqRNy.css.br +0 -0
  185. codex/static_root/assets/browser-CZeVqRNy.css.gz +0 -0
  186. codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js +1 -0
  187. codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.br +0 -0
  188. codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.gz +0 -0
  189. codex/static_root/assets/change-password-dialog-C8YONrId.js +1 -0
  190. codex/static_root/assets/change-password-dialog-C8YONrId.js.br +0 -0
  191. codex/static_root/assets/change-password-dialog-C8YONrId.js.gz +0 -0
  192. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +1 -0
  193. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
  194. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
  195. codex/static_root/assets/change-password-dialog-HvIvmmIE.css +1 -0
  196. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
  197. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
  198. codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js +1 -0
  199. codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.br +0 -0
  200. codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.gz +0 -0
  201. codex/static_root/assets/confirm-dialog-Clg-o2G-.js +1 -0
  202. codex/static_root/assets/confirm-dialog-Clg-o2G-.js.br +0 -0
  203. codex/static_root/assets/confirm-dialog-Clg-o2G-.js.gz +0 -0
  204. codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js +1 -0
  205. codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.br +0 -0
  206. codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.gz +0 -0
  207. codex/static_root/assets/datetime-column-DejrPZxl.js +1 -0
  208. codex/static_root/assets/datetime-column-DejrPZxl.js.br +0 -0
  209. codex/static_root/assets/datetime-column-DejrPZxl.js.gz +0 -0
  210. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +1 -0
  211. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
  212. codex/static_root/assets/datetime-column-Dg_RxTVM.css +1 -0
  213. codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
  214. codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js +1 -0
  215. codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.br +0 -0
  216. codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.gz +0 -0
  217. codex/static_root/assets/filter-DHo57nTp.js +1 -0
  218. codex/static_root/assets/filter-DHo57nTp.js.br +0 -0
  219. codex/static_root/assets/filter-DHo57nTp.js.gz +0 -0
  220. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +1 -0
  221. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
  222. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
  223. codex/static_root/assets/flag-tab-BacZLlVi.css +1 -0
  224. codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
  225. codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
  226. codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js +1 -0
  227. codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.br +0 -0
  228. codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.gz +0 -0
  229. codex/static_root/assets/flag-tab-CagMLGSE.js +1 -0
  230. codex/static_root/assets/flag-tab-CagMLGSE.js.br +0 -0
  231. codex/static_root/assets/flag-tab-CagMLGSE.js.gz +0 -0
  232. codex/static_root/assets/{group-tab-D5vWrihT.js → group-tab-BTAjhv32.ac196780d0a9.js} +1 -1
  233. codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.br +0 -0
  234. codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.gz +0 -0
  235. codex/static_root/assets/{group-tab-D5vWrihT.1afb2ce453ff.js → group-tab-BTAjhv32.js} +1 -1
  236. codex/static_root/assets/group-tab-BTAjhv32.js.br +0 -0
  237. codex/static_root/assets/group-tab-BTAjhv32.js.gz +0 -0
  238. codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js +1 -0
  239. codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.br +0 -0
  240. codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.gz +0 -0
  241. codex/static_root/assets/http-error-DaHEsKbT.js +1 -0
  242. codex/static_root/assets/http-error-DaHEsKbT.js.br +0 -0
  243. codex/static_root/assets/http-error-DaHEsKbT.js.gz +0 -0
  244. codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css +1 -0
  245. codex/static_root/assets/{http-error-DKulXuTl.95e42988d5d5.css.br → http-error-_wlDqEcp.5b78d3e332c9.css.br} +0 -0
  246. codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css.gz +0 -0
  247. codex/static_root/assets/http-error-_wlDqEcp.css +1 -0
  248. codex/static_root/assets/{http-error-DKulXuTl.css.br → http-error-_wlDqEcp.css.br} +0 -0
  249. codex/static_root/assets/http-error-_wlDqEcp.css.gz +0 -0
  250. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +1 -0
  251. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
  252. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
  253. codex/static_root/assets/index-DUjeNg4x.js +1 -0
  254. codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
  255. codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
  256. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +1 -0
  257. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +2 -0
  258. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
  259. codex/static_root/assets/library-tab-CXbkxNZt.css +1 -0
  260. codex/static_root/assets/library-tab-CXbkxNZt.css.br +2 -0
  261. codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
  262. codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js +1 -0
  263. codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.br +0 -0
  264. codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.gz +0 -0
  265. codex/static_root/assets/library-tab-Cud4639b.js +1 -0
  266. codex/static_root/assets/library-tab-Cud4639b.js.br +0 -0
  267. codex/static_root/assets/library-tab-Cud4639b.js.gz +0 -0
  268. codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js +32 -0
  269. codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.br +0 -0
  270. codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.gz +0 -0
  271. codex/static_root/assets/main-BnzL_Byv.js +32 -0
  272. codex/static_root/assets/main-BnzL_Byv.js.br +0 -0
  273. codex/static_root/assets/main-BnzL_Byv.js.gz +0 -0
  274. codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css → main-sK7HC5h4.6f04ddfc5f1a.css} +1 -1
  275. codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
  276. codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css.gz → main-sK7HC5h4.6f04ddfc5f1a.css.gz} +0 -0
  277. codex/static_root/assets/{main-Ztp855PW.css → main-sK7HC5h4.css} +1 -1
  278. codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
  279. codex/static_root/assets/{main-Ztp855PW.css.gz → main-sK7HC5h4.css.gz} +0 -0
  280. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css +1 -0
  281. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
  282. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
  283. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css +1 -0
  284. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
  285. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
  286. codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js +1 -0
  287. codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.br +0 -0
  288. codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.gz +0 -0
  289. codex/static_root/assets/pagination-toolbar-Covpd-k8.js +1 -0
  290. codex/static_root/assets/pagination-toolbar-Covpd-k8.js.br +0 -0
  291. codex/static_root/assets/pagination-toolbar-Covpd-k8.js.gz +0 -0
  292. codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js +35 -0
  293. codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.br +0 -0
  294. codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.gz +0 -0
  295. codex/static_root/assets/pdf-doc-CD2lnB32.js +35 -0
  296. codex/static_root/assets/pdf-doc-CD2lnB32.js.br +0 -0
  297. codex/static_root/assets/pdf-doc-CD2lnB32.js.gz +0 -0
  298. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css +1 -0
  299. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.br +0 -0
  300. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.gz +0 -0
  301. codex/static_root/assets/reader-Dspy46e8.css +1 -0
  302. codex/static_root/assets/reader-Dspy46e8.css.br +0 -0
  303. codex/static_root/assets/reader-Dspy46e8.css.gz +0 -0
  304. codex/static_root/assets/reader-L85y7IXD.6de58f450421.js +2 -0
  305. codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.br +0 -0
  306. codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.gz +0 -0
  307. codex/static_root/assets/reader-L85y7IXD.js +2 -0
  308. codex/static_root/assets/reader-L85y7IXD.js.br +0 -0
  309. codex/static_root/assets/reader-L85y7IXD.js.gz +0 -0
  310. codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js +1 -0
  311. codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.br +0 -0
  312. codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.gz +0 -0
  313. codex/static_root/assets/relation-chips-BpuqEeqM.js +1 -0
  314. codex/static_root/assets/relation-chips-BpuqEeqM.js.br +0 -0
  315. codex/static_root/assets/relation-chips-BpuqEeqM.js.gz +0 -0
  316. codex/static_root/assets/{relation-chips-Bsc-gUxE.98e65384c3c9.css → relation-chips-D6T6FX7G.css} +1 -1
  317. codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
  318. codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
  319. codex/static_root/assets/{relation-chips-Bsc-gUxE.css → relation-chips-D6T6FX7G.db095b2497e4.css} +1 -1
  320. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
  321. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
  322. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css +1 -0
  323. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.br +0 -0
  324. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.gz +0 -0
  325. codex/static_root/assets/settings-drawer-BgUazs7O.css +1 -0
  326. codex/static_root/assets/settings-drawer-BgUazs7O.css.br +0 -0
  327. codex/static_root/assets/settings-drawer-BgUazs7O.css.gz +0 -0
  328. codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js +2 -0
  329. codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.br +0 -0
  330. codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.gz +0 -0
  331. codex/static_root/assets/settings-drawer-CLMcxfVd.js +2 -0
  332. codex/static_root/assets/settings-drawer-CLMcxfVd.js.br +0 -0
  333. codex/static_root/assets/settings-drawer-CLMcxfVd.js.gz +0 -0
  334. codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js +1 -0
  335. codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.br +0 -0
  336. codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.gz +0 -0
  337. codex/static_root/assets/stats-tab-CQa3-W05.js +1 -0
  338. codex/static_root/assets/stats-tab-CQa3-W05.js.br +0 -0
  339. codex/static_root/assets/stats-tab-CQa3-W05.js.gz +0 -0
  340. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +1 -0
  341. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +2 -0
  342. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
  343. codex/static_root/assets/stats-tab-TsyG3wH_.css +1 -0
  344. codex/static_root/assets/stats-tab-TsyG3wH_.css.br +2 -0
  345. codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
  346. codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js +1 -0
  347. codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.br +0 -0
  348. codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.gz +0 -0
  349. codex/static_root/assets/task-tab-OPhBm-O2.js +1 -0
  350. codex/static_root/assets/task-tab-OPhBm-O2.js.br +0 -0
  351. codex/static_root/assets/task-tab-OPhBm-O2.js.gz +0 -0
  352. codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +1 -0
  353. codex/static_root/assets/to-case-kUm8NPpW.js +1 -0
  354. codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js +1 -0
  355. codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.br +0 -0
  356. codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.gz +0 -0
  357. codex/static_root/assets/unauthorized-ClQNhRb5.js +1 -0
  358. codex/static_root/assets/unauthorized-ClQNhRb5.js.br +0 -0
  359. codex/static_root/assets/unauthorized-ClQNhRb5.js.gz +0 -0
  360. codex/static_root/assets/{unauthorized-CibJYD8o.4c2de98c2b53.css → unauthorized-DWgytUow.ca2f13dbdbf5.css} +1 -1
  361. codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.br +0 -0
  362. codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.gz +0 -0
  363. codex/static_root/assets/{unauthorized-CibJYD8o.css → unauthorized-DWgytUow.css} +1 -1
  364. codex/static_root/assets/unauthorized-DWgytUow.css.br +0 -0
  365. codex/static_root/assets/unauthorized-DWgytUow.css.gz +0 -0
  366. codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js +1 -0
  367. codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.br +0 -0
  368. codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.gz +0 -0
  369. codex/static_root/assets/user-tab-BKkxa2WL.js +1 -0
  370. codex/static_root/assets/user-tab-BKkxa2WL.js.br +0 -0
  371. codex/static_root/assets/user-tab-BKkxa2WL.js.gz +0 -0
  372. codex/static_root/js/choices-admin.248384ece3f6.json +1 -0
  373. codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
  374. codex/static_root/js/choices-admin.248384ece3f6.json.gz +0 -0
  375. codex/static_root/js/choices-admin.json +1 -1
  376. codex/static_root/js/choices-admin.json.br +0 -0
  377. codex/static_root/js/choices-admin.json.gz +0 -0
  378. codex/static_root/js/choices.b60817ba0a93.json +1 -0
  379. codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
  380. codex/static_root/js/choices.b60817ba0a93.json.gz +0 -0
  381. codex/static_root/js/choices.json +1 -1
  382. codex/static_root/js/choices.json.br +0 -0
  383. codex/static_root/js/choices.json.gz +0 -0
  384. codex/static_root/manifest.c5fa10a7dfdc.json +641 -0
  385. codex/static_root/manifest.c5fa10a7dfdc.json.br +0 -0
  386. codex/static_root/manifest.c5fa10a7dfdc.json.gz +0 -0
  387. codex/static_root/manifest.json +298 -279
  388. codex/static_root/manifest.json.br +0 -0
  389. codex/static_root/manifest.json.gz +0 -0
  390. codex/static_root/staticfiles.json +1 -1
  391. codex/status_controller.py +2 -11
  392. codex/urls/api/auth.py +1 -2
  393. codex/urls/api/browser.py +4 -13
  394. codex/urls/api/reader.py +9 -3
  395. codex/urls/api/v3.py +0 -2
  396. codex/urls/opds/binary.py +1 -1
  397. codex/views/admin/api_key.py +8 -7
  398. codex/views/admin/flag.py +7 -2
  399. codex/views/admin/group.py +6 -2
  400. codex/views/admin/library.py +37 -44
  401. codex/views/admin/stats.py +83 -20
  402. codex/views/admin/tasks.py +12 -26
  403. codex/views/admin/user.py +9 -3
  404. codex/views/auth.py +54 -31
  405. codex/views/bookmark.py +72 -92
  406. codex/views/browser/base.py +90 -42
  407. codex/views/browser/browser.py +371 -92
  408. codex/views/browser/{annotations.py → browser_annotations.py} +121 -67
  409. codex/views/browser/browser_breadcrumbs.py +175 -0
  410. codex/views/browser/browser_order_by.py +63 -0
  411. codex/views/browser/choices.py +85 -38
  412. codex/views/browser/const.py +3 -0
  413. codex/views/browser/filters/annotations.py +19 -87
  414. codex/views/browser/filters/bookmark.py +12 -24
  415. codex/views/browser/filters/field.py +6 -8
  416. codex/views/browser/filters/group.py +14 -35
  417. codex/views/browser/filters/search.py +20 -31
  418. codex/views/browser/metadata.py +73 -112
  419. codex/views/browser/session.py +12 -0
  420. codex/views/const.py +0 -140
  421. codex/views/cover.py +77 -0
  422. codex/views/download.py +8 -5
  423. codex/views/frontend.py +2 -4
  424. codex/views/opds/authentication_v1.py +45 -46
  425. codex/views/opds/binary.py +10 -5
  426. codex/views/opds/const.py +0 -13
  427. codex/views/opds/urls.py +4 -6
  428. codex/views/opds/util.py +12 -4
  429. codex/views/opds/v1/entry/links.py +33 -18
  430. codex/views/opds/v1/facets.py +7 -7
  431. codex/views/opds/v1/feed.py +45 -55
  432. codex/views/opds/v1/links.py +5 -7
  433. codex/views/opds/v1/opensearch_v1.py +6 -2
  434. codex/views/opds/v2/feed.py +26 -25
  435. codex/views/opds/v2/links.py +2 -4
  436. codex/views/opds/v2/publications.py +54 -34
  437. codex/views/opds/v2/top_links.py +2 -2
  438. codex/views/reader/page.py +8 -15
  439. codex/views/reader/reader.py +322 -19
  440. codex/views/reader/{settings.py → session.py} +2 -4
  441. codex/views/session.py +102 -83
  442. codex/views/template.py +3 -3
  443. codex/views/version.py +2 -2
  444. codex/websockets/consumers.py +10 -14
  445. {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/METADATA +15 -22
  446. {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/RECORD +449 -467
  447. codex/librarian/importer/cache.py +0 -93
  448. codex/librarian/importer/create_covers.py +0 -96
  449. codex/librarian/importer/importer.py +0 -96
  450. codex/librarian/importer/init.py +0 -265
  451. codex/librarian/importer/link_covers.py +0 -73
  452. codex/librarian/importer/query_covers.py +0 -52
  453. codex/migrations/0027_import_order_and_covers.py +0 -280
  454. codex/serializers/browser/mtime.py +0 -21
  455. codex/serializers/fields.py +0 -149
  456. codex/serializers/settings.py +0 -12
  457. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js +0 -1
  458. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.br +0 -0
  459. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.gz +0 -0
  460. codex/static_root/assets/VCheckbox-DanrzdHo.js +0 -1
  461. codex/static_root/assets/VCheckbox-DanrzdHo.js.br +0 -0
  462. codex/static_root/assets/VCheckbox-DanrzdHo.js.gz +0 -0
  463. codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.br +0 -0
  464. codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.gz +0 -0
  465. codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.br +0 -0
  466. codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.gz +0 -0
  467. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js +0 -1
  468. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.br +0 -0
  469. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.gz +0 -0
  470. codex/static_root/assets/VCombobox-DDvdoPTG.js +0 -1
  471. codex/static_root/assets/VCombobox-DDvdoPTG.js.br +0 -0
  472. codex/static_root/assets/VCombobox-DDvdoPTG.js.gz +0 -0
  473. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js +0 -1
  474. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.br +0 -0
  475. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.gz +0 -0
  476. codex/static_root/assets/VDialog-Bd5dN_E1.js +0 -1
  477. codex/static_root/assets/VDialog-Bd5dN_E1.js.br +0 -0
  478. codex/static_root/assets/VDialog-Bd5dN_E1.js.gz +0 -0
  479. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
  480. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
  481. codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
  482. codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
  483. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js +0 -1
  484. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.br +0 -0
  485. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.gz +0 -0
  486. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js +0 -1
  487. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.br +0 -0
  488. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.gz +0 -0
  489. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js +0 -1
  490. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.br +0 -0
  491. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.gz +0 -0
  492. codex/static_root/assets/VRadioGroup-DKb8XPr-.js +0 -1
  493. codex/static_root/assets/VRadioGroup-DKb8XPr-.js.br +0 -0
  494. codex/static_root/assets/VRadioGroup-DKb8XPr-.js.gz +0 -0
  495. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js +0 -1
  496. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.br +0 -0
  497. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.gz +0 -0
  498. codex/static_root/assets/VSelect-C_yPbyCG.js +0 -1
  499. codex/static_root/assets/VSelect-C_yPbyCG.js.br +0 -0
  500. codex/static_root/assets/VSelect-C_yPbyCG.js.gz +0 -0
  501. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js +0 -1
  502. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.br +0 -0
  503. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.gz +0 -0
  504. codex/static_root/assets/VSelectionControl-CmFPvGMS.js +0 -1
  505. codex/static_root/assets/VSelectionControl-CmFPvGMS.js.br +0 -0
  506. codex/static_root/assets/VSelectionControl-CmFPvGMS.js.gz +0 -0
  507. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js +0 -1
  508. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.br +0 -0
  509. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.gz +0 -0
  510. codex/static_root/assets/VSlideGroup-D5FZWsM1.js +0 -1
  511. codex/static_root/assets/VSlideGroup-D5FZWsM1.js.br +0 -0
  512. codex/static_root/assets/VSlideGroup-D5FZWsM1.js.gz +0 -0
  513. codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.br +0 -0
  514. codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.gz +0 -0
  515. codex/static_root/assets/VTable-2RHFqfg6.js.br +0 -0
  516. codex/static_root/assets/VTable-2RHFqfg6.js.gz +0 -0
  517. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +0 -1
  518. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
  519. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
  520. codex/static_root/assets/VTextField-BssVoDhB.css +0 -1
  521. codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
  522. codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
  523. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js +0 -1
  524. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.br +0 -0
  525. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.gz +0 -0
  526. codex/static_root/assets/VTextField-o1N9ylIb.js +0 -1
  527. codex/static_root/assets/VTextField-o1N9ylIb.js.br +0 -0
  528. codex/static_root/assets/VTextField-o1N9ylIb.js.gz +0 -0
  529. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js +0 -1
  530. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.br +0 -0
  531. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.gz +0 -0
  532. codex/static_root/assets/VWindowItem-C_zvuz9C.js +0 -1
  533. codex/static_root/assets/VWindowItem-C_zvuz9C.js.br +0 -0
  534. codex/static_root/assets/VWindowItem-C_zvuz9C.js.gz +0 -0
  535. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
  536. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
  537. codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
  538. codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
  539. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js +0 -1
  540. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.br +0 -0
  541. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.gz +0 -0
  542. codex/static_root/assets/admin-DHyHEWZ3.js +0 -1
  543. codex/static_root/assets/admin-DHyHEWZ3.js.br +0 -0
  544. codex/static_root/assets/admin-DHyHEWZ3.js.gz +0 -0
  545. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js +0 -30
  546. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.br +0 -0
  547. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.gz +0 -0
  548. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js +0 -30
  549. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.br +0 -0
  550. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.gz +0 -0
  551. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css +0 -1
  552. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
  553. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
  554. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css +0 -1
  555. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
  556. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
  557. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js +0 -1
  558. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.br +0 -0
  559. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.gz +0 -0
  560. codex/static_root/assets/browser-D0Vjmmp_.js +0 -1
  561. codex/static_root/assets/browser-D0Vjmmp_.js.br +0 -0
  562. codex/static_root/assets/browser-D0Vjmmp_.js.gz +0 -0
  563. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css +0 -1
  564. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.br +0 -0
  565. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.gz +0 -0
  566. codex/static_root/assets/browser-D7QQDneO.css +0 -1
  567. codex/static_root/assets/browser-D7QQDneO.css.br +0 -0
  568. codex/static_root/assets/browser-D7QQDneO.css.gz +0 -0
  569. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +0 -1
  570. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
  571. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
  572. codex/static_root/assets/change-password-dialog-0u6IJA87.css +0 -1
  573. codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
  574. codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
  575. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js +0 -1
  576. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.br +0 -0
  577. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.gz +0 -0
  578. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js +0 -1
  579. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.br +0 -0
  580. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.gz +0 -0
  581. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js +0 -1
  582. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.br +0 -0
  583. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.gz +0 -0
  584. codex/static_root/assets/confirm-dialog-DCKvg3ai.js +0 -1
  585. codex/static_root/assets/confirm-dialog-DCKvg3ai.js.br +0 -0
  586. codex/static_root/assets/confirm-dialog-DCKvg3ai.js.gz +0 -0
  587. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +0 -1
  588. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
  589. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +0 -1
  590. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
  591. codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +0 -1
  592. codex/static_root/assets/datetime-column-DeCthByU.css +0 -1
  593. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js +0 -1
  594. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.br +0 -0
  595. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.gz +0 -0
  596. codex/static_root/assets/datetime-column-jHKA_uNm.js +0 -1
  597. codex/static_root/assets/datetime-column-jHKA_uNm.js.br +0 -0
  598. codex/static_root/assets/datetime-column-jHKA_uNm.js.gz +0 -0
  599. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js +0 -1
  600. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.br +0 -0
  601. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.gz +0 -0
  602. codex/static_root/assets/filter-DgPg2ts9.js +0 -1
  603. codex/static_root/assets/filter-DgPg2ts9.js.br +0 -0
  604. codex/static_root/assets/filter-DgPg2ts9.js.gz +0 -0
  605. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js +0 -1
  606. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.br +0 -0
  607. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.gz +0 -0
  608. codex/static_root/assets/flag-tab-9qxOALxP.js +0 -1
  609. codex/static_root/assets/flag-tab-9qxOALxP.js.br +0 -0
  610. codex/static_root/assets/flag-tab-9qxOALxP.js.gz +0 -0
  611. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +0 -1
  612. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
  613. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
  614. codex/static_root/assets/flag-tab-BNwbLfrN.css +0 -1
  615. codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
  616. codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
  617. codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.br +0 -0
  618. codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.gz +0 -0
  619. codex/static_root/assets/group-tab-D5vWrihT.js.br +0 -0
  620. codex/static_root/assets/group-tab-D5vWrihT.js.gz +0 -0
  621. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js +0 -1
  622. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.br +0 -0
  623. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.gz +0 -0
  624. codex/static_root/assets/http-error-B4FHoKfs.js +0 -1
  625. codex/static_root/assets/http-error-B4FHoKfs.js.br +0 -0
  626. codex/static_root/assets/http-error-B4FHoKfs.js.gz +0 -0
  627. codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css +0 -1
  628. codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css.gz +0 -0
  629. codex/static_root/assets/http-error-DKulXuTl.css +0 -1
  630. codex/static_root/assets/http-error-DKulXuTl.css.gz +0 -0
  631. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +0 -1
  632. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
  633. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
  634. codex/static_root/assets/library-tab-BN0SfPcH.css +0 -1
  635. codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
  636. codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
  637. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js +0 -1
  638. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.br +0 -0
  639. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.gz +0 -0
  640. codex/static_root/assets/library-tab-BWo0cmzk.js +0 -1
  641. codex/static_root/assets/library-tab-BWo0cmzk.js.br +0 -0
  642. codex/static_root/assets/library-tab-BWo0cmzk.js.gz +0 -0
  643. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  644. codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
  645. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js +0 -32
  646. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.br +0 -0
  647. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.gz +0 -0
  648. codex/static_root/assets/main-ipmlipkF.js +0 -32
  649. codex/static_root/assets/main-ipmlipkF.js.br +0 -0
  650. codex/static_root/assets/main-ipmlipkF.js.gz +0 -0
  651. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +0 -1
  652. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
  653. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
  654. codex/static_root/assets/pagination-toolbar-BA9ifTed.css +0 -1
  655. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
  656. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
  657. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js +0 -1
  658. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.br +0 -0
  659. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.gz +0 -0
  660. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js +0 -1
  661. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.br +0 -0
  662. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.gz +0 -0
  663. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js +0 -305
  664. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.br +0 -0
  665. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.gz +0 -0
  666. codex/static_root/assets/pdf-doc-C2StaYUz.js +0 -305
  667. codex/static_root/assets/pdf-doc-C2StaYUz.js.br +0 -0
  668. codex/static_root/assets/pdf-doc-C2StaYUz.js.gz +0 -0
  669. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js +0 -2
  670. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.br +0 -0
  671. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.gz +0 -0
  672. codex/static_root/assets/reader-CCnq3j1-.js +0 -2
  673. codex/static_root/assets/reader-CCnq3j1-.js.br +0 -0
  674. codex/static_root/assets/reader-CCnq3j1-.js.gz +0 -0
  675. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css +0 -1
  676. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.br +0 -0
  677. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.gz +0 -0
  678. codex/static_root/assets/reader-CqXFa_gE.css +0 -1
  679. codex/static_root/assets/reader-CqXFa_gE.css.br +0 -0
  680. codex/static_root/assets/reader-CqXFa_gE.css.gz +0 -0
  681. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
  682. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
  683. codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
  684. codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
  685. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js +0 -1
  686. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.br +0 -0
  687. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.gz +0 -0
  688. codex/static_root/assets/relation-chips-C9ysW0tU.js +0 -1
  689. codex/static_root/assets/relation-chips-C9ysW0tU.js.br +0 -0
  690. codex/static_root/assets/relation-chips-C9ysW0tU.js.gz +0 -0
  691. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js +0 -2
  692. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.br +0 -0
  693. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.gz +0 -0
  694. codex/static_root/assets/settings-drawer-C0ofzAhW.js +0 -2
  695. codex/static_root/assets/settings-drawer-C0ofzAhW.js.br +0 -0
  696. codex/static_root/assets/settings-drawer-C0ofzAhW.js.gz +0 -0
  697. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +0 -1
  698. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
  699. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
  700. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +0 -1
  701. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
  702. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
  703. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +0 -1
  704. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
  705. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
  706. codex/static_root/assets/stats-tab-B4yKuY0c.css +0 -1
  707. codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
  708. codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
  709. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js +0 -1
  710. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.br +0 -0
  711. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.gz +0 -0
  712. codex/static_root/assets/stats-tab-BqJj7j1w.js +0 -1
  713. codex/static_root/assets/stats-tab-BqJj7j1w.js.br +0 -0
  714. codex/static_root/assets/stats-tab-BqJj7j1w.js.gz +0 -0
  715. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js +0 -1
  716. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.br +0 -0
  717. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.gz +0 -0
  718. codex/static_root/assets/task-tab-BsPzNk8S.js +0 -1
  719. codex/static_root/assets/task-tab-BsPzNk8S.js.br +0 -0
  720. codex/static_root/assets/task-tab-BsPzNk8S.js.gz +0 -0
  721. codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js +0 -1
  722. codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js.br +0 -0
  723. codex/static_root/assets/to-case-KvqtSug2.js +0 -1
  724. codex/static_root/assets/to-case-KvqtSug2.js.br +0 -0
  725. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
  726. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
  727. codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
  728. codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
  729. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js +0 -1
  730. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.br +0 -0
  731. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.gz +0 -0
  732. codex/static_root/assets/unauthorized-DwSdNzX2.js +0 -1
  733. codex/static_root/assets/unauthorized-DwSdNzX2.js.br +0 -0
  734. codex/static_root/assets/unauthorized-DwSdNzX2.js.gz +0 -0
  735. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js +0 -1
  736. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.br +0 -0
  737. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.gz +0 -0
  738. codex/static_root/assets/user-tab-DrJOb9Ys.js +0 -1
  739. codex/static_root/assets/user-tab-DrJOb9Ys.js.br +0 -0
  740. codex/static_root/assets/user-tab-DrJOb9Ys.js.gz +0 -0
  741. codex/static_root/js/choices-admin.3521e2a863df.json +0 -1
  742. codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
  743. codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
  744. codex/static_root/js/choices.2f6b0359e1ef.json +0 -1
  745. codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
  746. codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
  747. codex/static_root/manifest.1c84116d9a0a.json +0 -622
  748. codex/static_root/manifest.1c84116d9a0a.json.br +0 -0
  749. codex/static_root/manifest.1c84116d9a0a.json.gz +0 -0
  750. codex/util.py +0 -12
  751. codex/views/admin/auth.py +0 -28
  752. codex/views/browser/breadcrumbs.py +0 -202
  753. codex/views/browser/cover.py +0 -165
  754. codex/views/browser/mtime.py +0 -61
  755. codex/views/browser/order_by.py +0 -65
  756. codex/views/browser/paginate.py +0 -106
  757. codex/views/browser/settings.py +0 -66
  758. codex/views/browser/title.py +0 -48
  759. codex/views/browser/validate.py +0 -161
  760. codex/views/opds/auth.py +0 -17
  761. codex/views/public.py +0 -36
  762. codex/views/reader/arcs.py +0 -92
  763. codex/views/reader/books.py +0 -181
  764. codex/views/reader/init.py +0 -125
  765. codex/views/settings.py +0 -47
  766. codex/views/util.py +0 -77
  767. {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/LICENSE +0 -0
  768. {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/WHEEL +0 -0
  769. {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/entry_points.txt +0 -0
@@ -2,93 +2,30 @@
2
2
 
3
3
  import re
4
4
  from os import fsdecode
5
- from pathlib import Path
6
- from types import MappingProxyType
7
5
 
8
6
  from comicbox.box import Comicbox
9
7
  from watchdog.events import (
10
8
  EVENT_TYPE_CLOSED,
11
9
  EVENT_TYPE_CREATED,
12
- EVENT_TYPE_DELETED,
13
- EVENT_TYPE_MODIFIED,
14
10
  EVENT_TYPE_MOVED,
15
11
  EVENT_TYPE_OPENED,
16
12
  FileCreatedEvent,
17
13
  FileDeletedEvent,
18
- FileModifiedEvent,
19
- FileMovedEvent,
20
14
  FileSystemEventHandler,
21
15
  )
22
16
 
23
17
  from codex.librarian.watchdog.tasks import WatchdogEventTask
24
18
  from codex.logger.logging import get_logger
25
19
  from codex.logger_base import LoggerBaseMixin
26
- from codex.models import CustomCover
27
- from codex.settings.settings import CUSTOM_COVERS_DIR, CUSTOM_COVERS_GROUP_DIRS
28
20
 
29
21
  LOG = get_logger(__name__)
30
- _IMAGE_EXTS = frozenset({"jpg", "jpeg", "webp", "png", "gif", "bmp"})
31
- _GROUP_COVERS_DIRS = frozenset(CUSTOM_COVERS_GROUP_DIRS)
32
22
 
33
23
 
34
- class CoverMovedEvent(FileMovedEvent):
35
- """Cover Modified."""
36
-
37
- is_cover = True
38
- is_synthetic = True
39
-
40
-
41
- class CoverModifiedEvent(FileModifiedEvent):
42
- """Cover Modified."""
43
-
44
- is_cover = True
45
- is_synthetic = True
46
-
47
-
48
- class CoverCreatedEvent(FileCreatedEvent):
49
- """Cover Created."""
50
-
51
- is_cover = True
52
- is_synthetic = True
53
-
54
-
55
- class CoverDeletedEvent(FileDeletedEvent):
56
- """Cover Deleted."""
57
-
58
- is_cover = True
59
- is_synthetic = True
60
-
61
-
62
- COVERS_EVENT_TYPE_MAP = MappingProxyType(
63
- {
64
- EVENT_TYPE_MODIFIED: CoverModifiedEvent,
65
- EVENT_TYPE_CREATED: CoverCreatedEvent,
66
- EVENT_TYPE_DELETED: CoverDeletedEvent,
67
- }
68
- )
69
-
70
-
71
- class CodexEventHandlerBase(FileSystemEventHandler, LoggerBaseMixin):
72
- """Base class for Codex Event Handlers."""
24
+ class CodexLibraryEventHandler(FileSystemEventHandler, LoggerBaseMixin):
25
+ """Handle watchdog events for comics in a library."""
73
26
 
74
27
  IGNORED_EVENTS = frozenset({EVENT_TYPE_CLOSED, EVENT_TYPE_OPENED})
75
28
 
76
- def __init__(self, library, *args, **kwargs):
77
- """Let us send along he library id."""
78
- self.library_pk = library.pk
79
- self.librarian_queue = kwargs.pop("librarian_queue")
80
- log_queue = kwargs.pop("log_queue")
81
- self.init_logger(log_queue)
82
- super().__init__(*args, **kwargs)
83
-
84
- @staticmethod
85
- def _match_image_suffix(path: Path) -> bool:
86
- return path and len(path.suffix) > 1 and path.suffix[1:] in _IMAGE_EXTS
87
-
88
-
89
- class CodexLibraryEventHandler(CodexEventHandlerBase):
90
- """Handle watchdog events for comics in a library."""
91
-
92
29
  def _set_comic_matcher(self):
93
30
  comic_regex = r"\.(cb[zt"
94
31
  unsupported = []
@@ -108,8 +45,12 @@ class CodexLibraryEventHandler(CodexEventHandlerBase):
108
45
  un_str = ", ".join(unsupported)
109
46
  LOG.warning(f"Cannot detect or read from {un_str} archives")
110
47
 
111
- def __init__(self, *args, **kwargs):
48
+ def __init__(self, library, *args, **kwargs):
112
49
  """Let us send along he library id."""
50
+ self.library_pk = library.pk
51
+ self.librarian_queue = kwargs.pop("librarian_queue")
52
+ log_queue = kwargs.pop("log_queue")
53
+ self.init_logger(log_queue)
113
54
  self._set_comic_matcher()
114
55
  super().__init__(*args, **kwargs)
115
56
 
@@ -122,129 +63,48 @@ class CodexLibraryEventHandler(CodexEventHandlerBase):
122
63
  suffix = fsdecode(suffix)
123
64
  return self._comic_matcher.match(suffix) is not None
124
65
 
125
- @classmethod
126
- def _match_folder_cover(cls, path_str: str) -> bool:
127
- if not path_str:
128
- return False
129
- path = Path(path_str)
130
- return path.stem == CustomCover.FOLDER_COVER_STEM and cls._match_image_suffix(
131
- path
132
- )
133
-
134
- def _transform_file_move_event(
135
- self,
136
- event,
137
- events,
138
- source_match_comic,
139
- ):
140
- """Create file events for file moves."""
141
- dest_match_comic = self._match_comic_suffix(event.dest_path)
142
- if not source_match_comic and dest_match_comic:
143
- # Moved from an ignored file extension into a comic type,
144
- # so create a new comic.
145
- events.append(FileCreatedEvent(event.dest_path))
146
- elif source_match_comic and not dest_match_comic:
147
- # moved into something that's not a comic name so delete
148
- events.append(FileDeletedEvent(event.src_path))
149
- elif source_match_comic and dest_match_comic:
150
- events.append(event)
151
-
152
- def _transform_file_move_event_to_cover_event(self, event, events):
153
- """Create cover events for file moves."""
154
- source_match_cover = self._match_folder_cover(event.src_path)
155
- dest_match_cover = self._match_folder_cover(event.dest_path)
156
- if source_match_cover and not dest_match_cover:
157
- events.append(CoverDeletedEvent(event.src_path))
158
- elif not source_match_cover and dest_match_cover:
159
- events.append(CoverCreatedEvent(event.dest_path))
160
- elif source_match_cover and dest_match_cover:
161
- events.append(CoverMovedEvent(event.src_path, event.dest_path))
162
-
163
66
  def _transform_file_event(self, event):
164
67
  """Transform file events into other events."""
165
- events = []
166
- source_match_comic = self._match_comic_suffix(event.src_path)
68
+ source_match = self._match_comic_suffix(event.src_path)
167
69
  if event.event_type == EVENT_TYPE_MOVED:
168
- self._transform_file_move_event(event, events, source_match_comic)
169
- self._transform_file_move_event_to_cover_event(event, events)
170
- elif source_match_comic:
171
- events.append(event)
70
+ # Some types of file moves need to be cast as other events.
71
+ dest_match = self._match_comic_suffix(event.dest_path)
72
+ if not source_match and dest_match:
73
+ # Moved from an ignored file extension into a comic type,
74
+ # so create a new comic.
75
+ event = FileCreatedEvent(event.dest_path)
76
+ elif source_match and not dest_match:
77
+ # moved into something that's not a comic name so delete
78
+ event = FileDeletedEvent(event.src_path)
79
+ elif not source_match:
80
+ # Don't process non-moved, non-comic files at all
81
+ event = None
82
+ return event
83
+
84
+ def _transform_event(self, event):
85
+ """Transform events into other events."""
86
+ if event.event_type in self.IGNORED_EVENTS:
87
+ event = None
88
+ elif event.is_directory:
89
+ if event.event_type == EVENT_TYPE_CREATED:
90
+ # Directories are only created by comics
91
+ event = None
172
92
  else:
173
- source_match_cover = self._match_folder_cover(event.src_path)
174
- if source_match_cover and (
175
- event_class := COVERS_EVENT_TYPE_MAP.get(event.event_type)
176
- ):
177
- # Convert to cover type
178
- event = event_class(event.src_path)
179
- events.append(event)
180
- return events
93
+ event = self._transform_file_event(event)
94
+ return event
181
95
 
182
96
  def dispatch(self, event):
183
97
  """Send only valid codex events to the EventBatcher."""
184
98
  try:
185
- if event.event_type in self.IGNORED_EVENTS or (
186
- event.is_directory and event.event_type == EVENT_TYPE_CREATED
187
- ):
188
- # Directories are only created by comics
99
+ event = self._transform_event(event)
100
+ if not event:
189
101
  return
190
102
 
191
- events = self._transform_file_event(event)
192
-
193
103
  # Send it to the EventBatcher
194
- for event in events:
195
- task = WatchdogEventTask(self.library_pk, event)
196
- self.librarian_queue.put(task)
104
+ task = WatchdogEventTask(self.library_pk, event)
105
+ self.librarian_queue.put(task)
197
106
 
198
107
  # Calls stub event dispatchers
199
108
  # super().dispatch(event)
200
109
  except Exception:
201
110
  self.log.exception(f"{self.__class__.__name__} dispatch")
202
-
203
-
204
- class CodexCustomCoverEventHandler(CodexEventHandlerBase):
205
- """Special event handler for the custom cover dir."""
206
-
207
- @classmethod
208
- def _match_group_cover_image(cls, path_str: str) -> bool:
209
- path = Path(path_str)
210
- parent = path.parent
211
- if (
212
- parent.parent != CUSTOM_COVERS_DIR
213
- or str(parent.name) not in _GROUP_COVERS_DIRS
214
- ):
215
- return False
216
- return cls._match_image_suffix(path)
217
-
218
- @classmethod
219
- def _transform_event_moved(cls, event, src_cover_match):
220
- """Get a cover event for a file moved event."""
221
- send_event = None
222
- dest_cover_match = cls._match_group_cover_image(event.dest_path)
223
- if src_cover_match and dest_cover_match:
224
- send_event = CoverMovedEvent(event.src_path, event.dest_path)
225
- elif not src_cover_match and dest_cover_match:
226
- send_event = CoverCreatedEvent(event.dest_path)
227
- elif src_cover_match and not dest_cover_match:
228
- send_event = CoverDeletedEvent(event.src_path)
229
- return send_event
230
-
231
- def dispatch(self, event):
232
- """Send only valid cover events to the EventBatcher."""
233
- send_event = None
234
- try:
235
- if event.is_directory or event.event_type in self.IGNORED_EVENTS:
236
- return
237
- src_cover_match = self._match_group_cover_image(event.src_path)
238
- if event.event_type == EVENT_TYPE_MOVED:
239
- send_event = self._transform_event_moved(event, src_cover_match)
240
- elif src_cover_match:
241
- event_class = COVERS_EVENT_TYPE_MAP.get(event.event_type)
242
- if not event_class:
243
- return
244
- send_event = event_class(event.src_path)
245
-
246
- if send_event:
247
- task = WatchdogEventTask(self.library_pk, send_event)
248
- self.librarian_queue.put(task)
249
- except Exception:
250
- self.log.exception(f"{self.__class__.__name__} dispatch")
@@ -8,10 +8,7 @@ from watchdog.observers.api import (
8
8
  )
9
9
 
10
10
  from codex.librarian.watchdog.emitter import DatabasePollingEmitter
11
- from codex.librarian.watchdog.events import (
12
- CodexCustomCoverEventHandler,
13
- CodexLibraryEventHandler,
14
- )
11
+ from codex.librarian.watchdog.events import CodexLibraryEventHandler
15
12
  from codex.models import Library
16
13
  from codex.worker_base import WorkerBaseMixin
17
14
 
@@ -53,12 +50,7 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
53
50
  return
54
51
 
55
52
  # Set up the watch
56
- handler_class = (
57
- CodexCustomCoverEventHandler
58
- if library.covers_only
59
- else CodexLibraryEventHandler
60
- )
61
- handler = handler_class(
53
+ handler = CodexLibraryEventHandler(
62
54
  library,
63
55
  librarian_queue=self.librarian_queue,
64
56
  log_queue=self.log_queue,
@@ -117,14 +109,12 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
117
109
 
118
110
  # If we don't have an emitter for this watch already, create it.
119
111
  if self._emitter_for_watch.get(watch) is None:
120
- covers_only = isinstance(event_handler, CodexCustomCoverEventHandler)
121
112
  emitter = self._emitter_class(
122
113
  event_queue=self.event_queue,
123
114
  watch=watch,
124
115
  timeout=self.timeout,
125
116
  log_queue=self.log_queue,
126
117
  librarian_queue=self.librarian_queue,
127
- covers_only=covers_only,
128
118
  )
129
119
  self._add_emitter(emitter)
130
120
  if self.is_alive():
@@ -159,7 +149,7 @@ class LibraryPollingObserver(UatuMixin):
159
149
  def poll(self, library_pks, force=False):
160
150
  """Poll each requested emitter."""
161
151
  try:
162
- qs = Library.objects.all()
152
+ qs = Library.objects
163
153
  if library_pks:
164
154
  qs = qs.filter(pk__in=library_pks)
165
155
  paths = frozenset(qs.values_list("path", flat=True))
codex/middleware.py CHANGED
@@ -22,7 +22,8 @@ class TimezoneMiddleware:
22
22
 
23
23
  def __call__(self, request):
24
24
  """Fix timeszone from the django session."""
25
- if tzname := request.session.get("django_timezone"):
25
+ tzname = request.session.get("django_timezone")
26
+ if tzname:
26
27
  timezone.activate(tzname)
27
28
  else:
28
29
  timezone.deactivate()
@@ -0,0 +1,149 @@
1
+ """Generated by Django 5.0.4 on 2024-05-05 23:11."""
2
+
3
+ import os
4
+ from contextlib import suppress
5
+ from pathlib import Path
6
+
7
+ from django.db import migrations, models
8
+
9
+ from codex.models.const import ARTICLES
10
+
11
+ CONFIG_PATH = Path(os.environ.get("CODEX_CONFIG_DIR", Path.cwd() / "config"))
12
+ COVER_ROOT = CONFIG_PATH / "cache" / "covers"
13
+ _GROUP_MODEL_NAMES = (
14
+ "publisher",
15
+ "imprint",
16
+ "series",
17
+ "storyarc",
18
+ "folder",
19
+ "comic",
20
+ "failedimport",
21
+ )
22
+
23
+
24
+ def _set_sort_name(obj):
25
+ """Create sort_name for model."""
26
+ # Duplicate code from BaseGroupModel
27
+ sort_name = ""
28
+ lower_name = obj.name.lower()
29
+ sort_name = lower_name
30
+ name_parts = lower_name.split()
31
+ if len(name_parts) > 1:
32
+ first_word = name_parts[0]
33
+ if first_word in ARTICLES:
34
+ sort_name = " ".join(name_parts[1:])
35
+ sort_name += ", " + first_word
36
+ obj.sort_name = sort_name
37
+
38
+
39
+ def _generate_sort_name(apps, _schema_editor):
40
+ """Update new sort_name field."""
41
+ for model_name in _GROUP_MODEL_NAMES:
42
+ model = apps.get_model("codex", model_name)
43
+ update_fields = ("sort_name",)
44
+ only_fields = ("name", *update_fields)
45
+ update_groups = []
46
+ objs = model.objects.only(*only_fields)
47
+ for obj in objs:
48
+ _set_sort_name(obj)
49
+ update_groups.append(obj)
50
+ if update_groups:
51
+ model.objects.bulk_update(update_groups, update_fields)
52
+ print(f"\tUpdated sort_name for {len(update_groups)} {model_name}s")
53
+
54
+
55
+ def _remove_cover_symlinks(_apps, _schema_editor):
56
+ """Remove old missing cover symlinks."""
57
+ # Handled programmatically now.
58
+ count = 0
59
+ for dirpath_str, _, filenames in os.walk(COVER_ROOT):
60
+ dirpath = Path(dirpath_str)
61
+ for filename in filenames:
62
+ full_path = dirpath / filename
63
+ if full_path.is_symlink():
64
+ with suppress(OSError):
65
+ full_path.unlink(missing_ok=True)
66
+ count += 1
67
+ if count:
68
+ print(f"Removed {count} missing-cover symlinks from cover cache.")
69
+
70
+
71
+ class Migration(migrations.Migration):
72
+ """Migrate."""
73
+
74
+ dependencies = [
75
+ ("codex", "0026_comicbox_1"),
76
+ ]
77
+
78
+ operations = [
79
+ migrations.AddField(
80
+ model_name="comic",
81
+ name="sort_name",
82
+ field=models.CharField(db_index=True, default="", max_length=128),
83
+ ),
84
+ migrations.AddField(
85
+ model_name="failedimport",
86
+ name="sort_name",
87
+ field=models.CharField(db_index=True, default="", max_length=128),
88
+ ),
89
+ migrations.AddField(
90
+ model_name="folder",
91
+ name="sort_name",
92
+ field=models.CharField(db_index=True, default="", max_length=128),
93
+ ),
94
+ migrations.AddField(
95
+ model_name="imprint",
96
+ name="sort_name",
97
+ field=models.CharField(db_index=True, default="", max_length=128),
98
+ ),
99
+ migrations.AddField(
100
+ model_name="publisher",
101
+ name="sort_name",
102
+ field=models.CharField(db_index=True, default="", max_length=128),
103
+ ),
104
+ migrations.AddField(
105
+ model_name="series",
106
+ name="sort_name",
107
+ field=models.CharField(db_index=True, default="", max_length=128),
108
+ ),
109
+ migrations.AddField(
110
+ model_name="storyarc",
111
+ name="sort_name",
112
+ field=models.CharField(db_index=True, default="", max_length=128),
113
+ ),
114
+ migrations.AlterField(
115
+ model_name="adminflag",
116
+ name="key",
117
+ field=models.CharField(
118
+ choices=[
119
+ ("FV", "Folder View"),
120
+ ("RG", "Registration"),
121
+ ("NU", "Non Users"),
122
+ ("AU", "Auto Update"),
123
+ ("SO", "Search Index Optimize"),
124
+ ("IM", "Import Metadata"),
125
+ ],
126
+ db_index=True,
127
+ max_length=2,
128
+ ),
129
+ ),
130
+ migrations.AlterField(
131
+ model_name="adminflag",
132
+ name="key",
133
+ field=models.CharField(
134
+ choices=[
135
+ ("FV", "Folder View"),
136
+ ("RG", "Registration"),
137
+ ("NU", "Non Users"),
138
+ ("AU", "Auto Update"),
139
+ ("SO", "Search Index Optimize"),
140
+ ("IM", "Import Metadata"),
141
+ ("DC", "Dynamic Group Covers"),
142
+ ],
143
+ db_index=True,
144
+ max_length=2,
145
+ ),
146
+ ),
147
+ migrations.RunPython(_generate_sort_name),
148
+ migrations.RunPython(_remove_cover_symlinks),
149
+ ]
codex/models/admin.py CHANGED
@@ -39,6 +39,7 @@ class AdminFlag(BaseModel):
39
39
  AUTO_UPDATE = "AU"
40
40
  SEARCH_INDEX_OPTIMIZE = "SO"
41
41
  IMPORT_METADATA = "IM"
42
+ DYNAMIC_GROUP_COVERS = "DC"
42
43
 
43
44
  FALSE_DEFAULTS = frozenset({FlagChoices.AUTO_UPDATE})
44
45
 
@@ -82,6 +83,7 @@ class Timestamp(BaseModel):
82
83
  class TimestampChoices(TextChoices):
83
84
  """Choices for Timestamps."""
84
85
 
86
+ COVERS = "CV", _("Covers")
85
87
  JANITOR = "JA", _("Janitor")
86
88
  CODEX_VERSION = "VR", _("Codex Version")
87
89
  SEARCH_INDEX_UUID = "SI", _("Search Index UUID")
codex/models/base.py CHANGED
@@ -23,6 +23,3 @@ class BaseModel(Model):
23
23
 
24
24
  abstract = True
25
25
  get_latest_by = "updated_at"
26
-
27
- def presave(self):
28
- """Create values before save."""
codex/models/comic.py CHANGED
@@ -22,14 +22,7 @@ from django.db.models import (
22
22
  TextField,
23
23
  )
24
24
 
25
- from codex.models.groups import (
26
- Folder,
27
- Imprint,
28
- Publisher,
29
- Series,
30
- Volume,
31
- WatchedPathBrowserGroup,
32
- )
25
+ from codex.models.groups import Imprint, Publisher, Series, Volume
33
26
  from codex.models.named import (
34
27
  AgeRating,
35
28
  Character,
@@ -48,6 +41,7 @@ from codex.models.named import (
48
41
  Tagger,
49
42
  Team,
50
43
  )
44
+ from codex.models.paths import Folder, WatchedPath
51
45
 
52
46
  __all__ = ("Comic",)
53
47
 
@@ -61,7 +55,7 @@ class ReadingDirection(Choices):
61
55
  BTT = ReadingDirectionEnum.BTT.value
62
56
 
63
57
 
64
- class Comic(WatchedPathBrowserGroup):
58
+ class Comic(WatchedPath):
65
59
  """Comic metadata."""
66
60
 
67
61
  class FileType(Choices):
@@ -174,12 +168,10 @@ class Comic(WatchedPathBrowserGroup):
174
168
  default="",
175
169
  )
176
170
 
177
- # Not useful
178
- custom_cover = None
179
-
180
- class Meta(WatchedPathBrowserGroup.Meta):
171
+ class Meta(WatchedPath.Meta):
181
172
  """Constraints."""
182
173
 
174
+ unique_together = ("library", "path")
183
175
  verbose_name = "Issue"
184
176
 
185
177
  def _set_date(self):
@@ -287,3 +279,7 @@ class Comic(WatchedPathBrowserGroup):
287
279
  if self.issue_suffix:
288
280
  res += self.issue_suffix
289
281
  return res
282
+
283
+ def search_path(self) -> str:
284
+ """Relative path for search index."""
285
+ return self.path.removeprefix(self.library.path)
@@ -1,6 +1,6 @@
1
- """Utilities for models."""
1
+ """Constants for models."""
2
2
 
3
- _ARTICLES = frozenset(
3
+ ARTICLES = frozenset(
4
4
  ("a", "an", "the") # en # noqa RUF005
5
5
  + ("un", "unos", "unas", "el", "los", "la", "las") # es
6
6
  + ("un", "une", "le", "les", "la", "les", "l'") # fr
@@ -16,16 +16,3 @@ _ARTICLES = frozenset(
16
16
  + ("en", "et") # da
17
17
  + ("el", "la", "els", "les", "un", "una", "uns", "unes", "na") # ct
18
18
  )
19
-
20
-
21
- def get_sort_name(name: str) -> str:
22
- """Create sort_name from name."""
23
- lower_name = name.lower()
24
- sort_name = lower_name
25
- name_parts = lower_name.split()
26
- if len(name_parts) > 1:
27
- first_word = name_parts[0]
28
- if first_word in _ARTICLES:
29
- sort_name = " ".join(name_parts[1:])
30
- sort_name += ", " + first_word
31
- return sort_name
codex/models/groups.py CHANGED
@@ -1,17 +1,17 @@
1
1
  """Browser Group models."""
2
2
 
3
- from django.db.models import CASCADE, SET_DEFAULT, ForeignKey
4
- from django.db.models.fields import (
3
+ from django.db.models import (
4
+ CASCADE,
5
5
  CharField,
6
+ ForeignKey,
6
7
  PositiveSmallIntegerField,
7
8
  SmallIntegerField,
8
9
  )
9
10
 
10
11
  from codex.models.base import MAX_NAME_LEN, BaseModel
11
- from codex.models.paths import CustomCover, WatchedPath
12
- from codex.models.util import get_sort_name
12
+ from codex.models.const import ARTICLES
13
13
 
14
- __all__ = ("BrowserGroupModel", "Publisher", "Imprint", "Series", "Volume", "Folder")
14
+ __all__ = ("BrowserGroupModel", "Publisher", "Imprint", "Series", "Volume")
15
15
 
16
16
 
17
17
  class BrowserGroupModel(BaseModel):
@@ -22,13 +22,18 @@ class BrowserGroupModel(BaseModel):
22
22
 
23
23
  name = CharField(db_index=True, max_length=MAX_NAME_LEN, default=DEFAULT_NAME)
24
24
  sort_name = CharField(db_index=True, max_length=MAX_NAME_LEN, default=DEFAULT_NAME)
25
- custom_cover = ForeignKey(
26
- CustomCover, on_delete=SET_DEFAULT, null=True, default=None
27
- )
28
25
 
29
26
  def set_sort_name(self):
30
27
  """Create sort_name for model."""
31
- self.sort_name = get_sort_name(self.name)
28
+ lower_name = self.name.lower()
29
+ sort_name = lower_name
30
+ name_parts = lower_name.split()
31
+ if len(name_parts) > 1:
32
+ first_word = name_parts[0]
33
+ if first_word in ARTICLES:
34
+ sort_name = " ".join(name_parts[1:])
35
+ sort_name += ", " + first_word
36
+ self.sort_name = sort_name
32
37
 
33
38
  def presave(self):
34
39
  """Set computed values."""
@@ -98,7 +103,6 @@ class Volume(BrowserGroupModel):
98
103
 
99
104
  # Harmful because name is numeric
100
105
  sort_name = None
101
- custom_cover = None
102
106
 
103
107
  def set_sort_name(self):
104
108
  """Noop."""
@@ -121,21 +125,3 @@ class Volume(BrowserGroupModel):
121
125
  def __str__(self):
122
126
  """Represent volume as a string."""
123
127
  return self.to_str(self.name)
124
-
125
-
126
- class WatchedPathBrowserGroup(BrowserGroupModel, WatchedPath):
127
- """Watched Path Browser Group."""
128
-
129
- def presave(self):
130
- """Fix multiple inheritance presave."""
131
- super().presave()
132
- WatchedPath.presave(self)
133
-
134
- class Meta(WatchedPath.Meta): # type: ignore
135
- """Use Mixin Meta."""
136
-
137
- abstract = True
138
-
139
-
140
- class Folder(WatchedPathBrowserGroup):
141
- """File system folder."""