codex 1.5.17__py3-none-any.whl → 1.6.0__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 (921) hide show
  1. codex/exceptions.py +25 -12
  2. codex/integrity.py +66 -3
  3. codex/librarian/covers/coverd.py +2 -2
  4. codex/librarian/covers/create.py +52 -30
  5. codex/librarian/covers/path.py +9 -8
  6. codex/librarian/covers/purge.py +28 -18
  7. codex/librarian/covers/tasks.py +3 -2
  8. codex/librarian/importer/{aggregate_metadata.py → aggregate.py} +58 -88
  9. codex/librarian/importer/cache.py +93 -0
  10. codex/librarian/importer/const.py +77 -23
  11. codex/librarian/importer/create_comics.py +101 -18
  12. codex/librarian/importer/create_covers.py +96 -0
  13. codex/librarian/importer/create_fks.py +129 -150
  14. codex/librarian/importer/deleted.py +103 -28
  15. codex/librarian/importer/{clean_metadata.py → extract.py} +33 -4
  16. codex/librarian/importer/failed_imports.py +44 -66
  17. codex/librarian/importer/importer.py +96 -0
  18. codex/librarian/importer/importerd.py +71 -290
  19. codex/librarian/importer/init.py +265 -0
  20. codex/librarian/importer/link_comics.py +26 -26
  21. codex/librarian/importer/link_covers.py +73 -0
  22. codex/librarian/importer/moved.py +138 -89
  23. codex/librarian/importer/query_covers.py +52 -0
  24. codex/librarian/importer/query_fks.py +255 -189
  25. codex/librarian/importer/status.py +8 -39
  26. codex/librarian/importer/tasks.py +26 -9
  27. codex/librarian/janitor/cleanup.py +61 -16
  28. codex/librarian/janitor/failed_imports.py +1 -1
  29. codex/librarian/janitor/janitor.py +13 -1
  30. codex/librarian/janitor/janitord.py +1 -1
  31. codex/librarian/janitor/status.py +1 -0
  32. codex/librarian/janitor/tasks.py +5 -0
  33. codex/librarian/librariand.py +9 -5
  34. codex/librarian/notifier/notifierd.py +1 -1
  35. codex/librarian/notifier/tasks.py +4 -4
  36. codex/librarian/search/update.py +1 -1
  37. codex/librarian/watchdog/db_snapshot.py +9 -6
  38. codex/librarian/watchdog/emitter.py +3 -0
  39. codex/librarian/watchdog/event_batcherd.py +19 -3
  40. codex/librarian/watchdog/events.py +176 -36
  41. codex/librarian/watchdog/observers.py +13 -3
  42. codex/middleware.py +1 -2
  43. codex/migrations/0027_import_order_and_covers.py +280 -0
  44. codex/models/admin.py +0 -1
  45. codex/models/base.py +6 -0
  46. codex/models/comic.py +41 -44
  47. codex/models/functions.py +12 -0
  48. codex/models/groups.py +58 -12
  49. codex/models/library.py +11 -3
  50. codex/models/paths.py +65 -13
  51. codex/models/query.py +91 -0
  52. codex/models/util.py +31 -0
  53. codex/permissions.py +1 -1
  54. codex/search/backend.py +2 -2
  55. codex/search/backend_search.py +229 -0
  56. codex/search/engine.py +1 -9
  57. codex/search/query.py +67 -0
  58. codex/search/writing.py +1 -1
  59. codex/serializers/admin.py +56 -45
  60. codex/serializers/browser/choices.py +37 -42
  61. codex/serializers/browser/filters.py +32 -136
  62. codex/serializers/browser/metadata.py +59 -0
  63. codex/serializers/browser/mixins.py +65 -0
  64. codex/serializers/browser/mtime.py +21 -0
  65. codex/serializers/browser/page.py +6 -21
  66. codex/serializers/browser/settings.py +43 -11
  67. codex/serializers/choices.py +35 -12
  68. codex/serializers/fields.py +149 -0
  69. codex/serializers/mixins.py +0 -22
  70. codex/serializers/models/pycountry.py +11 -51
  71. codex/serializers/opds/urls.py +10 -0
  72. codex/serializers/opds/v2.py +10 -16
  73. codex/serializers/opds/v2_unused.py +5 -3
  74. codex/serializers/reader.py +24 -16
  75. codex/serializers/redirect.py +3 -2
  76. codex/serializers/route.py +45 -0
  77. codex/serializers/settings.py +12 -0
  78. codex/settings/settings.py +79 -32
  79. codex/signals/django_signals.py +6 -0
  80. codex/startup.py +77 -3
  81. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js +1 -0
  82. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.br +0 -0
  83. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.gz +0 -0
  84. codex/static_root/assets/VCheckbox-DanrzdHo.js +1 -0
  85. codex/static_root/assets/VCheckbox-DanrzdHo.js.br +0 -0
  86. codex/static_root/assets/VCheckbox-DanrzdHo.js.gz +0 -0
  87. codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js +1 -0
  88. codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.br +0 -0
  89. codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.gz +0 -0
  90. codex/static_root/assets/VCheckboxBtn-DxNOKONG.js +1 -0
  91. codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.br +0 -0
  92. codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.gz +0 -0
  93. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js +1 -0
  94. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.br +0 -0
  95. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.gz +0 -0
  96. codex/static_root/assets/VCombobox-DDvdoPTG.js +1 -0
  97. codex/static_root/assets/VCombobox-DDvdoPTG.js.br +0 -0
  98. codex/static_root/assets/VCombobox-DDvdoPTG.js.gz +0 -0
  99. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js +1 -0
  100. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.br +0 -0
  101. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.gz +0 -0
  102. codex/static_root/assets/VDialog-Bd5dN_E1.js +1 -0
  103. codex/static_root/assets/VDialog-Bd5dN_E1.js.br +0 -0
  104. codex/static_root/assets/VDialog-Bd5dN_E1.js.gz +0 -0
  105. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css +1 -0
  106. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
  107. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
  108. codex/static_root/assets/VDialog-lQ13FnRR.css +1 -0
  109. codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
  110. codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
  111. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js +1 -0
  112. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.br +0 -0
  113. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.gz +0 -0
  114. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js +1 -0
  115. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.br +0 -0
  116. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.gz +0 -0
  117. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js +1 -0
  118. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.br +0 -0
  119. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.gz +0 -0
  120. codex/static_root/assets/VRadioGroup-DKb8XPr-.js +1 -0
  121. codex/static_root/assets/VRadioGroup-DKb8XPr-.js.br +0 -0
  122. codex/static_root/assets/VRadioGroup-DKb8XPr-.js.gz +0 -0
  123. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +1 -0
  124. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
  125. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
  126. codex/static_root/assets/VSelect-ARDhJiQK.css +1 -0
  127. codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
  128. codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
  129. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js +1 -0
  130. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.br +0 -0
  131. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.gz +0 -0
  132. codex/static_root/assets/VSelect-C_yPbyCG.js +1 -0
  133. codex/static_root/assets/VSelect-C_yPbyCG.js.br +0 -0
  134. codex/static_root/assets/VSelect-C_yPbyCG.js.gz +0 -0
  135. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js +1 -0
  136. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.br +0 -0
  137. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.gz +0 -0
  138. codex/static_root/assets/VSelectionControl-CmFPvGMS.js +1 -0
  139. codex/static_root/assets/VSelectionControl-CmFPvGMS.js.br +0 -0
  140. codex/static_root/assets/VSelectionControl-CmFPvGMS.js.gz +0 -0
  141. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js +1 -0
  142. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.br +0 -0
  143. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.gz +0 -0
  144. codex/static_root/assets/VSlideGroup-D5FZWsM1.js +1 -0
  145. codex/static_root/assets/VSlideGroup-D5FZWsM1.js.br +0 -0
  146. codex/static_root/assets/VSlideGroup-D5FZWsM1.js.gz +0 -0
  147. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css +1 -0
  148. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.br +0 -0
  149. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.gz +0 -0
  150. codex/static_root/assets/VSlideGroup-Dk_1upHt.css +1 -0
  151. codex/static_root/assets/VSlideGroup-Dk_1upHt.css.br +0 -0
  152. codex/static_root/assets/VSlideGroup-Dk_1upHt.css.gz +0 -0
  153. codex/static_root/assets/{VTable-XNBBnB2q.b2784736062a.js → VTable-2RHFqfg6.f0f2106b158b.js} +1 -1
  154. codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.br +0 -0
  155. codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.gz +0 -0
  156. codex/static_root/assets/{VTable-XNBBnB2q.js → VTable-2RHFqfg6.js} +1 -1
  157. codex/static_root/assets/VTable-2RHFqfg6.js.br +0 -0
  158. codex/static_root/assets/VTable-2RHFqfg6.js.gz +0 -0
  159. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +1 -0
  160. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
  161. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
  162. codex/static_root/assets/VTextField-BssVoDhB.css +1 -0
  163. codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
  164. codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
  165. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js +1 -0
  166. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.br +0 -0
  167. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.gz +0 -0
  168. codex/static_root/assets/VTextField-o1N9ylIb.js +1 -0
  169. codex/static_root/assets/VTextField-o1N9ylIb.js.br +0 -0
  170. codex/static_root/assets/VTextField-o1N9ylIb.js.gz +0 -0
  171. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js +1 -0
  172. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.br +0 -0
  173. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.gz +0 -0
  174. codex/static_root/assets/VWindowItem-C_zvuz9C.js +1 -0
  175. codex/static_root/assets/VWindowItem-C_zvuz9C.js.br +0 -0
  176. codex/static_root/assets/VWindowItem-C_zvuz9C.js.gz +0 -0
  177. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css +1 -0
  178. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
  179. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
  180. codex/static_root/assets/admin-B8Bp2Uih.css +1 -0
  181. codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
  182. codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
  183. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js +1 -0
  184. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.br +0 -0
  185. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.gz +0 -0
  186. codex/static_root/assets/admin-DHyHEWZ3.js +1 -0
  187. codex/static_root/assets/admin-DHyHEWZ3.js.br +0 -0
  188. codex/static_root/assets/admin-DHyHEWZ3.js.gz +0 -0
  189. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js +30 -0
  190. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.br +0 -0
  191. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.gz +0 -0
  192. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js +30 -0
  193. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.br +0 -0
  194. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.gz +0 -0
  195. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css +1 -0
  196. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
  197. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
  198. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css +1 -0
  199. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
  200. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
  201. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js +1 -0
  202. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.br +0 -0
  203. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.gz +0 -0
  204. codex/static_root/assets/browser-D0Vjmmp_.js +1 -0
  205. codex/static_root/assets/browser-D0Vjmmp_.js.br +0 -0
  206. codex/static_root/assets/browser-D0Vjmmp_.js.gz +0 -0
  207. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css +1 -0
  208. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.br +0 -0
  209. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.gz +0 -0
  210. codex/static_root/assets/browser-D7QQDneO.css +1 -0
  211. codex/static_root/assets/browser-D7QQDneO.css.br +0 -0
  212. codex/static_root/assets/browser-D7QQDneO.css.gz +0 -0
  213. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +1 -0
  214. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
  215. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
  216. codex/static_root/assets/change-password-dialog-0u6IJA87.css +1 -0
  217. codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
  218. codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
  219. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js +1 -0
  220. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.br +0 -0
  221. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.gz +0 -0
  222. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js +1 -0
  223. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.br +0 -0
  224. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.gz +0 -0
  225. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js +1 -0
  226. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.br +0 -0
  227. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.gz +0 -0
  228. codex/static_root/assets/confirm-dialog-DCKvg3ai.js +1 -0
  229. codex/static_root/assets/confirm-dialog-DCKvg3ai.js.br +0 -0
  230. codex/static_root/assets/confirm-dialog-DCKvg3ai.js.gz +0 -0
  231. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +1 -0
  232. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
  233. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +1 -0
  234. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
  235. codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +1 -0
  236. codex/static_root/assets/datetime-column-DeCthByU.css +1 -0
  237. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js +1 -0
  238. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.br +0 -0
  239. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.gz +0 -0
  240. codex/static_root/assets/datetime-column-jHKA_uNm.js +1 -0
  241. codex/static_root/assets/datetime-column-jHKA_uNm.js.br +0 -0
  242. codex/static_root/assets/datetime-column-jHKA_uNm.js.gz +0 -0
  243. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js +1 -0
  244. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.br +0 -0
  245. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.gz +0 -0
  246. codex/static_root/assets/filter-DgPg2ts9.js +1 -0
  247. codex/static_root/assets/filter-DgPg2ts9.js.br +0 -0
  248. codex/static_root/assets/filter-DgPg2ts9.js.gz +0 -0
  249. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js +1 -0
  250. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.br +0 -0
  251. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.gz +0 -0
  252. codex/static_root/assets/flag-tab-9qxOALxP.js +1 -0
  253. codex/static_root/assets/flag-tab-9qxOALxP.js.br +0 -0
  254. codex/static_root/assets/flag-tab-9qxOALxP.js.gz +0 -0
  255. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +1 -0
  256. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
  257. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
  258. codex/static_root/assets/flag-tab-BNwbLfrN.css +1 -0
  259. codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
  260. codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
  261. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css +1 -0
  262. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.br +0 -0
  263. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.gz +0 -0
  264. codex/static_root/assets/group-tab-CBKuS3Iv.css +1 -0
  265. codex/static_root/assets/group-tab-CBKuS3Iv.css.br +0 -0
  266. codex/static_root/assets/group-tab-CBKuS3Iv.css.gz +0 -0
  267. codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js +1 -0
  268. codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.br +0 -0
  269. codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.gz +0 -0
  270. codex/static_root/assets/group-tab-D5vWrihT.js +1 -0
  271. codex/static_root/assets/group-tab-D5vWrihT.js.br +0 -0
  272. codex/static_root/assets/group-tab-D5vWrihT.js.gz +0 -0
  273. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js +1 -0
  274. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.br +0 -0
  275. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.gz +0 -0
  276. codex/static_root/assets/http-error-B4FHoKfs.js +1 -0
  277. codex/static_root/assets/http-error-B4FHoKfs.js.br +0 -0
  278. codex/static_root/assets/http-error-B4FHoKfs.js.gz +0 -0
  279. codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css +1 -0
  280. codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css.br +0 -0
  281. codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css.gz +0 -0
  282. codex/static_root/assets/http-error-DKulXuTl.css +1 -0
  283. codex/static_root/assets/http-error-DKulXuTl.css.br +0 -0
  284. codex/static_root/assets/http-error-DKulXuTl.css.gz +0 -0
  285. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +1 -0
  286. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
  287. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
  288. codex/static_root/assets/library-tab-BN0SfPcH.css +1 -0
  289. codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
  290. codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
  291. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js +1 -0
  292. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.br +0 -0
  293. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.gz +0 -0
  294. codex/static_root/assets/library-tab-BWo0cmzk.js +1 -0
  295. codex/static_root/assets/library-tab-BWo0cmzk.js.br +0 -0
  296. codex/static_root/assets/library-tab-BWo0cmzk.js.gz +0 -0
  297. codex/static_root/assets/{main-ClKzvaJK.css → main-Ztp855PW.css} +1 -1
  298. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  299. codex/static_root/assets/{main-ClKzvaJK.css.gz → main-Ztp855PW.css.gz} +0 -0
  300. codex/static_root/assets/{main-ClKzvaJK.04ea1888c6b4.css → main-Ztp855PW.d46d718fac45.css} +1 -1
  301. codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
  302. codex/static_root/assets/{main-ClKzvaJK.04ea1888c6b4.css.gz → main-Ztp855PW.d46d718fac45.css.gz} +0 -0
  303. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js +32 -0
  304. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.br +0 -0
  305. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.gz +0 -0
  306. codex/static_root/assets/main-ipmlipkF.js +32 -0
  307. codex/static_root/assets/main-ipmlipkF.js.br +0 -0
  308. codex/static_root/assets/main-ipmlipkF.js.gz +0 -0
  309. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +1 -0
  310. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
  311. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
  312. codex/static_root/assets/pagination-toolbar-BA9ifTed.css +1 -0
  313. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
  314. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
  315. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js +1 -0
  316. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.br +0 -0
  317. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.gz +0 -0
  318. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js +1 -0
  319. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.br +0 -0
  320. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.gz +0 -0
  321. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js +305 -0
  322. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.br +0 -0
  323. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.gz +0 -0
  324. codex/static_root/assets/pdf-doc-C2StaYUz.js +305 -0
  325. codex/static_root/assets/pdf-doc-C2StaYUz.js.br +0 -0
  326. codex/static_root/assets/pdf-doc-C2StaYUz.js.gz +0 -0
  327. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js +2 -0
  328. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.br +0 -0
  329. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.gz +0 -0
  330. codex/static_root/assets/reader-CCnq3j1-.js +2 -0
  331. codex/static_root/assets/reader-CCnq3j1-.js.br +0 -0
  332. codex/static_root/assets/reader-CCnq3j1-.js.gz +0 -0
  333. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css +1 -0
  334. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.br +0 -0
  335. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.gz +0 -0
  336. codex/static_root/assets/reader-CqXFa_gE.css +1 -0
  337. codex/static_root/assets/reader-CqXFa_gE.css.br +0 -0
  338. codex/static_root/assets/reader-CqXFa_gE.css.gz +0 -0
  339. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css +1 -0
  340. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
  341. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
  342. codex/static_root/assets/relation-chips-Bsc-gUxE.css +1 -0
  343. codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
  344. codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
  345. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js +1 -0
  346. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.br +0 -0
  347. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.gz +0 -0
  348. codex/static_root/assets/relation-chips-C9ysW0tU.js +1 -0
  349. codex/static_root/assets/relation-chips-C9ysW0tU.js.br +0 -0
  350. codex/static_root/assets/relation-chips-C9ysW0tU.js.gz +0 -0
  351. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js +2 -0
  352. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.br +0 -0
  353. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.gz +0 -0
  354. codex/static_root/assets/settings-drawer-C0ofzAhW.js +2 -0
  355. codex/static_root/assets/settings-drawer-C0ofzAhW.js.br +0 -0
  356. codex/static_root/assets/settings-drawer-C0ofzAhW.js.gz +0 -0
  357. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +1 -0
  358. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
  359. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
  360. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +1 -0
  361. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
  362. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
  363. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +1 -0
  364. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
  365. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
  366. codex/static_root/assets/stats-tab-B4yKuY0c.css +1 -0
  367. codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
  368. codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
  369. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js +1 -0
  370. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.br +0 -0
  371. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.gz +0 -0
  372. codex/static_root/assets/stats-tab-BqJj7j1w.js +1 -0
  373. codex/static_root/assets/stats-tab-BqJj7j1w.js.br +0 -0
  374. codex/static_root/assets/stats-tab-BqJj7j1w.js.gz +0 -0
  375. codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css +1 -0
  376. codex/static_root/assets/{task-tab-DoX9OA37.9bb355f36dbf.css.br → task-tab-1XiWn9xP.06cb8e1b794d.css.br} +1 -1
  377. codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css.gz +0 -0
  378. codex/static_root/assets/task-tab-1XiWn9xP.css +1 -0
  379. codex/static_root/assets/{task-tab-DoX9OA37.css.br → task-tab-1XiWn9xP.css.br} +1 -1
  380. codex/static_root/assets/task-tab-1XiWn9xP.css.gz +0 -0
  381. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js +1 -0
  382. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.br +0 -0
  383. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.gz +0 -0
  384. codex/static_root/assets/task-tab-BsPzNk8S.js +1 -0
  385. codex/static_root/assets/task-tab-BsPzNk8S.js.br +0 -0
  386. codex/static_root/assets/task-tab-BsPzNk8S.js.gz +0 -0
  387. codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js +1 -0
  388. codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js.br +0 -0
  389. codex/static_root/assets/to-case-KvqtSug2.js +1 -0
  390. codex/static_root/assets/to-case-KvqtSug2.js.br +0 -0
  391. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css +1 -0
  392. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
  393. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
  394. codex/static_root/assets/unauthorized-CibJYD8o.css +1 -0
  395. codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
  396. codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
  397. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js +1 -0
  398. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.br +0 -0
  399. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.gz +0 -0
  400. codex/static_root/assets/unauthorized-DwSdNzX2.js +1 -0
  401. codex/static_root/assets/unauthorized-DwSdNzX2.js.br +0 -0
  402. codex/static_root/assets/unauthorized-DwSdNzX2.js.gz +0 -0
  403. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js +1 -0
  404. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.br +0 -0
  405. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.gz +0 -0
  406. codex/static_root/assets/user-tab-DrJOb9Ys.js +1 -0
  407. codex/static_root/assets/user-tab-DrJOb9Ys.js.br +0 -0
  408. codex/static_root/assets/user-tab-DrJOb9Ys.js.gz +0 -0
  409. codex/static_root/img/folder.f28936a3701d.svg +1 -0
  410. codex/static_root/img/folder.f28936a3701d.svg.br +0 -0
  411. codex/static_root/img/folder.f28936a3701d.svg.gz +0 -0
  412. codex/static_root/img/folder.svg +1 -0
  413. codex/static_root/img/folder.svg.br +0 -0
  414. codex/static_root/img/folder.svg.gz +0 -0
  415. codex/static_root/img/imprint.7c2adebb76d6.svg +1 -0
  416. codex/static_root/img/imprint.7c2adebb76d6.svg.br +0 -0
  417. codex/static_root/img/imprint.7c2adebb76d6.svg.gz +0 -0
  418. codex/static_root/img/imprint.svg +1 -0
  419. codex/static_root/img/imprint.svg.br +0 -0
  420. codex/static_root/img/imprint.svg.gz +0 -0
  421. codex/static_root/img/logo-32.5688944495bf.webp +0 -0
  422. codex/static_root/img/logo-32.webp +0 -0
  423. codex/static_root/img/logo-maskable-180.c58d966aa64e.webp +0 -0
  424. codex/static_root/img/logo-maskable-180.webp +0 -0
  425. codex/static_root/img/logo-maskable.8f7de7f1c6a8.svg +1 -0
  426. codex/static_root/img/logo-maskable.8f7de7f1c6a8.svg.br +0 -0
  427. codex/static_root/img/logo-maskable.8f7de7f1c6a8.svg.gz +0 -0
  428. codex/static_root/img/logo-maskable.svg +1 -83
  429. codex/static_root/img/logo-maskable.svg.br +0 -0
  430. codex/static_root/img/logo-maskable.svg.gz +0 -0
  431. codex/static_root/img/logo.0f5344a028ca.svg +1 -0
  432. codex/static_root/img/logo.0f5344a028ca.svg.br +0 -0
  433. codex/static_root/img/logo.0f5344a028ca.svg.gz +0 -0
  434. codex/static_root/img/logo.svg +1 -80
  435. codex/static_root/img/logo.svg.br +0 -0
  436. codex/static_root/img/logo.svg.gz +0 -0
  437. codex/static_root/img/missing-cover-165.0743171f2148.webp +0 -0
  438. codex/static_root/img/missing-cover-165.webp +0 -0
  439. codex/static_root/img/missing-cover.ddb038cc2c56.svg +1 -0
  440. codex/static_root/img/missing-cover.ddb038cc2c56.svg.br +0 -0
  441. codex/static_root/img/missing-cover.ddb038cc2c56.svg.gz +0 -0
  442. codex/static_root/img/missing-cover.svg +1 -0
  443. codex/static_root/img/missing-cover.svg.br +0 -0
  444. codex/static_root/img/missing-cover.svg.gz +0 -0
  445. codex/static_root/img/publisher.82d378fcd601.svg +1 -0
  446. codex/static_root/img/publisher.82d378fcd601.svg.br +0 -0
  447. codex/static_root/img/publisher.82d378fcd601.svg.gz +0 -0
  448. codex/static_root/img/publisher.svg +1 -0
  449. codex/static_root/img/publisher.svg.br +0 -0
  450. codex/static_root/img/publisher.svg.gz +0 -0
  451. codex/static_root/img/series.ec8a889b8f95.svg +1 -0
  452. codex/static_root/img/series.ec8a889b8f95.svg.br +0 -0
  453. codex/static_root/img/series.ec8a889b8f95.svg.gz +0 -0
  454. codex/static_root/img/series.svg +1 -0
  455. codex/static_root/img/series.svg.br +0 -0
  456. codex/static_root/img/series.svg.gz +0 -0
  457. codex/static_root/img/story-arc.ca10014355ef.svg +1 -0
  458. codex/static_root/img/story-arc.ca10014355ef.svg.br +1 -0
  459. codex/static_root/img/story-arc.ca10014355ef.svg.gz +0 -0
  460. codex/static_root/img/story-arc.svg +1 -0
  461. codex/static_root/img/story-arc.svg.br +1 -0
  462. codex/static_root/img/story-arc.svg.gz +0 -0
  463. codex/static_root/img/volume.814e4c79461a.svg +1 -0
  464. codex/static_root/img/volume.814e4c79461a.svg.br +0 -0
  465. codex/static_root/img/volume.814e4c79461a.svg.gz +0 -0
  466. codex/static_root/img/volume.svg +1 -0
  467. codex/static_root/img/volume.svg.br +0 -0
  468. codex/static_root/img/volume.svg.gz +0 -0
  469. codex/static_root/js/choices-admin.3521e2a863df.json +1 -0
  470. codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
  471. codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
  472. codex/static_root/js/choices-admin.json +1 -1
  473. codex/static_root/js/choices-admin.json.br +0 -0
  474. codex/static_root/js/choices-admin.json.gz +0 -0
  475. codex/static_root/js/choices.2f6b0359e1ef.json +1 -0
  476. codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
  477. codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
  478. codex/static_root/js/choices.json +1 -1
  479. codex/static_root/js/choices.json.br +0 -0
  480. codex/static_root/js/choices.json.gz +0 -0
  481. codex/static_root/manifest.1c84116d9a0a.json +622 -0
  482. codex/static_root/manifest.1c84116d9a0a.json.br +0 -0
  483. codex/static_root/manifest.1c84116d9a0a.json.gz +0 -0
  484. codex/static_root/manifest.json +342 -327
  485. codex/static_root/manifest.json.br +0 -0
  486. codex/static_root/manifest.json.gz +0 -0
  487. codex/static_root/staticfiles.json +1 -1
  488. codex/status_controller.py +11 -2
  489. codex/templates/headers-script-globals.html +1 -4
  490. codex/urls/api/admin.py +5 -7
  491. codex/urls/api/auth.py +2 -1
  492. codex/urls/api/browser.py +18 -9
  493. codex/urls/api/reader.py +5 -11
  494. codex/urls/api/v3.py +4 -0
  495. codex/urls/app.py +8 -3
  496. codex/urls/converters.py +39 -0
  497. codex/urls/opds/binary.py +1 -1
  498. codex/urls/opds/root.py +3 -4
  499. codex/urls/opds/v1.py +1 -1
  500. codex/urls/opds/v2.py +6 -1
  501. codex/urls/root.py +2 -1
  502. codex/util.py +12 -0
  503. codex/views/admin/api_key.py +7 -8
  504. codex/views/admin/auth.py +28 -0
  505. codex/views/admin/flag.py +2 -7
  506. codex/views/admin/group.py +2 -6
  507. codex/views/admin/library.py +44 -37
  508. codex/views/admin/stats.py +20 -83
  509. codex/views/admin/tasks.py +26 -12
  510. codex/views/admin/user.py +3 -14
  511. codex/views/auth.py +39 -64
  512. codex/views/bookmark.py +93 -72
  513. codex/views/browser/annotations.py +293 -0
  514. codex/views/browser/base.py +101 -87
  515. codex/views/browser/breadcrumbs.py +202 -0
  516. codex/views/browser/browser.py +165 -518
  517. codex/views/browser/choices.py +67 -120
  518. codex/views/browser/cover.py +165 -0
  519. codex/views/browser/filters/annotations.py +102 -0
  520. codex/views/browser/filters/bookmark.py +24 -12
  521. codex/views/browser/filters/field.py +8 -6
  522. codex/views/browser/filters/group.py +38 -16
  523. codex/views/browser/filters/search.py +169 -25
  524. codex/views/browser/metadata.py +169 -136
  525. codex/views/browser/mtime.py +61 -0
  526. codex/views/browser/order_by.py +65 -0
  527. codex/views/browser/paginate.py +106 -0
  528. codex/views/browser/settings.py +66 -0
  529. codex/views/browser/title.py +48 -0
  530. codex/views/browser/validate.py +161 -0
  531. codex/views/const.py +157 -0
  532. codex/views/download.py +8 -20
  533. codex/views/error.py +2 -2
  534. codex/views/frontend.py +8 -3
  535. codex/views/mixins.py +76 -0
  536. codex/views/opds/auth.py +17 -0
  537. codex/views/opds/authentication_v1.py +46 -45
  538. codex/views/opds/binary.py +5 -10
  539. codex/views/opds/const.py +13 -0
  540. codex/views/opds/urls.py +30 -0
  541. codex/views/opds/util.py +11 -19
  542. codex/views/opds/v1/entry/data.py +2 -2
  543. codex/views/opds/v1/entry/entry.py +5 -5
  544. codex/views/opds/v1/entry/links.py +68 -58
  545. codex/views/opds/v1/facets.py +11 -11
  546. codex/views/opds/v1/feed.py +84 -63
  547. codex/views/opds/v1/links.py +21 -19
  548. codex/views/opds/v1/opensearch_v1.py +5 -6
  549. codex/views/opds/v2/feed.py +69 -53
  550. codex/views/opds/v2/links.py +4 -2
  551. codex/views/opds/v2/publications.py +50 -68
  552. codex/views/opds/v2/top_links.py +4 -4
  553. codex/views/public.py +36 -0
  554. codex/views/reader/arcs.py +92 -0
  555. codex/views/reader/books.py +181 -0
  556. codex/views/reader/init.py +125 -0
  557. codex/views/reader/page.py +16 -5
  558. codex/views/reader/reader.py +22 -236
  559. codex/views/reader/{session.py → settings.py} +4 -2
  560. codex/views/session.py +92 -102
  561. codex/views/settings.py +47 -0
  562. codex/views/template.py +3 -3
  563. codex/views/util.py +77 -0
  564. codex/views/version.py +2 -2
  565. codex/websockets/consumers.py +14 -10
  566. {codex-1.5.17.dist-info → codex-1.6.0.dist-info}/METADATA +36 -18
  567. codex-1.6.0.dist-info/RECORD +1013 -0
  568. codex/db_functions.py +0 -25
  569. codex/img/missing-cover.webp +0 -0
  570. codex/librarian/importer/update_comics.py +0 -90
  571. codex/serializers/metadata.py +0 -15
  572. codex/static_root/assets/VCheckbox-CFqm2IhY.60c7775eda9e.js +0 -1
  573. codex/static_root/assets/VCheckbox-CFqm2IhY.60c7775eda9e.js.br +0 -0
  574. codex/static_root/assets/VCheckbox-CFqm2IhY.60c7775eda9e.js.gz +0 -0
  575. codex/static_root/assets/VCheckbox-CFqm2IhY.js +0 -1
  576. codex/static_root/assets/VCheckbox-CFqm2IhY.js.br +0 -0
  577. codex/static_root/assets/VCheckbox-CFqm2IhY.js.gz +0 -0
  578. codex/static_root/assets/VCheckboxBtn-BjWW08N8.bee4c9c1c3b4.js +0 -1
  579. codex/static_root/assets/VCheckboxBtn-BjWW08N8.bee4c9c1c3b4.js.br +0 -0
  580. codex/static_root/assets/VCheckboxBtn-BjWW08N8.bee4c9c1c3b4.js.gz +0 -0
  581. codex/static_root/assets/VCheckboxBtn-BjWW08N8.js +0 -1
  582. codex/static_root/assets/VCheckboxBtn-BjWW08N8.js.br +0 -0
  583. codex/static_root/assets/VCheckboxBtn-BjWW08N8.js.gz +0 -0
  584. codex/static_root/assets/VCombobox-BiHR4LaF.7487536b173b.js +0 -1
  585. codex/static_root/assets/VCombobox-BiHR4LaF.7487536b173b.js.br +0 -0
  586. codex/static_root/assets/VCombobox-BiHR4LaF.7487536b173b.js.gz +0 -0
  587. codex/static_root/assets/VCombobox-BiHR4LaF.js +0 -1
  588. codex/static_root/assets/VCombobox-BiHR4LaF.js.br +0 -0
  589. codex/static_root/assets/VCombobox-BiHR4LaF.js.gz +0 -0
  590. codex/static_root/assets/VDataTableVirtual--XFLD9jr.ecfcb691279b.js +0 -1
  591. codex/static_root/assets/VDataTableVirtual--XFLD9jr.ecfcb691279b.js.br +0 -0
  592. codex/static_root/assets/VDataTableVirtual--XFLD9jr.ecfcb691279b.js.gz +0 -0
  593. codex/static_root/assets/VDataTableVirtual--XFLD9jr.js +0 -1
  594. codex/static_root/assets/VDataTableVirtual--XFLD9jr.js.br +0 -0
  595. codex/static_root/assets/VDataTableVirtual--XFLD9jr.js.gz +0 -0
  596. codex/static_root/assets/VDataTableVirtual-CSBxS0yr.825a0ca38f9d.css +0 -1
  597. codex/static_root/assets/VDataTableVirtual-CSBxS0yr.825a0ca38f9d.css.br +0 -0
  598. codex/static_root/assets/VDataTableVirtual-CSBxS0yr.825a0ca38f9d.css.gz +0 -0
  599. codex/static_root/assets/VDataTableVirtual-CSBxS0yr.css +0 -1
  600. codex/static_root/assets/VDataTableVirtual-CSBxS0yr.css.br +0 -0
  601. codex/static_root/assets/VDataTableVirtual-CSBxS0yr.css.gz +0 -0
  602. codex/static_root/assets/VExpansionPanel-CSSnZFhG.bb54c6635f82.js +0 -1
  603. codex/static_root/assets/VExpansionPanel-CSSnZFhG.bb54c6635f82.js.br +0 -0
  604. codex/static_root/assets/VExpansionPanel-CSSnZFhG.bb54c6635f82.js.gz +0 -0
  605. codex/static_root/assets/VExpansionPanel-CSSnZFhG.js +0 -1
  606. codex/static_root/assets/VExpansionPanel-CSSnZFhG.js.br +0 -0
  607. codex/static_root/assets/VExpansionPanel-CSSnZFhG.js.gz +0 -0
  608. codex/static_root/assets/VMain-BJLquw4_.0d36762696df.js +0 -1
  609. codex/static_root/assets/VMain-BJLquw4_.0d36762696df.js.br +0 -0
  610. codex/static_root/assets/VMain-BJLquw4_.0d36762696df.js.gz +0 -0
  611. codex/static_root/assets/VMain-BJLquw4_.js +0 -1
  612. codex/static_root/assets/VMain-BJLquw4_.js.br +0 -0
  613. codex/static_root/assets/VMain-BJLquw4_.js.gz +0 -0
  614. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css +0 -1
  615. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css.br +0 -0
  616. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css.gz +0 -0
  617. codex/static_root/assets/VMain-Byt37V4c.css +0 -1
  618. codex/static_root/assets/VMain-Byt37V4c.css.br +0 -0
  619. codex/static_root/assets/VMain-Byt37V4c.css.gz +0 -0
  620. codex/static_root/assets/VRadioGroup-BrO5lLMK.60b07cdd1b52.js +0 -1
  621. codex/static_root/assets/VRadioGroup-BrO5lLMK.60b07cdd1b52.js.br +0 -0
  622. codex/static_root/assets/VRadioGroup-BrO5lLMK.60b07cdd1b52.js.gz +0 -0
  623. codex/static_root/assets/VRadioGroup-BrO5lLMK.js +0 -1
  624. codex/static_root/assets/VRadioGroup-BrO5lLMK.js.br +0 -0
  625. codex/static_root/assets/VRadioGroup-BrO5lLMK.js.gz +0 -0
  626. codex/static_root/assets/VSelect-BdGop3V2.604c1785fbff.css +0 -1
  627. codex/static_root/assets/VSelect-BdGop3V2.604c1785fbff.css.br +0 -0
  628. codex/static_root/assets/VSelect-BdGop3V2.604c1785fbff.css.gz +0 -0
  629. codex/static_root/assets/VSelect-BdGop3V2.css +0 -1
  630. codex/static_root/assets/VSelect-BdGop3V2.css.br +0 -0
  631. codex/static_root/assets/VSelect-BdGop3V2.css.gz +0 -0
  632. codex/static_root/assets/VSelect-CAEbdtAK.6d93a3800567.js +0 -1
  633. codex/static_root/assets/VSelect-CAEbdtAK.6d93a3800567.js.br +0 -0
  634. codex/static_root/assets/VSelect-CAEbdtAK.6d93a3800567.js.gz +0 -0
  635. codex/static_root/assets/VSelect-CAEbdtAK.js +0 -1
  636. codex/static_root/assets/VSelect-CAEbdtAK.js.br +0 -0
  637. codex/static_root/assets/VSelect-CAEbdtAK.js.gz +0 -0
  638. codex/static_root/assets/VSelectionControl-BoyRbIct.7cfcfa9ae20b.js +0 -1
  639. codex/static_root/assets/VSelectionControl-BoyRbIct.7cfcfa9ae20b.js.br +0 -0
  640. codex/static_root/assets/VSelectionControl-BoyRbIct.7cfcfa9ae20b.js.gz +0 -0
  641. codex/static_root/assets/VSelectionControl-BoyRbIct.js +0 -1
  642. codex/static_root/assets/VSelectionControl-BoyRbIct.js.br +0 -0
  643. codex/static_root/assets/VSelectionControl-BoyRbIct.js.gz +0 -0
  644. codex/static_root/assets/VSlideGroup-C3lY91uv.css +0 -1
  645. codex/static_root/assets/VSlideGroup-C3lY91uv.css.br +0 -0
  646. codex/static_root/assets/VSlideGroup-C3lY91uv.css.gz +0 -0
  647. codex/static_root/assets/VSlideGroup-C3lY91uv.d268067bd078.css +0 -1
  648. codex/static_root/assets/VSlideGroup-C3lY91uv.d268067bd078.css.br +0 -0
  649. codex/static_root/assets/VSlideGroup-C3lY91uv.d268067bd078.css.gz +0 -0
  650. codex/static_root/assets/VSlideGroup-IIebYuJm.d50fd68270e6.js +0 -1
  651. codex/static_root/assets/VSlideGroup-IIebYuJm.d50fd68270e6.js.br +0 -0
  652. codex/static_root/assets/VSlideGroup-IIebYuJm.d50fd68270e6.js.gz +0 -0
  653. codex/static_root/assets/VSlideGroup-IIebYuJm.js +0 -1
  654. codex/static_root/assets/VSlideGroup-IIebYuJm.js.br +0 -0
  655. codex/static_root/assets/VSlideGroup-IIebYuJm.js.gz +0 -0
  656. codex/static_root/assets/VTable-XNBBnB2q.b2784736062a.js.br +0 -0
  657. codex/static_root/assets/VTable-XNBBnB2q.b2784736062a.js.gz +0 -0
  658. codex/static_root/assets/VTable-XNBBnB2q.js.br +0 -0
  659. codex/static_root/assets/VTable-XNBBnB2q.js.gz +0 -0
  660. codex/static_root/assets/VToolbar-CLYsoOKq.acca0b6c0684.js +0 -2
  661. codex/static_root/assets/VToolbar-CLYsoOKq.acca0b6c0684.js.br +0 -0
  662. codex/static_root/assets/VToolbar-CLYsoOKq.acca0b6c0684.js.gz +0 -0
  663. codex/static_root/assets/VToolbar-CLYsoOKq.js +0 -2
  664. codex/static_root/assets/VToolbar-CLYsoOKq.js.br +0 -0
  665. codex/static_root/assets/VToolbar-CLYsoOKq.js.gz +0 -0
  666. codex/static_root/assets/VToolbar-DqQLg1UA.614d6a4a8a09.css +0 -1
  667. codex/static_root/assets/VToolbar-DqQLg1UA.614d6a4a8a09.css.br +0 -0
  668. codex/static_root/assets/VToolbar-DqQLg1UA.614d6a4a8a09.css.gz +0 -0
  669. codex/static_root/assets/VToolbar-DqQLg1UA.css +0 -1
  670. codex/static_root/assets/VToolbar-DqQLg1UA.css.br +0 -0
  671. codex/static_root/assets/VToolbar-DqQLg1UA.css.gz +0 -0
  672. codex/static_root/assets/VWindowItem-CPevuSn_.27817d5bd8e4.js +0 -1
  673. codex/static_root/assets/VWindowItem-CPevuSn_.27817d5bd8e4.js.br +0 -0
  674. codex/static_root/assets/VWindowItem-CPevuSn_.27817d5bd8e4.js.gz +0 -0
  675. codex/static_root/assets/VWindowItem-CPevuSn_.js +0 -1
  676. codex/static_root/assets/VWindowItem-CPevuSn_.js.br +0 -0
  677. codex/static_root/assets/VWindowItem-CPevuSn_.js.gz +0 -0
  678. codex/static_root/assets/admin-CzWbM7Nl.95cf937f31ff.js +0 -1
  679. codex/static_root/assets/admin-CzWbM7Nl.95cf937f31ff.js.br +0 -0
  680. codex/static_root/assets/admin-CzWbM7Nl.95cf937f31ff.js.gz +0 -0
  681. codex/static_root/assets/admin-CzWbM7Nl.js +0 -1
  682. codex/static_root/assets/admin-CzWbM7Nl.js.br +0 -0
  683. codex/static_root/assets/admin-CzWbM7Nl.js.gz +0 -0
  684. codex/static_root/assets/admin-Df82-Qcj.4c3f0406ee54.css +0 -1
  685. codex/static_root/assets/admin-Df82-Qcj.4c3f0406ee54.css.br +0 -0
  686. codex/static_root/assets/admin-Df82-Qcj.4c3f0406ee54.css.gz +0 -0
  687. codex/static_root/assets/admin-Df82-Qcj.css +0 -1
  688. codex/static_root/assets/admin-Df82-Qcj.css.br +0 -0
  689. codex/static_root/assets/admin-Df82-Qcj.css.gz +0 -0
  690. codex/static_root/assets/admin-drawer-panel-B4-04ET4.ff74b52952d0.js +0 -30
  691. codex/static_root/assets/admin-drawer-panel-B4-04ET4.ff74b52952d0.js.br +0 -0
  692. codex/static_root/assets/admin-drawer-panel-B4-04ET4.ff74b52952d0.js.gz +0 -0
  693. codex/static_root/assets/admin-drawer-panel-B4-04ET4.js +0 -30
  694. codex/static_root/assets/admin-drawer-panel-B4-04ET4.js.br +0 -0
  695. codex/static_root/assets/admin-drawer-panel-B4-04ET4.js.gz +0 -0
  696. codex/static_root/assets/admin-drawer-panel-Cr-0BCQv.b519336cb005.css +0 -1
  697. codex/static_root/assets/admin-drawer-panel-Cr-0BCQv.b519336cb005.css.br +0 -0
  698. codex/static_root/assets/admin-drawer-panel-Cr-0BCQv.b519336cb005.css.gz +0 -0
  699. codex/static_root/assets/admin-drawer-panel-Cr-0BCQv.css +0 -1
  700. codex/static_root/assets/admin-drawer-panel-Cr-0BCQv.css.br +0 -0
  701. codex/static_root/assets/admin-drawer-panel-Cr-0BCQv.css.gz +0 -0
  702. codex/static_root/assets/browser-BldK4CGS.ce8c71552de0.js +0 -1
  703. codex/static_root/assets/browser-BldK4CGS.ce8c71552de0.js.br +0 -0
  704. codex/static_root/assets/browser-BldK4CGS.ce8c71552de0.js.gz +0 -0
  705. codex/static_root/assets/browser-BldK4CGS.js +0 -1
  706. codex/static_root/assets/browser-BldK4CGS.js.br +0 -0
  707. codex/static_root/assets/browser-BldK4CGS.js.gz +0 -0
  708. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css +0 -1
  709. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css.br +0 -0
  710. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css.gz +0 -0
  711. codex/static_root/assets/browser-HtKThxA9.css +0 -1
  712. codex/static_root/assets/browser-HtKThxA9.css.br +0 -0
  713. codex/static_root/assets/browser-HtKThxA9.css.gz +0 -0
  714. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css +0 -1
  715. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css.br +0 -0
  716. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css.gz +0 -0
  717. codex/static_root/assets/change-password-dialog-BRmvkNir.css +0 -1
  718. codex/static_root/assets/change-password-dialog-BRmvkNir.css.br +0 -0
  719. codex/static_root/assets/change-password-dialog-BRmvkNir.css.gz +0 -0
  720. codex/static_root/assets/change-password-dialog-DAvSz_BT.89752c597186.js +0 -1
  721. codex/static_root/assets/change-password-dialog-DAvSz_BT.89752c597186.js.br +0 -0
  722. codex/static_root/assets/change-password-dialog-DAvSz_BT.89752c597186.js.gz +0 -0
  723. codex/static_root/assets/change-password-dialog-DAvSz_BT.js +0 -1
  724. codex/static_root/assets/change-password-dialog-DAvSz_BT.js.br +0 -0
  725. codex/static_root/assets/change-password-dialog-DAvSz_BT.js.gz +0 -0
  726. codex/static_root/assets/confirm-dialog-0mqMQack.a2b39e1d65f7.js +0 -1
  727. codex/static_root/assets/confirm-dialog-0mqMQack.a2b39e1d65f7.js.br +0 -0
  728. codex/static_root/assets/confirm-dialog-0mqMQack.a2b39e1d65f7.js.gz +0 -0
  729. codex/static_root/assets/confirm-dialog-0mqMQack.js +0 -1
  730. codex/static_root/assets/confirm-dialog-0mqMQack.js.br +0 -0
  731. codex/static_root/assets/confirm-dialog-0mqMQack.js.gz +0 -0
  732. codex/static_root/assets/confirm-footer-VFepxcJs.4150131304bd.css +0 -1
  733. codex/static_root/assets/confirm-footer-VFepxcJs.4150131304bd.css.br +0 -0
  734. codex/static_root/assets/confirm-footer-VFepxcJs.4150131304bd.css.gz +0 -0
  735. codex/static_root/assets/confirm-footer-VFepxcJs.css +0 -1
  736. codex/static_root/assets/confirm-footer-VFepxcJs.css.br +0 -0
  737. codex/static_root/assets/confirm-footer-VFepxcJs.css.gz +0 -0
  738. codex/static_root/assets/confirm-footer-oTUv06_v.99f172061491.js +0 -1
  739. codex/static_root/assets/confirm-footer-oTUv06_v.99f172061491.js.br +0 -0
  740. codex/static_root/assets/confirm-footer-oTUv06_v.99f172061491.js.gz +0 -0
  741. codex/static_root/assets/confirm-footer-oTUv06_v.js +0 -1
  742. codex/static_root/assets/confirm-footer-oTUv06_v.js.br +0 -0
  743. codex/static_root/assets/confirm-footer-oTUv06_v.js.gz +0 -0
  744. codex/static_root/assets/datetime-column-BWWfvyhn.a4a94ba57c73.css +0 -1
  745. codex/static_root/assets/datetime-column-BWWfvyhn.a4a94ba57c73.css.br +0 -0
  746. codex/static_root/assets/datetime-column-BWWfvyhn.css +0 -1
  747. codex/static_root/assets/datetime-column-BWWfvyhn.css.br +0 -0
  748. codex/static_root/assets/datetime-column-D3I95kw3.26791f31ac02.js +0 -1
  749. codex/static_root/assets/datetime-column-D3I95kw3.26791f31ac02.js.br +0 -0
  750. codex/static_root/assets/datetime-column-D3I95kw3.26791f31ac02.js.gz +0 -0
  751. codex/static_root/assets/datetime-column-D3I95kw3.js +0 -1
  752. codex/static_root/assets/datetime-column-D3I95kw3.js.br +0 -0
  753. codex/static_root/assets/datetime-column-D3I95kw3.js.gz +0 -0
  754. codex/static_root/assets/filter-D95Y3XYa.b69f5d543997.js +0 -1
  755. codex/static_root/assets/filter-D95Y3XYa.b69f5d543997.js.br +0 -0
  756. codex/static_root/assets/filter-D95Y3XYa.b69f5d543997.js.gz +0 -0
  757. codex/static_root/assets/filter-D95Y3XYa.js +0 -1
  758. codex/static_root/assets/filter-D95Y3XYa.js.br +0 -0
  759. codex/static_root/assets/filter-D95Y3XYa.js.gz +0 -0
  760. codex/static_root/assets/flag-tab-C9vu256Y.bd43f22c727b.js +0 -1
  761. codex/static_root/assets/flag-tab-C9vu256Y.bd43f22c727b.js.br +0 -0
  762. codex/static_root/assets/flag-tab-C9vu256Y.bd43f22c727b.js.gz +0 -0
  763. codex/static_root/assets/flag-tab-C9vu256Y.js +0 -1
  764. codex/static_root/assets/flag-tab-C9vu256Y.js.br +0 -0
  765. codex/static_root/assets/flag-tab-C9vu256Y.js.gz +0 -0
  766. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css +0 -1
  767. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css.br +0 -0
  768. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css.gz +0 -0
  769. codex/static_root/assets/flag-tab-D_HjkAta.css +0 -1
  770. codex/static_root/assets/flag-tab-D_HjkAta.css.br +0 -0
  771. codex/static_root/assets/flag-tab-D_HjkAta.css.gz +0 -0
  772. codex/static_root/assets/group-tab-DKqBg4yc.22ea16c1e331.js +0 -1
  773. codex/static_root/assets/group-tab-DKqBg4yc.22ea16c1e331.js.br +0 -0
  774. codex/static_root/assets/group-tab-DKqBg4yc.22ea16c1e331.js.gz +0 -0
  775. codex/static_root/assets/group-tab-DKqBg4yc.js +0 -1
  776. codex/static_root/assets/group-tab-DKqBg4yc.js.br +0 -0
  777. codex/static_root/assets/group-tab-DKqBg4yc.js.gz +0 -0
  778. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css +0 -1
  779. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css.br +0 -0
  780. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css.gz +0 -0
  781. codex/static_root/assets/group-tab-GFacGljN.css +0 -1
  782. codex/static_root/assets/group-tab-GFacGljN.css.br +0 -0
  783. codex/static_root/assets/group-tab-GFacGljN.css.gz +0 -0
  784. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css +0 -1
  785. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css.br +0 -0
  786. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css.gz +0 -0
  787. codex/static_root/assets/http-error-C25WdjWC.css +0 -1
  788. codex/static_root/assets/http-error-C25WdjWC.css.br +0 -0
  789. codex/static_root/assets/http-error-C25WdjWC.css.gz +0 -0
  790. codex/static_root/assets/http-error-F_AIFiVP.1d3b95f22256.js +0 -1
  791. codex/static_root/assets/http-error-F_AIFiVP.1d3b95f22256.js.br +0 -0
  792. codex/static_root/assets/http-error-F_AIFiVP.1d3b95f22256.js.gz +0 -0
  793. codex/static_root/assets/http-error-F_AIFiVP.js +0 -1
  794. codex/static_root/assets/http-error-F_AIFiVP.js.br +0 -0
  795. codex/static_root/assets/http-error-F_AIFiVP.js.gz +0 -0
  796. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +0 -1
  797. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
  798. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
  799. codex/static_root/assets/index-DUjeNg4x.js +0 -1
  800. codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
  801. codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
  802. codex/static_root/assets/library-tab-CKYjhIWV.a1119d2cb417.js +0 -1
  803. codex/static_root/assets/library-tab-CKYjhIWV.a1119d2cb417.js.br +0 -0
  804. codex/static_root/assets/library-tab-CKYjhIWV.a1119d2cb417.js.gz +0 -0
  805. codex/static_root/assets/library-tab-CKYjhIWV.js +0 -1
  806. codex/static_root/assets/library-tab-CKYjhIWV.js.br +0 -0
  807. codex/static_root/assets/library-tab-CKYjhIWV.js.gz +0 -0
  808. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css +0 -1
  809. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css.br +0 -3
  810. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css.gz +0 -0
  811. codex/static_root/assets/library-tab-DZKDtQ5v.css +0 -1
  812. codex/static_root/assets/library-tab-DZKDtQ5v.css.br +0 -3
  813. codex/static_root/assets/library-tab-DZKDtQ5v.css.gz +0 -0
  814. codex/static_root/assets/main-BJxn-WA7.a04c671bd796.js +0 -32
  815. codex/static_root/assets/main-BJxn-WA7.a04c671bd796.js.br +0 -0
  816. codex/static_root/assets/main-BJxn-WA7.a04c671bd796.js.gz +0 -0
  817. codex/static_root/assets/main-BJxn-WA7.js +0 -32
  818. codex/static_root/assets/main-BJxn-WA7.js.br +0 -0
  819. codex/static_root/assets/main-BJxn-WA7.js.gz +0 -0
  820. codex/static_root/assets/main-ClKzvaJK.04ea1888c6b4.css.br +0 -0
  821. codex/static_root/assets/main-ClKzvaJK.css.br +0 -0
  822. codex/static_root/assets/metadata-dialog-DNdhw8Y2.21df0d7eff56.js +0 -1
  823. codex/static_root/assets/metadata-dialog-DNdhw8Y2.21df0d7eff56.js.br +0 -0
  824. codex/static_root/assets/metadata-dialog-DNdhw8Y2.21df0d7eff56.js.gz +0 -0
  825. codex/static_root/assets/metadata-dialog-DNdhw8Y2.js +0 -1
  826. codex/static_root/assets/metadata-dialog-DNdhw8Y2.js.br +0 -0
  827. codex/static_root/assets/metadata-dialog-DNdhw8Y2.js.gz +0 -0
  828. codex/static_root/assets/metadata-dialog-DqTRiI3h.ac34b1bcab82.css +0 -1
  829. codex/static_root/assets/metadata-dialog-DqTRiI3h.ac34b1bcab82.css.br +0 -0
  830. codex/static_root/assets/metadata-dialog-DqTRiI3h.ac34b1bcab82.css.gz +0 -0
  831. codex/static_root/assets/metadata-dialog-DqTRiI3h.css +0 -1
  832. codex/static_root/assets/metadata-dialog-DqTRiI3h.css.br +0 -0
  833. codex/static_root/assets/metadata-dialog-DqTRiI3h.css.gz +0 -0
  834. codex/static_root/assets/pdf-doc-DQV6XqAo.38b6ebeec5bc.js +0 -35
  835. codex/static_root/assets/pdf-doc-DQV6XqAo.38b6ebeec5bc.js.br +0 -0
  836. codex/static_root/assets/pdf-doc-DQV6XqAo.38b6ebeec5bc.js.gz +0 -0
  837. codex/static_root/assets/pdf-doc-DQV6XqAo.js +0 -35
  838. codex/static_root/assets/pdf-doc-DQV6XqAo.js.br +0 -0
  839. codex/static_root/assets/pdf-doc-DQV6XqAo.js.gz +0 -0
  840. codex/static_root/assets/reader-BuAYRwBN.3f834951f509.js +0 -2
  841. codex/static_root/assets/reader-BuAYRwBN.3f834951f509.js.br +0 -0
  842. codex/static_root/assets/reader-BuAYRwBN.3f834951f509.js.gz +0 -0
  843. codex/static_root/assets/reader-BuAYRwBN.js +0 -2
  844. codex/static_root/assets/reader-BuAYRwBN.js.br +0 -0
  845. codex/static_root/assets/reader-BuAYRwBN.js.gz +0 -0
  846. codex/static_root/assets/reader-CG6zrLXl.css +0 -1
  847. codex/static_root/assets/reader-CG6zrLXl.css.br +0 -0
  848. codex/static_root/assets/reader-CG6zrLXl.css.gz +0 -0
  849. codex/static_root/assets/reader-CG6zrLXl.ed017042cd53.css +0 -1
  850. codex/static_root/assets/reader-CG6zrLXl.ed017042cd53.css.br +0 -0
  851. codex/static_root/assets/reader-CG6zrLXl.ed017042cd53.css.gz +0 -0
  852. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css +0 -1
  853. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css.br +0 -0
  854. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css.gz +0 -0
  855. codex/static_root/assets/stats-tab-BTZLMBCU.css +0 -1
  856. codex/static_root/assets/stats-tab-BTZLMBCU.css.br +0 -0
  857. codex/static_root/assets/stats-tab-BTZLMBCU.css.gz +0 -0
  858. codex/static_root/assets/stats-tab-wzOFMrgw.ffc62bf814ce.js +0 -1
  859. codex/static_root/assets/stats-tab-wzOFMrgw.ffc62bf814ce.js.br +0 -0
  860. codex/static_root/assets/stats-tab-wzOFMrgw.ffc62bf814ce.js.gz +0 -0
  861. codex/static_root/assets/stats-tab-wzOFMrgw.js +0 -1
  862. codex/static_root/assets/stats-tab-wzOFMrgw.js.br +0 -0
  863. codex/static_root/assets/stats-tab-wzOFMrgw.js.gz +0 -0
  864. codex/static_root/assets/task-tab-DoX9OA37.9bb355f36dbf.css +0 -1
  865. codex/static_root/assets/task-tab-DoX9OA37.9bb355f36dbf.css.gz +0 -0
  866. codex/static_root/assets/task-tab-DoX9OA37.css +0 -1
  867. codex/static_root/assets/task-tab-DoX9OA37.css.gz +0 -0
  868. codex/static_root/assets/task-tab-UYMTSKl-.c37096a07f51.js +0 -1
  869. codex/static_root/assets/task-tab-UYMTSKl-.c37096a07f51.js.br +0 -0
  870. codex/static_root/assets/task-tab-UYMTSKl-.c37096a07f51.js.gz +0 -0
  871. codex/static_root/assets/task-tab-UYMTSKl-.js +0 -1
  872. codex/static_root/assets/task-tab-UYMTSKl-.js.br +0 -0
  873. codex/static_root/assets/task-tab-UYMTSKl-.js.gz +0 -0
  874. codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +0 -1
  875. codex/static_root/assets/to-case-kUm8NPpW.js +0 -1
  876. codex/static_root/assets/user-tab-ByP2OwxN.255023553b2e.js +0 -1
  877. codex/static_root/assets/user-tab-ByP2OwxN.255023553b2e.js.br +0 -0
  878. codex/static_root/assets/user-tab-ByP2OwxN.255023553b2e.js.gz +0 -0
  879. codex/static_root/assets/user-tab-ByP2OwxN.js +0 -1
  880. codex/static_root/assets/user-tab-ByP2OwxN.js.br +0 -0
  881. codex/static_root/assets/user-tab-ByP2OwxN.js.gz +0 -0
  882. codex/static_root/img/logo-32.dbb98f6dfde4.webp +0 -0
  883. codex/static_root/img/logo-maskable-180.201cbde62d55.webp +0 -0
  884. codex/static_root/img/logo-maskable.d618c2d7943f.svg +0 -83
  885. codex/static_root/img/logo-maskable.d618c2d7943f.svg.br +0 -0
  886. codex/static_root/img/logo-maskable.d618c2d7943f.svg.gz +0 -0
  887. codex/static_root/img/logo.3fb129bfbd92.svg +0 -80
  888. codex/static_root/img/logo.3fb129bfbd92.svg.br +0 -0
  889. codex/static_root/img/logo.3fb129bfbd92.svg.gz +0 -0
  890. codex/static_root/img/missing-cover.8c8c1d4f6782.webp +0 -0
  891. codex/static_root/img/missing-cover.webp +0 -0
  892. codex/static_root/img/missing-page.71bb10f7c72a.svg +0 -13
  893. codex/static_root/img/missing-page.71bb10f7c72a.svg.br +0 -0
  894. codex/static_root/img/missing-page.71bb10f7c72a.svg.gz +0 -0
  895. codex/static_root/img/missing-page.svg +0 -13
  896. codex/static_root/img/missing-page.svg.br +0 -0
  897. codex/static_root/img/missing-page.svg.gz +0 -0
  898. codex/static_root/js/choices-admin.bb730e4f26ac.json +0 -1
  899. codex/static_root/js/choices-admin.bb730e4f26ac.json.br +0 -0
  900. codex/static_root/js/choices-admin.bb730e4f26ac.json.gz +0 -0
  901. codex/static_root/js/choices.fedcbd1ec085.json +0 -1
  902. codex/static_root/js/choices.fedcbd1ec085.json.br +0 -0
  903. codex/static_root/js/choices.fedcbd1ec085.json.gz +0 -0
  904. codex/static_root/manifest.d2fc837c9c53.json +0 -607
  905. codex/static_root/manifest.d2fc837c9c53.json.br +0 -0
  906. codex/static_root/manifest.d2fc837c9c53.json.gz +0 -0
  907. codex/views/browser/browser_annotations.py +0 -298
  908. codex/views/browser/browser_order_by.py +0 -108
  909. codex/views/browser/const.py +0 -3
  910. codex/views/browser/session.py +0 -12
  911. codex/views/cover.py +0 -59
  912. codex-1.5.17.dist-info/RECORD +0 -936
  913. /codex/static_root/assets/{VExpansionPanel-DsEXVcCv.20b475255e66.css → VExpansionPanels-DsEXVcCv.20b475255e66.css} +0 -0
  914. /codex/static_root/assets/{VExpansionPanel-DsEXVcCv.20b475255e66.css.br → VExpansionPanels-DsEXVcCv.20b475255e66.css.br} +0 -0
  915. /codex/static_root/assets/{VExpansionPanel-DsEXVcCv.20b475255e66.css.gz → VExpansionPanels-DsEXVcCv.20b475255e66.css.gz} +0 -0
  916. /codex/static_root/assets/{VExpansionPanel-DsEXVcCv.css → VExpansionPanels-DsEXVcCv.css} +0 -0
  917. /codex/static_root/assets/{VExpansionPanel-DsEXVcCv.css.br → VExpansionPanels-DsEXVcCv.css.br} +0 -0
  918. /codex/static_root/assets/{VExpansionPanel-DsEXVcCv.css.gz → VExpansionPanels-DsEXVcCv.css.gz} +0 -0
  919. {codex-1.5.17.dist-info → codex-1.6.0.dist-info}/LICENSE +0 -0
  920. {codex-1.5.17.dist-info → codex-1.6.0.dist-info}/WHEEL +0 -0
  921. {codex-1.5.17.dist-info → codex-1.6.0.dist-info}/entry_points.txt +0 -0
codex/exceptions.py CHANGED
@@ -1,5 +1,9 @@
1
1
  """Special Redirect Error."""
2
2
 
3
+ from collections.abc import Mapping
4
+ from copy import deepcopy
5
+ from pprint import pformat
6
+
3
7
  from django.shortcuts import redirect
4
8
  from django.urls import reverse
5
9
  from django.utils.http import urlencode
@@ -8,7 +12,7 @@ from rest_framework.status import HTTP_303_SEE_OTHER
8
12
 
9
13
  from codex.logger.logging import get_logger
10
14
  from codex.serializers.choices import DEFAULTS
11
- from codex.serializers.redirect import BrowserRedirectSerializer
15
+ from codex.serializers.route import RouteSerializer
12
16
 
13
17
  LOG = get_logger(__name__)
14
18
 
@@ -20,18 +24,29 @@ class SeeOtherRedirectError(APIException):
20
24
  default_code = "redirect"
21
25
  default_detail = "redirect to a valid route"
22
26
 
23
- def __init__(self, detail):
27
+ def __init__(self, detail: Mapping[str, str | dict]):
24
28
  """Create a response to pass to the exception handler."""
25
- LOG.debug(f"redirect {detail.get('reason')}")
26
- serializer = BrowserRedirectSerializer(detail)
27
- self.detail: dict = serializer.data # type: ignore
28
- # super().__init__ converts every type into strings!
29
- # do not use it.
29
+ detail = dict(detail)
30
+ route: dict = detail.get("route", {}) # type: ignore
31
+ params: dict = route.get("params", DEFAULTS["breadcrumbs"][0])
32
+ # Save route in server format for redirect reverse
33
+ params.pop("name", None)
34
+ self.route_kwargs = params
35
+ # Serialize route for detail
36
+ serializer = RouteSerializer(params)
37
+ route = deepcopy(route)
38
+ route["params"] = serializer.data
39
+ detail["route"] = route
40
+ # Pop uneccissary breadcrumbs.
41
+ detail.get("settings", {}).pop("breadcrumbs", None) # type: ignore
42
+ self.detail: Mapping[str, str | Mapping] = detail # type: ignore
43
+ LOG.debug(f"redirect {pformat(detail)}")
44
+ # super().__init__ converts every type into strings! do not use it.
30
45
 
31
46
  def _add_query_params(self, url):
32
47
  """Change OPDS settings like the frontend does with error.detail."""
33
48
  query_params = {}
34
- settings = self.detail.get("settings", {})
49
+ settings: Mapping = self.detail.get("settings", {}) # type: ignore
35
50
  if top_group := settings.get("top_group"):
36
51
  query_params["top_group"] = top_group
37
52
  if order_by := settings.get("order_by"):
@@ -43,9 +58,7 @@ class SeeOtherRedirectError(APIException):
43
58
  def get_response(self, url_name):
44
59
  """Return a Django Redirect Response."""
45
60
  # only used in codex_exception_handler for opds stuff
46
- route = self.detail.get("route", {})
47
- params = route.get("params", DEFAULTS["route"])
48
- url = reverse(url_name, kwargs=params)
61
+ url = reverse(url_name, kwargs=self.route_kwargs)
49
62
  url = self._add_query_params(url)
50
63
 
51
- return redirect(url)
64
+ return redirect(url, permanent=False)
codex/integrity.py CHANGED
@@ -12,12 +12,14 @@ from django.db import DEFAULT_DB_ALIAS, connection, connections
12
12
  from django.db.migrations.executor import MigrationExecutor
13
13
  from django.db.migrations.recorder import MigrationRecorder
14
14
  from django.db.models.functions import Now
15
+ from django.db.models.query import Q
15
16
  from django.db.utils import OperationalError
16
17
 
17
18
  from codex.logger.logging import get_logger
18
19
  from codex.settings.settings import (
19
20
  CODEX_PATH,
20
21
  CONFIG_PATH,
22
+ CUSTOM_COVERS_DIR,
21
23
  DB_PATH,
22
24
  SKIP_INTEGRITY_CHECK,
23
25
  )
@@ -34,6 +36,7 @@ MIGRATION_0011 = "0010_library_groups_and_metadata_changes"
34
36
  MIGRATION_0018 = "0018_rename_userbookmark_bookmark"
35
37
  MIGRATION_0025 = "0025_add_story_arc_number"
36
38
  MIGRATION_0026 = "0026_comicbox_1"
39
+ MIGRATION_0027 = "0027_import_order_and_covers"
37
40
  M2M_NAMES = MappingProxyType(
38
41
  {
39
42
  "Character": "characters",
@@ -51,7 +54,7 @@ SKIP_M2M_CHECKS = MappingProxyType(
51
54
  {"Contributor": MIGRATION_0026, "StoryArcNumber": MIGRATION_0025}
52
55
  )
53
56
  NULL_SET = frozenset({None})
54
- HAVE_LIBRARY_FKS = ("FailedImport", "Folder", "Comic")
57
+ HAVE_LIBRARY_FKS = ("FailedImport", "Folder", "Comic", "CustomCover")
55
58
  GROUP_HOSTS = MappingProxyType(
56
59
  {
57
60
  "Imprint": ("Publisher",),
@@ -60,7 +63,6 @@ GROUP_HOSTS = MappingProxyType(
60
63
  "Comic": ("Publisher", "Imprint", "Series", "Volume"),
61
64
  }
62
65
  )
63
-
64
66
  COMIC_FK_FIELDS = MappingProxyType(
65
67
  {
66
68
  "AgeRating": "age_rating",
@@ -71,7 +73,6 @@ COMIC_FK_FIELDS = MappingProxyType(
71
73
  "Tagger": "tagger",
72
74
  }
73
75
  )
74
-
75
76
  WATCHED_PATHS = ("Comic", "Folder")
76
77
  CONTRIBUTOR_FIELDS = MappingProxyType(
77
78
  {"ContributorRole": "role", "ContributorPerson": "person"}
@@ -92,6 +93,9 @@ DELETE_BAD_COMIC_FOLDER_RELATIONS_SQL = (
92
93
  'IN (SELECT "codex_folder"."id" FROM "codex_folder")))'
93
94
  )
94
95
  MIGRATION_DIR = CODEX_PATH / "migrations"
96
+ CUSTOM_COVER_MODEL_NAMES = MappingProxyType(
97
+ {"publisher": "p", "imprint": "i", "series": "s", "storyarc": "a", "folder": "f"}
98
+ )
95
99
 
96
100
  LOG = get_logger(__name__)
97
101
 
@@ -303,9 +307,67 @@ def _repair_library_groups(apps):
303
307
  bad_relations.delete()
304
308
 
305
309
 
310
+ def _delete_extra_custom_cover_libraries(apps):
311
+ if not has_applied_migration(MIGRATION_0027):
312
+ return
313
+ library_model = apps.get_model("codex", "library")
314
+ custom_cover_libraries = library_model.objects.filter(covers_only=True)
315
+ if custom_cover_libraries.count() <= 1:
316
+ return
317
+
318
+ # Attempt to remove the bad ones, probably futile
319
+ bad_lib_pks = []
320
+ for library in custom_cover_libraries:
321
+ if library.path != CUSTOM_COVERS_DIR:
322
+ bad_lib_pks.append(library.pk)
323
+ delete_libs = library_model.object.filter(pk__in=bad_lib_pks)
324
+ count = delete_libs.count()
325
+ if count:
326
+ delete_libs.delete()
327
+ LOG.warn(
328
+ f"Removed {count} duplicate custom cover libraries pointing to unused custom cover dirs."
329
+ )
330
+
331
+ custom_cover_libraries = library_model.object.filter(covers_only=True)
332
+ count = custom_cover_libraries.count()
333
+ if count <= 1:
334
+ return
335
+ custom_cover_libraries.delete()
336
+ LOG.warn(
337
+ f"Removed all ({count}) custom cover libraries, Unable to determine valid one. Will recreate upon startup."
338
+ )
339
+
340
+
341
+ def _repair_groups_with_custom_covers(apps):
342
+ if not has_applied_migration(MIGRATION_0027):
343
+ return
344
+ custom_cover_model = apps.get_model("codex", "customcover")
345
+ for model_name, group_letter in CUSTOM_COVER_MODEL_NAMES.items():
346
+ valid_covers = custom_cover_model.objects.filter(group=group_letter)
347
+ group_model = apps.get_model("codex", model_name)
348
+ objs = group_model.objects.exclude(
349
+ Q(custom_cover__isnull=True) | Q(custom_cover__in=valid_covers)
350
+ )
351
+ update_groups = []
352
+ for group in objs:
353
+ group.custom_cover = None
354
+ group.updated_at = Now()
355
+ update_groups.append(group)
356
+ group_model.objects.bulk_update(update_groups, ["custom_cover"])
357
+ count = len(update_groups)
358
+ if count:
359
+ LOG.warn(f"Removed illegal custom cover links from {count} {model_name}s")
360
+
361
+
306
362
  def _delete_errors():
307
363
  """DELETE things we can't fix."""
364
+ _delete_extra_custom_cover_libraries(apps)
365
+
308
366
  for host_model_name in HAVE_LIBRARY_FKS:
367
+ if host_model_name == "CustomCover" and not has_applied_migration(
368
+ MIGRATION_0027
369
+ ):
370
+ continue
309
371
  _delete_fk_integrity_errors(apps, host_model_name, "Library", "library")
310
372
 
311
373
  for host_model_name in WATCHED_PATHS:
@@ -378,6 +440,7 @@ def _repair_integrity():
378
440
  _mark_comics_with_bad_rels_for_update(comic_model, bad_comic_ids)
379
441
 
380
442
  _repair_library_groups(apps)
443
+ _repair_groups_with_custom_covers(apps)
381
444
 
382
445
 
383
446
  def repair_db():
@@ -11,7 +11,7 @@ from codex.librarian.covers.tasks import (
11
11
  )
12
12
 
13
13
 
14
- class CoverContributorThread(CoverCreateMixin, CoverPurgeMixin):
14
+ class CoverThread(CoverCreateMixin, CoverPurgeMixin):
15
15
  """Create comic covers in it's own thread."""
16
16
 
17
17
  def process_item(self, item):
@@ -22,7 +22,7 @@ class CoverContributorThread(CoverCreateMixin, CoverPurgeMixin):
22
22
  elif isinstance(task, CoverRemoveAllTask):
23
23
  self.purge_all_comic_covers(self.librarian_queue)
24
24
  elif isinstance(task, CoverRemoveTask):
25
- self.purge_comic_covers(task.comic_pks)
25
+ self.purge_comic_covers(task.pks, task.custom)
26
26
  elif isinstance(task, CoverRemoveOrphansTask):
27
27
  self.cleanup_orphan_covers()
28
28
  elif isinstance(task, CoverCreateAllTask):
@@ -1,6 +1,7 @@
1
1
  """Create comic cover paths."""
2
2
 
3
3
  from io import BytesIO
4
+ from pathlib import Path
4
5
  from time import time
5
6
 
6
7
  from comicbox.box import Comicbox
@@ -10,7 +11,7 @@ from PIL import Image
10
11
  from codex.librarian.covers.path import CoverPathMixin
11
12
  from codex.librarian.covers.status import CoverStatusTypes
12
13
  from codex.librarian.covers.tasks import CoverSaveToCache
13
- from codex.models import Comic
14
+ from codex.models import Comic, CustomCover
14
15
  from codex.status import Status
15
16
 
16
17
 
@@ -25,17 +26,17 @@ class CoverCreateMixin(CoverPathMixin):
25
26
  @classmethod
26
27
  def _create_cover_thumbnail(cls, cover_image_data):
27
28
  """Isolate the save thumbnail function for leak detection."""
28
- with BytesIO() as cover_thumb_buffer:
29
- with BytesIO(cover_image_data) as image_io:
30
- with Image.open(image_io) as cover_image:
31
- cover_image.thumbnail(
32
- cls._THUMBNAIL_SIZE,
33
- Image.Resampling.LANCZOS, # type: ignore
34
- reducing_gap=3.0,
35
- )
36
- cover_image.save(cover_thumb_buffer, "WEBP", method=6)
37
- cover_image.close() # extra close for animated sequences
38
- return cover_thumb_buffer.getvalue()
29
+ cover_thumb_buffer = BytesIO()
30
+ with BytesIO(cover_image_data) as image_io:
31
+ with Image.open(image_io) as cover_image:
32
+ cover_image.thumbnail(
33
+ cls._THUMBNAIL_SIZE,
34
+ Image.Resampling.LANCZOS, # type: ignore
35
+ reducing_gap=3.0,
36
+ )
37
+ cover_image.save(cover_thumb_buffer, "WEBP", method=6)
38
+ cover_image.close() # extra close for animated sequences
39
+ return cover_thumb_buffer
39
40
 
40
41
  @classmethod
41
42
  def _get_comic_cover_image(cls, comic_path):
@@ -51,24 +52,37 @@ class CoverCreateMixin(CoverPathMixin):
51
52
  return image_data
52
53
 
53
54
  @classmethod
54
- def create_cover_from_path(cls, pk, cover_path, log, librarian_queue):
55
+ def _get_custom_cover_image(cls, cover_path):
56
+ """Get cover image from image file."""
57
+ with Path(cover_path).open("rb") as f:
58
+ return f.read()
59
+
60
+ @classmethod
61
+ def create_cover_from_path(cls, pk, cover_path, log, librarian_queue, custom=False): # noqa: PLR0913
55
62
  """Create cover for path.
56
63
 
57
64
  Called from views/cover.
58
65
  """
59
- comic_path = None
66
+ db_path = None
60
67
  try:
61
- comic_path = Comic.objects.only("path").get(pk=pk).path
62
- cover_image = cls._get_comic_cover_image(comic_path)
63
- data = cls._create_cover_thumbnail(cover_image)
68
+ model = CustomCover if custom else Comic
69
+ db_path = model.objects.only("path").get(pk=pk).path
70
+ if custom:
71
+ cover_image = cls._get_custom_cover_image(db_path)
72
+ else:
73
+ cover_image = cls._get_comic_cover_image(db_path)
74
+ thumb_buffer = cls._create_cover_thumbnail(cover_image)
75
+ thumb_bytes = thumb_buffer.getvalue()
76
+ thumb_buffer.seek(0)
64
77
  except Exception as exc:
65
- data = b""
66
- comic_str = comic_path if comic_path else f"{pk=}"
67
- log.warning(f"Could not create cover thumbnail for {comic_str}: {exc}")
78
+ thumb_bytes = b""
79
+ thumb_buffer = None
80
+ cover_str = db_path if db_path else f"{pk=}"
81
+ log.warning(f"Could not create cover thumbnail for {cover_str}: {exc}")
68
82
 
69
- task = CoverSaveToCache(cover_path, data)
83
+ task = CoverSaveToCache(cover_path, thumb_bytes)
70
84
  librarian_queue.put(task)
71
- return data
85
+ return thumb_buffer
72
86
 
73
87
  def save_cover_to_cache(self, cover_path, data):
74
88
  """Save cover thumb image to the disk cache."""
@@ -77,11 +91,12 @@ class CoverCreateMixin(CoverPathMixin):
77
91
  with cover_path.open("wb") as cover_file:
78
92
  cover_file.write(data)
79
93
  elif not cover_path.exists():
80
- cover_path.symlink_to(self.MISSING_COVER_PATH)
94
+ # zero length file is code for missing.
95
+ cover_path.touch()
81
96
 
82
- def _bulk_create_comic_covers(self, comic_pks):
97
+ def _bulk_create_comic_covers(self, pks, custom=False):
83
98
  """Create bulk comic covers."""
84
- num_comics = len(comic_pks)
99
+ num_comics = len(pks)
85
100
  if not num_comics:
86
101
  return None
87
102
  status = Status(CoverStatusTypes.CREATE_COVERS, 0, num_comics)
@@ -91,26 +106,33 @@ class CoverCreateMixin(CoverPathMixin):
91
106
  self.status_controller.start(status)
92
107
 
93
108
  # Get comic objects
94
- for pk in comic_pks:
109
+ for pk in pks:
95
110
  # Create all covers.
96
- cover_path = self.get_cover_path(pk)
111
+ cover_path = self.get_cover_path(pk, custom)
97
112
  if cover_path.exists():
98
113
  status.decrement_total()
99
114
  else:
100
115
  # bulk contributor creates covers inline
101
- self.create_cover_from_path(
116
+ data = self.create_cover_from_path(
102
117
  pk, cover_path, self.log, self.librarian_queue
103
118
  )
119
+ if data:
120
+ data.close()
104
121
  status.increment_complete()
105
122
  self.status_controller.update(status)
106
123
 
107
124
  total_elapsed = naturaldelta(time() - start_time)
108
- self.log.info(f"Created {status.complete} comic covers in {total_elapsed}.")
125
+ desc = "custom" if custom else "comic"
126
+ self.log.info(
127
+ f"Created {status.complete} {desc} covers in {total_elapsed}."
128
+ )
109
129
  finally:
110
130
  self.status_controller.finish(status)
111
131
  return status.complete
112
132
 
113
133
  def create_all_covers(self):
114
134
  """Create all covers for all libraries."""
135
+ pks = CustomCover.objects.values_list("pk", flat=True)
136
+ self._bulk_create_comic_covers(pks, True)
115
137
  pks = Comic.objects.values_list("pk", flat=True)
116
- self._bulk_create_comic_covers(pks)
138
+ self._bulk_create_comic_covers(pks, False)
@@ -4,15 +4,15 @@ from pathlib import Path
4
4
 
5
5
  from fnvhash import fnv1a_32
6
6
 
7
- from codex.settings.settings import CODEX_PATH, ROOT_CACHE_PATH
7
+ from codex.settings.settings import ROOT_CACHE_PATH
8
8
  from codex.threads import QueuedThread
9
9
 
10
10
 
11
11
  class CoverPathMixin(QueuedThread):
12
12
  """Path methods for covers."""
13
13
 
14
- COVER_ROOT = ROOT_CACHE_PATH / "covers"
15
- MISSING_COVER_PATH = CODEX_PATH / "img" / "missing-cover.webp"
14
+ COVERS_ROOT = ROOT_CACHE_PATH / "covers"
15
+ CUSTOM_COVERS_ROOT = ROOT_CACHE_PATH / "custom-covers"
16
16
  _HEX_FILL = 8
17
17
  _PATH_STEP = 2
18
18
  _ZFILL = 12
@@ -28,16 +28,17 @@ class CoverPathMixin(QueuedThread):
28
28
  return Path("/".join(parts))
29
29
 
30
30
  @classmethod
31
- def get_cover_path(cls, pk):
31
+ def get_cover_path(cls, pk, custom=False):
32
32
  """Get cover path for comic pk."""
33
33
  cover_path = cls._hex_path(pk)
34
- return cls.COVER_ROOT / cover_path.with_suffix(".webp")
34
+ root = cls.CUSTOM_COVERS_ROOT if custom else cls.COVERS_ROOT
35
+ return root / cover_path.with_suffix(".webp")
35
36
 
36
37
  @classmethod
37
- def get_cover_paths(cls, comic_pks):
38
+ def get_cover_paths(cls, pks, custom=False):
38
39
  """Get cover paths for many comic pks."""
39
40
  cover_paths = set()
40
- for comic_pk in comic_pks:
41
- cover_path = cls.get_cover_path(comic_pk)
41
+ for pk in pks:
42
+ cover_path = cls.get_cover_path(pk, custom)
42
43
  cover_paths.add(cover_path)
43
44
  return cover_paths
@@ -7,7 +7,8 @@ from pathlib import Path
7
7
  from codex.librarian.covers.path import CoverPathMixin
8
8
  from codex.librarian.covers.status import CoverStatusTypes
9
9
  from codex.librarian.notifier.tasks import LIBRARY_CHANGED_TASK
10
- from codex.models import Comic, Timestamp
10
+ from codex.models import Comic
11
+ from codex.models.paths import CustomCover
11
12
  from codex.status import Status
12
13
 
13
14
 
@@ -20,17 +21,17 @@ class CoverPurgeMixin(CoverPathMixin):
20
21
  )
21
22
 
22
23
  @classmethod
23
- def _cleanup_cover_dirs(cls, path):
24
+ def _cleanup_cover_dirs(cls, path, cover_root):
24
25
  """Recursively remove empty cover directories."""
25
- if not path or cls.COVER_ROOT not in path.parents:
26
+ if not path or cover_root not in path.parents:
26
27
  return
27
28
  try:
28
29
  path.rmdir()
29
- cls._cleanup_cover_dirs(path.parent)
30
+ cls._cleanup_cover_dirs(path.parent, cover_root)
30
31
  except OSError:
31
32
  pass
32
33
 
33
- def purge_cover_paths(self, cover_paths):
34
+ def purge_cover_paths(self, cover_paths, cover_root):
34
35
  """Purge a set a cover paths."""
35
36
  self.log.debug(f"Removing {len(cover_paths)} possible cover thumbnails...")
36
37
  status = Status(CoverStatusTypes.PURGE_COVERS, 0, len(cover_paths))
@@ -46,38 +47,40 @@ class CoverPurgeMixin(CoverPathMixin):
46
47
  cover_dirs.add(cover_path.parent)
47
48
  self.status_controller.update(status)
48
49
  for cover_dir in cover_dirs:
49
- self._cleanup_cover_dirs(cover_dir)
50
+ self._cleanup_cover_dirs(cover_dir, cover_root)
50
51
  self.log.info(f"Removed {status.complete} cover thumbnails.")
51
52
  finally:
52
53
  self.status_controller.finish(status)
53
54
  return status.complete
54
55
 
55
- def purge_comic_covers(self, comic_pks):
56
+ def purge_comic_covers(self, pks: frozenset[int], custom: bool):
56
57
  """Purge a set a cover paths."""
57
- cover_paths = self.get_cover_paths(comic_pks)
58
- return self.purge_cover_paths(cover_paths)
58
+ cover_paths = self.get_cover_paths(pks, custom)
59
+ cover_root = self.CUSTOM_COVERS_ROOT if custom else self.COVERS_ROOT
60
+ return self.purge_cover_paths(cover_paths, cover_root)
59
61
 
60
62
  def purge_all_comic_covers(self, librarian_queue):
61
63
  """Purge every comic cover."""
62
64
  self.log.debug("Removing entire comic cover cache.")
63
65
  try:
64
- shutil.rmtree(self.COVER_ROOT)
66
+ shutil.rmtree(self.COVERS_ROOT)
65
67
  self.log.info("Removed entire comic cover cache.")
68
+ shutil.rmtree(self.CUSTOM_COVERS_ROOT)
69
+ self.log.info("Removed entire custom cover cache.")
66
70
  except Exception as exc:
67
71
  self.log.warning(exc)
68
- Timestamp.touch(Timestamp.TimestampChoices.COVERS)
69
72
  librarian_queue.put(LIBRARY_CHANGED_TASK)
70
73
 
71
- def cleanup_orphan_covers(self):
74
+ def _cleanup_orphan_covers(self, cover_class, cover_root, name):
72
75
  """Remove all orphan cover thumbs."""
73
76
  try:
74
- self.log.debug("Removing covers from missing comics.")
77
+ self.log.debug(f"Removing covers from missing {name}.")
75
78
  self.status_controller.start_many(self._CLEANUP_STATUS_MAP)
76
- comic_pks = Comic.objects.all().values_list("pk", flat=True)
77
- db_cover_paths = self.get_cover_paths(comic_pks)
79
+ pks = cover_class.objects.all().values_list("pk", flat=True)
80
+ db_cover_paths = self.get_cover_paths(pks)
78
81
 
79
82
  orphan_cover_paths = set()
80
- for root, _, filenames in os.walk(self.COVER_ROOT):
83
+ for root, _, filenames in os.walk(cover_root):
81
84
  root_path = Path(root)
82
85
  for fn in filenames:
83
86
  fs_cover_path = root_path / fn
@@ -86,6 +89,13 @@ class CoverPurgeMixin(CoverPathMixin):
86
89
  finally:
87
90
  self.status_controller.finish(CoverStatusTypes.FIND_ORPHAN)
88
91
 
89
- count = self.purge_cover_paths(orphan_cover_paths)
90
- self.log.info(f"Removed {count} covers for missing comics.")
92
+ count = self.purge_cover_paths(orphan_cover_paths, cover_root)
93
+ self.log.info(f"Removed {count} covers for missing {name}.")
91
94
  return count
95
+
96
+ def cleanup_orphan_covers(self):
97
+ """Cleanup both comic and custom covers."""
98
+ self._cleanup_orphan_covers(Comic, self.COVERS_ROOT, "comics")
99
+ self._cleanup_orphan_covers(
100
+ CustomCover, self.CUSTOM_COVERS_ROOT, "custom covers"
101
+ )
@@ -5,7 +5,7 @@ from dataclasses import dataclass
5
5
 
6
6
  @dataclass
7
7
  class CoverTask:
8
- """Handle with the CoverContributor."""
8
+ """Handle with the CoverThread."""
9
9
 
10
10
 
11
11
  @dataclass
@@ -29,7 +29,8 @@ class LibrariesTask:
29
29
  class CoverRemoveTask(CoverTask):
30
30
  """Purge a set of comic covers."""
31
31
 
32
- comic_pks: frozenset
32
+ pks: frozenset
33
+ custom: bool
33
34
 
34
35
 
35
36
  @dataclass