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
@@ -10,6 +10,7 @@ from codex.librarian.importer.const import (
10
10
  DICT_MODEL_REL_LINK_MAP,
11
11
  FOLDERS_FIELD,
12
12
  IMPRINT,
13
+ M2M_MDS,
13
14
  PARENT_FOLDER,
14
15
  PUBLISHER,
15
16
  SERIES,
@@ -17,7 +18,8 @@ from codex.librarian.importer.const import (
17
18
  STORY_ARCS_METADATA_KEY,
18
19
  VOLUME,
19
20
  )
20
- from codex.librarian.importer.status import ImportStatusTypes, status_notify
21
+ from codex.librarian.importer.link_covers import LinkCoversImporter
22
+ from codex.librarian.importer.status import ImportStatusTypes
21
23
  from codex.models import (
22
24
  Comic,
23
25
  Folder,
@@ -27,10 +29,10 @@ from codex.models import (
27
29
  Volume,
28
30
  )
29
31
  from codex.models.named import Identifier
30
- from codex.threads import QueuedThread
32
+ from codex.status import Status
31
33
 
32
34
 
33
- class LinkComicsMixin(QueuedThread):
35
+ class LinkComicsImporter(LinkCoversImporter):
34
36
  """Link comics methods."""
35
37
 
36
38
  @staticmethod
@@ -40,13 +42,12 @@ class LinkComicsMixin(QueuedThread):
40
42
  return md.get(field_name, group_class.DEFAULT_NAME)
41
43
 
42
44
  @classmethod
43
- def get_comic_fk_links(cls, md, library, path):
45
+ def get_comic_fk_links(cls, md, path):
44
46
  """Get links for all foreign keys for creating and updating."""
45
47
  publisher_name = cls._get_group_name(Publisher, md)
46
48
  imprint_name = cls._get_group_name(Imprint, md)
47
49
  series_name = cls._get_group_name(Series, md)
48
50
  volume_name = cls._get_group_name(Volume, md)
49
- md["library"] = library
50
51
  md[PUBLISHER] = Publisher.objects.get(name=publisher_name)
51
52
  md[IMPRINT] = Imprint.objects.get(
52
53
  publisher__name=publisher_name, name=imprint_name
@@ -122,13 +123,15 @@ class LinkComicsMixin(QueuedThread):
122
123
  result = frozenset(pks)
123
124
  all_m2m_links[key][comic_pk] = result
124
125
 
125
- def _link_comic_m2m_fields(self, m2m_mds):
126
+ def _link_comic_m2m_fields(self):
126
127
  """Get the complete m2m field data to create."""
127
128
  all_m2m_links = {}
128
- comic_paths = frozenset(m2m_mds.keys())
129
+ comic_paths = frozenset(self.metadata.get(M2M_MDS, {}).keys())
130
+ if not comic_paths:
131
+ return all_m2m_links
129
132
  comics = Comic.objects.filter(path__in=comic_paths).values_list("pk", "path")
130
133
  for comic_pk, comic_path in comics:
131
- md = m2m_mds[comic_path]
134
+ md = self.metadata[M2M_MDS][comic_path]
132
135
  link_data = (all_m2m_links, md, comic_pk)
133
136
  self._link_prepare_special_m2ms(
134
137
  link_data, FOLDERS_FIELD, Folder, self._get_link_folders_filter
@@ -141,6 +144,7 @@ class LinkComicsMixin(QueuedThread):
141
144
  self._link_prepare_special_m2ms(link_data, field_name, model, method)
142
145
 
143
146
  self._link_named_m2ms(all_m2m_links, comic_pk, md)
147
+ self.metadata.pop(M2M_MDS)
144
148
  return all_m2m_links
145
149
 
146
150
  def _query_relation_adjustments(
@@ -202,10 +206,9 @@ class LinkComicsMixin(QueuedThread):
202
206
  tms, all_del_pks = self._query_relation_adjustments(
203
207
  m2m_links, ThroughModel, through_field_id_name, status
204
208
  )
205
- if status:
206
- status.total = status.total or 0
207
- status.total += len(tms) + len(all_del_pks)
208
- self.status_controller.update(status)
209
+ status.total = status.total or 0
210
+ status.total += len(tms) + len(all_del_pks)
211
+ self.status_controller.update(status)
209
212
 
210
213
  update_fields = ("comic_id", through_field_id_name)
211
214
  ThroughModel.objects.bulk_create(
@@ -216,10 +219,9 @@ class LinkComicsMixin(QueuedThread):
216
219
  )
217
220
  if created_count := len(tms):
218
221
  self.log.info(f"Linked {created_count} new {field_name} to altered comics.")
219
- if status:
220
- status.complete = status.complete or 0
221
- status.complete += created_count
222
- self.status_controller.update(status)
222
+ status.complete = status.complete or 0
223
+ status.complete += created_count
224
+ self.status_controller.update(status)
223
225
 
224
226
  if del_count := len(all_del_pks):
225
227
  del_qs = ThroughModel.objects.filter(pk__in=all_del_pks)
@@ -227,19 +229,16 @@ class LinkComicsMixin(QueuedThread):
227
229
  self.log.info(
228
230
  f"Deleted {del_count} stale {field_name} relations for altered comics.",
229
231
  )
230
- if status:
231
- status.complete = status.complete or 0
232
- status.complete += del_count
233
- self.status_controller.update(status)
232
+ status.complete = status.complete or 0
233
+ status.complete += del_count
234
+ self.status_controller.update(status)
234
235
  return created_count, del_count
235
236
 
236
- @status_notify(status_type=ImportStatusTypes.LINK_M2M_FIELDS, updates=False)
237
- def bulk_query_and_link_comic_m2m_fields(self, all_m2m_mds, status=None):
237
+ def bulk_query_and_link_comic_m2m_fields(self):
238
238
  """Combine query and bulk link into a batch."""
239
- if status:
240
- status.complete = 0
241
- status.total = 0
242
- all_m2m_links = self._link_comic_m2m_fields(all_m2m_mds)
239
+ status = Status(ImportStatusTypes.LINK_M2M_FIELDS)
240
+ self.status_controller.start(status)
241
+ all_m2m_links = self._link_comic_m2m_fields()
243
242
  created_total = 0
244
243
  del_total = 0
245
244
  for field_name, m2m_links in all_m2m_links.items():
@@ -256,4 +255,5 @@ class LinkComicsMixin(QueuedThread):
256
255
  self.log.info(f"Linked {created_total} comics to tags.")
257
256
  if del_total:
258
257
  self.log.info(f"Deleted {del_total} stale relations for altered comics.")
258
+ self.status_controller.finish(status)
259
259
  return created_total + del_total
@@ -0,0 +1,73 @@
1
+ """Link Covers."""
2
+
3
+ from pathlib import Path
4
+
5
+ from codex.librarian.importer.const import CLASS_CUSTOM_COVER_GROUP_MAP, LINK_COVER_PKS
6
+ from codex.librarian.importer.failed_imports import FailedImportsImporter
7
+ from codex.librarian.importer.status import ImportStatusTypes
8
+ from codex.models import CustomCover, Folder
9
+ from codex.status import Status
10
+
11
+
12
+ class LinkCoversImporter(FailedImportsImporter):
13
+ """Link Covers methods."""
14
+
15
+ def _link_custom_cover_prepare(self, cover, model_map):
16
+ """Prepare one cover in the model map for bulk update."""
17
+ if cover.library and cover.library.covers_only:
18
+ model = CLASS_CUSTOM_COVER_GROUP_MAP.inverse.get(cover.group)
19
+ if not model:
20
+ self.log.warning(f"Custom Cover model not found for {cover.path}")
21
+ return
22
+ group_filter = {"sort_name": cover.sort_name}
23
+ else:
24
+ model = Folder
25
+ path = str(Path(cover.path).parent)
26
+ group_filter = {"path": path}
27
+ qs = model.objects.filter(**group_filter).exclude(custom_cover=cover)
28
+ if not qs.exists():
29
+ return
30
+
31
+ if model not in model_map:
32
+ model_map[model] = []
33
+
34
+ for obj in qs.iterator():
35
+ obj.custom_cover = cover
36
+ model_map[model].append(obj)
37
+
38
+ def _link_custom_cover_group(self, model, objs, status):
39
+ """Bulk link a group to it's custom covers."""
40
+ count = 0
41
+ if not objs:
42
+ return count
43
+ model.objects.bulk_update(objs, ["custom_cover"])
44
+ count += len(objs)
45
+ self.log.info(f"Linked {count} custom covers to {model.__name__}s")
46
+ status.complete += count
47
+ self.status_controller.update(status, notify=False)
48
+ return count
49
+
50
+ def link_custom_covers(self):
51
+ """Link Custom Covers to Groups."""
52
+ link_cover_pks = self.metadata.get(LINK_COVER_PKS, {})
53
+ num_link_cover_pks = len(link_cover_pks)
54
+ if not num_link_cover_pks:
55
+ return 0
56
+ status = Status(ImportStatusTypes.COVERS_LINK, 0, num_link_cover_pks)
57
+ try:
58
+ self.status_controller.start(status)
59
+ # Aggregate objs to update for each group model.
60
+ model_map = {}
61
+ covers = CustomCover.objects.filter(pk__in=link_cover_pks).only(
62
+ "library", "path"
63
+ )
64
+ for cover in covers:
65
+ self._link_custom_cover_prepare(cover, model_map)
66
+
67
+ # Bulk update each model type
68
+ total_count = 0
69
+ for model, objs in model_map.items():
70
+ total_count += self._link_custom_cover_group(model, objs, status)
71
+ finally:
72
+ self.status_controller.finish(status)
73
+ return total_count
@@ -4,74 +4,143 @@ from pathlib import Path
4
4
 
5
5
  from django.db.models.functions import Now
6
6
 
7
- from codex.librarian.importer.const import FOLDERS_FIELD, PARENT_FOLDER
8
- from codex.librarian.importer.create_comics import CreateComicsMixin
9
- from codex.librarian.importer.create_fks import (
7
+ from codex.librarian.importer.aggregate import AggregateMetadataImporter
8
+ from codex.librarian.importer.const import (
10
9
  BULK_UPDATE_FOLDER_MODIFIED_FIELDS,
11
- CreateForeignKeysMixin,
10
+ CLASS_CUSTOM_COVER_GROUP_MAP,
11
+ CUSTOM_COVER_UPDATE_FIELDS,
12
+ FOLDERS_FIELD,
13
+ LINK_COVER_PKS,
14
+ MOVED_BULK_COMIC_UPDATE_FIELDS,
15
+ MOVED_BULK_FOLDER_UPDATE_FIELDS,
16
+ PARENT_FOLDER,
12
17
  )
13
- from codex.librarian.importer.query_fks import QueryForeignKeysMixin
14
- from codex.librarian.importer.status import ImportStatusTypes, status_notify
15
- from codex.models import Comic, Folder, Library
18
+ from codex.librarian.importer.status import ImportStatusTypes
19
+ from codex.models import Comic, CustomCover, Folder
20
+ from codex.status import Status
16
21
 
17
- _MOVED_BULK_COMIC_UPDATE_FIELDS = ("path", "parent_folder")
18
- _MOVED_BULK_FOLDER_UPDATE_FIELDS = ("path", "parent_folder", "name")
19
22
 
20
-
21
- class MovedMixin(CreateComicsMixin, CreateForeignKeysMixin, QueryForeignKeysMixin):
23
+ class MovedImporter(AggregateMetadataImporter):
22
24
  """Methods for moving comics and folders."""
23
25
 
24
- @status_notify(status_type=ImportStatusTypes.FILES_MOVED, updates=False)
25
- def _bulk_comics_moved(self, moved_paths, library, status=None):
26
+ def _bulk_comics_moved(self):
26
27
  """Move comcis."""
27
- count = 0
28
- if not moved_paths:
29
- return count
28
+ num_files_moved = len(self.task.files_moved)
29
+ if not num_files_moved:
30
+ return
31
+ status = Status(ImportStatusTypes.FILES_MOVED, 0, num_files_moved)
32
+ self.status_controller.start(status)
30
33
 
31
34
  # Prepare FKs
32
35
  create_folder_paths = set()
33
- self.query_missing_folder_paths(
34
- moved_paths.values(), library.path, create_folder_paths, status=status
35
- )
36
- self.bulk_folders_create(create_folder_paths, library, status=status)
36
+ self.query_missing_folder_paths(create_folder_paths, status)
37
+ self.bulk_folders_create(frozenset(create_folder_paths), status)
37
38
 
38
39
  # Update Comics
39
40
  comics = Comic.objects.filter(
40
- library=library, path__in=moved_paths.keys()
41
+ library=self.library, path__in=self.task.files_moved.keys()
41
42
  ).only("pk", "path", PARENT_FOLDER, FOLDERS_FIELD)
42
43
 
43
44
  folder_m2m_links = {}
44
- now = Now()
45
45
  updated_comics = []
46
46
  for comic in comics.iterator():
47
47
  try:
48
- new_path = moved_paths[comic.path]
48
+ new_path = self.task.files_moved[comic.path]
49
49
  comic.path = new_path
50
50
  new_path = Path(new_path)
51
51
  comic.parent_folder = Folder.objects.get( # type: ignore
52
52
  path=new_path.parent
53
53
  )
54
- comic.updated_at = now
55
- comic.set_stat()
54
+ comic.updated_at = Now()
55
+ comic.presave()
56
56
  folder_m2m_links[comic.pk] = Folder.objects.filter(
57
57
  path__in=new_path.parents
58
58
  ).values_list("pk", flat=True)
59
59
  updated_comics.append(comic)
60
60
  except Exception:
61
61
  self.log.exception(f"moving {comic.path}")
62
+ self.task.files_moved = {}
62
63
 
63
- Comic.objects.bulk_update(updated_comics, _MOVED_BULK_COMIC_UPDATE_FIELDS)
64
+ Comic.objects.bulk_update(updated_comics, MOVED_BULK_COMIC_UPDATE_FIELDS)
64
65
 
65
66
  # Update m2m field
66
67
  if folder_m2m_links:
67
- self.bulk_fix_comic_m2m_field(FOLDERS_FIELD, folder_m2m_links, None)
68
+ self.bulk_fix_comic_m2m_field(FOLDERS_FIELD, folder_m2m_links, status)
68
69
  count = len(comics)
69
70
  if count:
70
71
  self.log.info(f"Moved {count} comics.")
71
72
 
72
- return count
73
+ self.changed += count
74
+ self.status_controller.finish(status)
75
+
76
+ def _bulk_covers_moved_prepare(self, status):
77
+ """Create an update map for bulk update."""
78
+ covers = CustomCover.objects.filter(
79
+ library=self.library, path__in=self.task.covers_moved.keys()
80
+ ).only("pk", "path")
81
+
82
+ if status:
83
+ status.total = covers.count()
84
+
85
+ moved_covers = []
86
+ unlink_pks = set()
87
+ for cover in covers.iterator():
88
+ try:
89
+ new_path = self.task.covers_moved[cover.path]
90
+ cover.path = new_path
91
+ new_path = Path(new_path)
92
+ cover.updated_at = Now()
93
+ cover.presave()
94
+ moved_covers.append(cover)
95
+ unlink_pks.add(cover.pk)
96
+ except Exception:
97
+ self.log.exception(f"moving {cover.path}")
98
+ return moved_covers, unlink_pks
99
+
100
+ def _bulk_covers_moved_unlink(self, unlink_pks):
101
+ """Unlink moved covers because they could have moved between group dirs."""
102
+ if not unlink_pks:
103
+ return
104
+ self.log.debug(f"Unlinking {len(unlink_pks)} moved custom covers.")
105
+ for model in CLASS_CUSTOM_COVER_GROUP_MAP:
106
+ groups = model.objects.filter(custom_cover__in=unlink_pks)
107
+ unlink_groups = []
108
+ for group in groups:
109
+ group.custom_cover = None
110
+ unlink_groups.append(group)
111
+ if unlink_groups:
112
+ model.objects.bulk_update(unlink_groups, ["custom_cover"])
113
+ self.log.debug(
114
+ f"Unlinked {len(unlink_groups)} {model.__name__} moved custom covers."
115
+ )
116
+
117
+ self._remove_covers(unlink_pks, custom=True) # type: ignore
118
+
119
+ def _bulk_covers_moved(self, status=None):
120
+ """Move covers."""
121
+ num_covers_moved = len(self.task.covers_moved)
122
+ if not num_covers_moved:
123
+ return
124
+ status = Status(ImportStatusTypes.COVERS_MOVED, None, num_covers_moved)
125
+ self.status_controller.start(status)
126
+
127
+ moved_covers, unlink_pks = self._bulk_covers_moved_prepare(status)
128
+ if LINK_COVER_PKS not in self.metadata:
129
+ self.metadata[LINK_COVER_PKS] = set()
130
+ self.metadata[LINK_COVER_PKS].update(unlink_pks)
131
+ if moved_covers:
132
+ CustomCover.objects.bulk_update(moved_covers, CUSTOM_COVER_UPDATE_FIELDS)
133
+
134
+ self._bulk_covers_moved_unlink(unlink_pks)
73
135
 
74
- def _get_parent_folders(self, library, dest_folder_paths, status):
136
+ count = len(moved_covers)
137
+ if count:
138
+ self.log.info(f"Moved {count} custom covers.")
139
+
140
+ self.changed += count
141
+ self.status_controller.finish(status)
142
+
143
+ def _get_parent_folders(self, dest_folder_paths, status):
75
144
  """Get destination parent folders."""
76
145
  # Determine parent folder paths.
77
146
  dest_parent_folder_paths = set()
@@ -82,12 +151,12 @@ class MovedMixin(CreateComicsMixin, CreateForeignKeysMixin, QueryForeignKeysMixi
82
151
 
83
152
  # Create only intermediate subfolders.
84
153
  existing_folder_paths = Folder.objects.filter(
85
- library=library, path__in=dest_parent_folder_paths
154
+ library=self.library, path__in=dest_parent_folder_paths
86
155
  ).values_list("path", flat=True)
87
156
  create_folder_paths = frozenset(
88
157
  dest_parent_folder_paths - frozenset(existing_folder_paths)
89
158
  )
90
- self.bulk_folders_create(create_folder_paths, library, status=status)
159
+ self.bulk_folders_create(create_folder_paths, status)
91
160
 
92
161
  # get parent folders path to model obj dict
93
162
  dest_parent_folders_objs = Folder.objects.filter(
@@ -98,93 +167,73 @@ class MovedMixin(CreateComicsMixin, CreateForeignKeysMixin, QueryForeignKeysMixi
98
167
  dest_parent_folders[folder.path] = folder
99
168
  return dest_parent_folders
100
169
 
101
- @status_notify(status_type=ImportStatusTypes.DIRS_MOVED, updates=False)
102
- def _bulk_folders_moved(self, folders_moved, library, **kwargs):
170
+ def bulk_folders_moved(self):
103
171
  """Move folders in the database instead of recreating them."""
104
- if not folders_moved:
105
- return 0
172
+ num_dirs_moved = len(self.task.dirs_moved)
173
+ if not num_dirs_moved:
174
+ return
175
+ status = Status(ImportStatusTypes.DIRS_MOVED, None, num_dirs_moved)
176
+ self.status_controller.start(status)
106
177
 
107
- dest_folder_paths = frozenset(folders_moved.values())
108
- status = kwargs.get("status")
109
- dest_parent_folders = self._get_parent_folders(
110
- library, dest_folder_paths, status
111
- )
178
+ dest_folder_paths = frozenset(self.task.dirs_moved.values())
179
+ dest_parent_folders = self._get_parent_folders(dest_folder_paths, status)
112
180
 
113
- src_folder_paths = frozenset(folders_moved.keys())
181
+ src_folder_paths = frozenset(self.task.dirs_moved.keys())
114
182
  folders_to_move = Folder.objects.filter(
115
- library=library, path__in=src_folder_paths
183
+ library=self.library, path__in=src_folder_paths
116
184
  ).order_by("path")
117
185
 
118
186
  update_folders = []
119
- now = Now()
120
187
  for folder in folders_to_move.iterator():
121
- new_path = folders_moved[folder.path]
188
+ new_path = self.task.dirs_moved[folder.path]
122
189
  folder.name = Path(new_path).name
123
190
  folder.path = new_path
124
191
  parent_path_str = str(Path(new_path).parent)
125
192
  folder.parent_folder = dest_parent_folders.get(parent_path_str)
126
- folder.set_stat()
127
- folder.updated_at = now # type: ignore
193
+ folder.presave()
194
+ folder.updated_at = Now()
128
195
  update_folders.append(folder)
196
+ self.task.dirs_moved = {}
129
197
 
130
198
  update_folders = sorted(update_folders, key=lambda x: len(Path(x.path).parts))
131
199
 
132
- Folder.objects.bulk_update(update_folders, _MOVED_BULK_FOLDER_UPDATE_FIELDS)
200
+ Folder.objects.bulk_update(update_folders, MOVED_BULK_FOLDER_UPDATE_FIELDS)
133
201
  count = len(update_folders)
134
202
  if count:
135
203
  self.log.info(f"Moved {count} folders.")
136
- return count
204
+ self.changed += count
205
+ self.status_controller.finish(status)
137
206
 
138
- @status_notify(status_type=ImportStatusTypes.DIRS_MODIFIED, updates=False)
139
- def bulk_folders_modified(self, paths, library, **kwargs):
207
+ def _bulk_folders_modified(self):
140
208
  """Update folders stat and nothing else."""
141
- count = 0
142
- if not paths:
143
- return count
144
- folders = Folder.objects.filter(library=library, path__in=paths).only(
145
- "stat", "updated_at"
146
- )
209
+ num_dirs_modified = len(self.task.dirs_modified)
210
+ if not num_dirs_modified:
211
+ return
212
+ status = Status(ImportStatusTypes.DIRS_MODIFIED, None, num_dirs_modified)
213
+ self.status_controller.start(status)
214
+
215
+ folders = Folder.objects.filter(
216
+ library=self.library, path__in=self.task.dirs_modified
217
+ ).only("stat", "updated_at")
218
+ self.task.dirs_modified = frozenset()
147
219
  update_folders = []
148
- now = Now()
149
220
  for folder in folders.iterator():
150
221
  if Path(folder.path).exists():
151
- folder.set_stat()
152
- folder.updated_at = now
222
+ folder.updated_at = Now()
223
+ folder.presave()
153
224
  update_folders.append(folder)
154
225
  Folder.objects.bulk_update(
155
226
  update_folders, fields=BULK_UPDATE_FOLDER_MODIFIED_FIELDS
156
227
  )
157
- count += len(update_folders)
228
+ count = len(update_folders)
158
229
  if count:
159
230
  self.log.info(f"Modified {count} folders")
160
- return count
231
+ self.changed += count
232
+ self.status_controller.finish(status)
161
233
 
162
- def adopt_orphan_folders(self):
163
- """Find orphan folders and move them into their correct place."""
164
- libraries = Library.objects.only("pk", "path")
165
- for library in libraries.iterator():
166
- orphan_folder_paths = (
167
- Folder.objects.filter(library=library, parent_folder=None)
168
- .exclude(path=library.path)
169
- .values_list("path", flat=True)
170
- )
171
-
172
- folders_moved = {}
173
- for path in orphan_folder_paths:
174
- folders_moved[path] = path
175
-
176
- self._bulk_folders_moved(folders_moved, library)
177
-
178
- def move_and_modify_dirs(self, library, task):
234
+ def move_and_modify_dirs(self):
179
235
  """Move files and dirs and modify dirs."""
180
- changed = 0
181
- changed += self._bulk_folders_moved(task.dirs_moved, library)
182
- task.dirs_moved = None
183
-
184
- changed += self._bulk_comics_moved(task.files_moved, library)
185
- task.files_moved = None
186
-
187
- changed += self.bulk_folders_modified(task.dirs_modified, library)
188
- task.dirs_modified = None
189
-
190
- return changed
236
+ self.bulk_folders_moved()
237
+ self._bulk_comics_moved()
238
+ self._bulk_covers_moved()
239
+ self._bulk_folders_modified()
@@ -0,0 +1,52 @@
1
+ """Query Missing Custom Covers."""
2
+
3
+ from codex.librarian.importer.const import (
4
+ COVERS_CREATE,
5
+ COVERS_UPDATE,
6
+ )
7
+ from codex.librarian.importer.create_fks import CreateForeignKeysImporter
8
+ from codex.librarian.importer.status import ImportStatusTypes
9
+ from codex.logger.logging import get_logger
10
+ from codex.models.paths import CustomCover
11
+ from codex.status import Status
12
+
13
+ LOG = get_logger(__name__)
14
+
15
+
16
+ class QueryCustomCoversImporter(CreateForeignKeysImporter):
17
+ """Query Missing Custom Covers."""
18
+
19
+ def query_missing_custom_covers(self):
20
+ """Identify update & create covers."""
21
+ cover_paths = self.task.covers_created | self.task.covers_modified
22
+ num_cover_paths = len(cover_paths)
23
+ if not num_cover_paths:
24
+ return
25
+ self.log.debug(f"Querying {num_cover_paths} custom cover_paths")
26
+ status = Status(ImportStatusTypes.QUERY_MISSING_COVERS, None, num_cover_paths)
27
+ self.status_controller.start(status)
28
+
29
+ update_covers_qs = CustomCover.objects.filter(
30
+ library=self.library, path__in=cover_paths
31
+ )
32
+ self.metadata[COVERS_UPDATE] = update_covers_qs
33
+ update_cover_paths = frozenset(update_covers_qs.values_list("path", flat=True))
34
+ update_count = len(update_cover_paths)
35
+ update_status = Status(ImportStatusTypes.COVERS_MODIFIED, 0, update_count)
36
+ self.status_controller.update(update_status, notify=False)
37
+
38
+ create_cover_paths = cover_paths - update_cover_paths
39
+ self.metadata[COVERS_CREATE] = create_cover_paths
40
+ create_count = len(create_cover_paths)
41
+ create_status = Status(ImportStatusTypes.COVERS_CREATED, 0, create_count)
42
+ self.status_controller.update(create_status, notify=False)
43
+
44
+ self.task.covers_created = self.task.covers_modified = frozenset()
45
+
46
+ count = create_count + update_count
47
+ if count:
48
+ self.log.debug(
49
+ f"Discovered {update_count} custom covers to update and {create_count} to create."
50
+ )
51
+ self.status_controller.finish(status)
52
+ return