codex 1.6.0a4__py3-none-any.whl → 1.6.0a5__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 (554) hide show
  1. codex/exceptions.py +17 -6
  2. codex/integrity.py +67 -3
  3. codex/librarian/covers/coverd.py +2 -2
  4. codex/librarian/covers/create.py +29 -13
  5. codex/librarian/covers/path.py +8 -6
  6. codex/librarian/covers/purge.py +27 -16
  7. codex/librarian/covers/tasks.py +3 -2
  8. codex/librarian/importer/aggregate_metadata.py +2 -2
  9. codex/librarian/importer/const.py +23 -3
  10. codex/librarian/importer/create_fks.py +88 -28
  11. codex/librarian/importer/deleted.py +26 -2
  12. codex/librarian/importer/importerd.py +124 -27
  13. codex/librarian/importer/link_comics.py +55 -0
  14. codex/librarian/importer/moved.py +78 -3
  15. codex/librarian/importer/query_fks.py +65 -1
  16. codex/librarian/importer/status.py +6 -0
  17. codex/librarian/importer/tasks.py +11 -3
  18. codex/librarian/importer/update_comics.py +1 -3
  19. codex/librarian/janitor/cleanup.py +24 -0
  20. codex/librarian/janitor/failed_imports.py +1 -1
  21. codex/librarian/janitor/janitor.py +5 -0
  22. codex/librarian/janitor/janitord.py +1 -1
  23. codex/librarian/janitor/status.py +1 -0
  24. codex/librarian/janitor/tasks.py +5 -0
  25. codex/librarian/librariand.py +3 -3
  26. codex/librarian/search/update.py +1 -1
  27. codex/librarian/watchdog/db_snapshot.py +9 -6
  28. codex/librarian/watchdog/emitter.py +3 -0
  29. codex/librarian/watchdog/event_batcherd.py +19 -3
  30. codex/librarian/watchdog/events.py +176 -36
  31. codex/librarian/watchdog/observers.py +13 -3
  32. codex/migrations/0027_sort_name.py +2 -12
  33. codex/migrations/0028_custom_covers.py +166 -0
  34. codex/models/admin.py +0 -1
  35. codex/models/base.py +3 -0
  36. codex/models/comic.py +15 -9
  37. codex/models/groups.py +30 -14
  38. codex/models/library.py +11 -3
  39. codex/models/paths.py +62 -14
  40. codex/models/{const.py → util.py} +15 -2
  41. codex/serializers/admin.py +11 -2
  42. codex/serializers/browser/mixins.py +1 -0
  43. codex/serializers/browser/settings.py +3 -0
  44. codex/settings/settings.py +11 -5
  45. codex/startup.py +77 -3
  46. codex/static_root/assets/{VCheckbox-CN6Sn_k7.9bf27129091c.js → VCheckbox-BF_99tgB.7fe59e56d9d4.js} +1 -1
  47. codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js.br +0 -0
  48. codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js.gz +0 -0
  49. codex/static_root/assets/{VCheckbox-CN6Sn_k7.js → VCheckbox-BF_99tgB.js} +1 -1
  50. codex/static_root/assets/VCheckbox-BF_99tgB.js.br +0 -0
  51. codex/static_root/assets/VCheckbox-BF_99tgB.js.gz +0 -0
  52. codex/static_root/assets/{VCheckboxBtn-CN1RPZPQ.b763c6a7abaf.js → VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js} +1 -1
  53. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js.br +0 -0
  54. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js.gz +0 -0
  55. codex/static_root/assets/{VCheckboxBtn-CN1RPZPQ.js → VCheckboxBtn-Coc7g8m7.js} +1 -1
  56. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js.br +0 -0
  57. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js.gz +0 -0
  58. codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js +1 -0
  59. codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js.br +0 -0
  60. codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js.gz +0 -0
  61. codex/static_root/assets/VCombobox-CTRWnOuM.js +1 -0
  62. codex/static_root/assets/VCombobox-CTRWnOuM.js.br +0 -0
  63. codex/static_root/assets/VCombobox-CTRWnOuM.js.gz +0 -0
  64. codex/static_root/assets/{VDialog-DC3KGsMT.71e2a460e44b.js → VDialog-pzwzInEh.51acf566a069.js} +1 -1
  65. codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js.br +0 -0
  66. codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js.gz +0 -0
  67. codex/static_root/assets/{VDialog-DC3KGsMT.js → VDialog-pzwzInEh.js} +1 -1
  68. codex/static_root/assets/VDialog-pzwzInEh.js.br +0 -0
  69. codex/static_root/assets/VDialog-pzwzInEh.js.gz +0 -0
  70. codex/static_root/assets/{VExpansionPanels-DJQYXCn2.f0d471a1b80a.js → VExpansionPanels-CKSq1GpG.0ecf702c5b67.js} +1 -1
  71. codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js.br +0 -0
  72. codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js.gz +0 -0
  73. codex/static_root/assets/{VExpansionPanels-DJQYXCn2.js → VExpansionPanels-CKSq1GpG.js} +1 -1
  74. codex/static_root/assets/VExpansionPanels-CKSq1GpG.js.br +0 -0
  75. codex/static_root/assets/VExpansionPanels-CKSq1GpG.js.gz +0 -0
  76. codex/static_root/assets/{VRadioGroup-CgodTThy.b20290d57146.js → VRadioGroup-CW5GobGU.b72925bca3de.js} +1 -1
  77. codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js.br +0 -0
  78. codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js.gz +0 -0
  79. codex/static_root/assets/{VRadioGroup-CgodTThy.js → VRadioGroup-CW5GobGU.js} +1 -1
  80. codex/static_root/assets/VRadioGroup-CW5GobGU.js.br +0 -0
  81. codex/static_root/assets/VRadioGroup-CW5GobGU.js.gz +0 -0
  82. codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js +1 -0
  83. codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js.br +0 -0
  84. codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js.gz +0 -0
  85. codex/static_root/assets/VSelect-ChOlLRyH.js +1 -0
  86. codex/static_root/assets/VSelect-ChOlLRyH.js.br +0 -0
  87. codex/static_root/assets/VSelect-ChOlLRyH.js.gz +0 -0
  88. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css +1 -0
  89. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.br +0 -0
  90. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.gz +0 -0
  91. codex/static_root/assets/VSelect-MGVSeLgr.css +1 -0
  92. codex/static_root/assets/VSelect-MGVSeLgr.css.br +0 -0
  93. codex/static_root/assets/VSelect-MGVSeLgr.css.gz +0 -0
  94. codex/static_root/assets/{VSelectionControl-zPp4DXWq.635b7f5aa882.js → VSelectionControl-DKOhc2zB.061dca094303.js} +1 -1
  95. codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js.br +0 -0
  96. codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js.gz +0 -0
  97. codex/static_root/assets/{VSelectionControl-zPp4DXWq.js → VSelectionControl-DKOhc2zB.js} +1 -1
  98. codex/static_root/assets/VSelectionControl-DKOhc2zB.js.br +0 -0
  99. codex/static_root/assets/VSelectionControl-DKOhc2zB.js.gz +0 -0
  100. codex/static_root/assets/{VSlideGroup-pK9Xompk.67014b86fc74.js → VSlideGroup-zFz5_byd.e0e246b7acbb.js} +1 -1
  101. codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js.br +0 -0
  102. codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js.gz +0 -0
  103. codex/static_root/assets/{VSlideGroup-pK9Xompk.js → VSlideGroup-zFz5_byd.js} +1 -1
  104. codex/static_root/assets/VSlideGroup-zFz5_byd.js.br +0 -0
  105. codex/static_root/assets/VSlideGroup-zFz5_byd.js.gz +0 -0
  106. codex/static_root/assets/{VTable-CMi6sDtP.8445f91d9023.js → VTable-FKMTcdkF.6c1eed84d25e.js} +1 -1
  107. codex/static_root/assets/VTable-FKMTcdkF.6c1eed84d25e.js.br +0 -0
  108. codex/static_root/assets/VTable-FKMTcdkF.6c1eed84d25e.js.gz +0 -0
  109. codex/static_root/assets/{VTable-CMi6sDtP.js → VTable-FKMTcdkF.js} +1 -1
  110. codex/static_root/assets/VTable-FKMTcdkF.js.br +0 -0
  111. codex/static_root/assets/VTable-FKMTcdkF.js.gz +0 -0
  112. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +1 -0
  113. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
  114. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
  115. codex/static_root/assets/VTextField-BssVoDhB.css +1 -0
  116. codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
  117. codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
  118. codex/static_root/assets/VTextField-Dftcik85.cb2f1dda87e1.js +1 -0
  119. codex/static_root/assets/VTextField-Dftcik85.cb2f1dda87e1.js.br +0 -0
  120. codex/static_root/assets/VTextField-Dftcik85.cb2f1dda87e1.js.gz +0 -0
  121. codex/static_root/assets/VTextField-Dftcik85.js +1 -0
  122. codex/static_root/assets/VTextField-Dftcik85.js.br +0 -0
  123. codex/static_root/assets/VTextField-Dftcik85.js.gz +0 -0
  124. codex/static_root/assets/{VWindowItem-DNsRQx9l.c42f636c386e.js → VWindowItem-CYCwnt3s.28025dc36c6a.js} +1 -1
  125. codex/static_root/assets/VWindowItem-CYCwnt3s.28025dc36c6a.js.br +0 -0
  126. codex/static_root/assets/VWindowItem-CYCwnt3s.28025dc36c6a.js.gz +0 -0
  127. codex/static_root/assets/{VWindowItem-DNsRQx9l.js → VWindowItem-CYCwnt3s.js} +1 -1
  128. codex/static_root/assets/VWindowItem-CYCwnt3s.js.br +0 -0
  129. codex/static_root/assets/VWindowItem-CYCwnt3s.js.gz +0 -0
  130. codex/static_root/assets/{admin-BbrOU9Y9.29936748af9e.css → admin-B8Bp2Uih.82c98714bcf1.css} +1 -1
  131. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
  132. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
  133. codex/static_root/assets/{admin-BbrOU9Y9.css → admin-B8Bp2Uih.css} +1 -1
  134. codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
  135. codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
  136. codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js +1 -0
  137. codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js.br +0 -0
  138. codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js.gz +0 -0
  139. codex/static_root/assets/admin-DH_DrB0F.js +1 -0
  140. codex/static_root/assets/admin-DH_DrB0F.js.br +0 -0
  141. codex/static_root/assets/admin-DH_DrB0F.js.gz +0 -0
  142. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js +30 -0
  143. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js.br +0 -0
  144. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js.gz +0 -0
  145. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js +30 -0
  146. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js.br +0 -0
  147. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js.gz +0 -0
  148. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css +1 -0
  149. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.br +0 -0
  150. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.gz +0 -0
  151. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css +1 -0
  152. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css.br +0 -0
  153. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css.gz +0 -0
  154. codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css +1 -0
  155. codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css.br +0 -0
  156. codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css.gz +0 -0
  157. codex/static_root/assets/browser-CpppBvim.css +1 -0
  158. codex/static_root/assets/browser-CpppBvim.css.br +0 -0
  159. codex/static_root/assets/browser-CpppBvim.css.gz +0 -0
  160. codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js +1 -0
  161. codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js.br +0 -0
  162. codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js.gz +0 -0
  163. codex/static_root/assets/browser-lKU7rqLd.js +1 -0
  164. codex/static_root/assets/browser-lKU7rqLd.js.br +0 -0
  165. codex/static_root/assets/browser-lKU7rqLd.js.gz +0 -0
  166. codex/static_root/assets/{change-password-dialog-BHTkpC6Z.2d5d5dd57ad3.js → change-password-dialog-BIBG6Bk8.3583021c124a.js} +1 -1
  167. codex/static_root/assets/change-password-dialog-BIBG6Bk8.3583021c124a.js.br +0 -0
  168. codex/static_root/assets/change-password-dialog-BIBG6Bk8.3583021c124a.js.gz +0 -0
  169. codex/static_root/assets/{change-password-dialog-BHTkpC6Z.js → change-password-dialog-BIBG6Bk8.js} +1 -1
  170. codex/static_root/assets/change-password-dialog-BIBG6Bk8.js.br +0 -0
  171. codex/static_root/assets/change-password-dialog-BIBG6Bk8.js.gz +0 -0
  172. codex/static_root/assets/{confirm-dialog-DmXGDg4z.284812bd376d.js → confirm-dialog-DARLmXRf.53fc6938fb8c.js} +1 -1
  173. codex/static_root/assets/confirm-dialog-DARLmXRf.53fc6938fb8c.js.br +0 -0
  174. codex/static_root/assets/confirm-dialog-DARLmXRf.53fc6938fb8c.js.gz +0 -0
  175. codex/static_root/assets/{confirm-dialog-DmXGDg4z.js → confirm-dialog-DARLmXRf.js} +1 -1
  176. codex/static_root/assets/confirm-dialog-DARLmXRf.js.br +0 -0
  177. codex/static_root/assets/confirm-dialog-DARLmXRf.js.gz +0 -0
  178. codex/static_root/assets/{datetime-column-DKaH5S3z.5c6042016200.js → datetime-column-LpLgWpkf.16ce3324970f.js} +1 -1
  179. codex/static_root/assets/datetime-column-LpLgWpkf.16ce3324970f.js.br +0 -0
  180. codex/static_root/assets/datetime-column-LpLgWpkf.16ce3324970f.js.gz +0 -0
  181. codex/static_root/assets/{datetime-column-DKaH5S3z.js → datetime-column-LpLgWpkf.js} +1 -1
  182. codex/static_root/assets/datetime-column-LpLgWpkf.js.br +0 -0
  183. codex/static_root/assets/datetime-column-LpLgWpkf.js.gz +0 -0
  184. codex/static_root/assets/{filter-DUVATQW3.e1e7aa6d0b18.js → filter-jRB1Ob6r.6a559a53e805.js} +1 -1
  185. codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js.br +0 -0
  186. codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js.gz +0 -0
  187. codex/static_root/assets/{filter-DUVATQW3.js → filter-jRB1Ob6r.js} +1 -1
  188. codex/static_root/assets/filter-jRB1Ob6r.js.br +0 -0
  189. codex/static_root/assets/filter-jRB1Ob6r.js.gz +0 -0
  190. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +1 -0
  191. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
  192. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
  193. codex/static_root/assets/flag-tab-BNwbLfrN.css +1 -0
  194. codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
  195. codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
  196. codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js +1 -0
  197. codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js.br +0 -0
  198. codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js.gz +0 -0
  199. codex/static_root/assets/flag-tab-mYS2wg5e.js +1 -0
  200. codex/static_root/assets/flag-tab-mYS2wg5e.js.br +0 -0
  201. codex/static_root/assets/flag-tab-mYS2wg5e.js.gz +0 -0
  202. codex/static_root/assets/{group-tab-D3CyU-Il.fbf9ce5ef593.js → group-tab-MK1lcjUU.98406d5babdc.js} +1 -1
  203. codex/static_root/assets/group-tab-MK1lcjUU.98406d5babdc.js.br +0 -0
  204. codex/static_root/assets/group-tab-MK1lcjUU.98406d5babdc.js.gz +0 -0
  205. codex/static_root/assets/{group-tab-D3CyU-Il.js → group-tab-MK1lcjUU.js} +1 -1
  206. codex/static_root/assets/group-tab-MK1lcjUU.js.br +0 -0
  207. codex/static_root/assets/group-tab-MK1lcjUU.js.gz +0 -0
  208. codex/static_root/assets/{http-error-0ef6-Ajc.e2879bcd242f.js → http-error-HFeYnF0n.a3c578a0265e.js} +1 -1
  209. codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js.br +0 -0
  210. codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js.gz +0 -0
  211. codex/static_root/assets/{http-error-0ef6-Ajc.js → http-error-HFeYnF0n.js} +1 -1
  212. codex/static_root/assets/http-error-HFeYnF0n.js.br +0 -0
  213. codex/static_root/assets/http-error-HFeYnF0n.js.gz +0 -0
  214. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +1 -0
  215. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
  216. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
  217. codex/static_root/assets/library-tab-BN0SfPcH.css +1 -0
  218. codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
  219. codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
  220. codex/static_root/assets/library-tab-DYeEBYlT.a2a2ef088d1c.js +1 -0
  221. codex/static_root/assets/library-tab-DYeEBYlT.a2a2ef088d1c.js.br +0 -0
  222. codex/static_root/assets/library-tab-DYeEBYlT.a2a2ef088d1c.js.gz +0 -0
  223. codex/static_root/assets/library-tab-DYeEBYlT.js +1 -0
  224. codex/static_root/assets/library-tab-DYeEBYlT.js.br +0 -0
  225. codex/static_root/assets/library-tab-DYeEBYlT.js.gz +0 -0
  226. codex/static_root/assets/{main-CuzJev9T.js → main-gSW7TFDY.329223b148d4.js} +6 -6
  227. codex/static_root/assets/main-gSW7TFDY.329223b148d4.js.br +0 -0
  228. codex/static_root/assets/main-gSW7TFDY.329223b148d4.js.gz +0 -0
  229. codex/static_root/assets/{main-CuzJev9T.f58771cc37e2.js → main-gSW7TFDY.js} +6 -6
  230. codex/static_root/assets/main-gSW7TFDY.js.br +0 -0
  231. codex/static_root/assets/main-gSW7TFDY.js.gz +0 -0
  232. codex/static_root/assets/{pagination-toolbar-B3wS4_7p.7695fafdefce.css → pagination-toolbar-ClZui9Zf.697d7ae11c21.css} +1 -1
  233. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.br +0 -0
  234. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.gz +0 -0
  235. codex/static_root/assets/{pagination-toolbar-B3wS4_7p.css → pagination-toolbar-ClZui9Zf.css} +1 -1
  236. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.br +0 -0
  237. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.gz +0 -0
  238. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js +1 -0
  239. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js.br +0 -0
  240. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js.gz +0 -0
  241. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js +1 -0
  242. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js.br +0 -0
  243. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js.gz +0 -0
  244. codex/static_root/assets/{pdf-doc-BlU5mm5n.0431bcdf48d1.js → pdf-doc-BTdS9hUz.7c966524280a.js} +1 -1
  245. codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js.br +0 -0
  246. codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js.gz +0 -0
  247. codex/static_root/assets/{pdf-doc-BlU5mm5n.js → pdf-doc-BTdS9hUz.js} +1 -1
  248. codex/static_root/assets/pdf-doc-BTdS9hUz.js.br +0 -0
  249. codex/static_root/assets/pdf-doc-BTdS9hUz.js.gz +0 -0
  250. codex/static_root/assets/{reader-s7jttB_F.f7b43b04961a.js → reader-BtSTIETq.f940f66c0e58.js} +2 -2
  251. codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js.br +0 -0
  252. codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js.gz +0 -0
  253. codex/static_root/assets/{reader-s7jttB_F.js → reader-BtSTIETq.js} +2 -2
  254. codex/static_root/assets/reader-BtSTIETq.js.br +0 -0
  255. codex/static_root/assets/reader-BtSTIETq.js.gz +0 -0
  256. codex/static_root/assets/{reader-BiKxuxj7.css → reader-CF3xt6JG.19fad7f62450.css} +1 -1
  257. codex/static_root/assets/reader-CF3xt6JG.19fad7f62450.css.br +0 -0
  258. codex/static_root/assets/reader-CF3xt6JG.19fad7f62450.css.gz +0 -0
  259. codex/static_root/assets/{reader-BiKxuxj7.dabe592f46f1.css → reader-CF3xt6JG.css} +1 -1
  260. codex/static_root/assets/reader-CF3xt6JG.css.br +0 -0
  261. codex/static_root/assets/reader-CF3xt6JG.css.gz +0 -0
  262. codex/static_root/assets/{relation-chips-D6T6FX7G.css → relation-chips-Bsc-gUxE.98e65384c3c9.css} +1 -1
  263. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
  264. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
  265. codex/static_root/assets/{relation-chips-D6T6FX7G.db095b2497e4.css → relation-chips-Bsc-gUxE.css} +1 -1
  266. codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
  267. codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
  268. codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js +1 -0
  269. codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js.br +0 -0
  270. codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js.gz +0 -0
  271. codex/static_root/assets/relation-chips-C4Ob7ySi.js +1 -0
  272. codex/static_root/assets/relation-chips-C4Ob7ySi.js.br +0 -0
  273. codex/static_root/assets/relation-chips-C4Ob7ySi.js.gz +0 -0
  274. codex/static_root/assets/{settings-drawer-DbJuixzh.352aaaab5925.js → settings-drawer-xSctoxZn.4ce8afcb879a.js} +2 -2
  275. codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js.br +0 -0
  276. codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js.gz +0 -0
  277. codex/static_root/assets/{settings-drawer-DbJuixzh.js → settings-drawer-xSctoxZn.js} +2 -2
  278. codex/static_root/assets/settings-drawer-xSctoxZn.js.br +0 -0
  279. codex/static_root/assets/settings-drawer-xSctoxZn.js.gz +0 -0
  280. codex/static_root/assets/{stats-tab-BhRLh7XB.7be95b5021c3.js → stats-tab-C_uC_U7o.e6b014b6e8c6.js} +1 -1
  281. codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js.br +0 -0
  282. codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js.gz +0 -0
  283. codex/static_root/assets/{stats-tab-BhRLh7XB.js → stats-tab-C_uC_U7o.js} +1 -1
  284. codex/static_root/assets/stats-tab-C_uC_U7o.js.br +0 -0
  285. codex/static_root/assets/stats-tab-C_uC_U7o.js.gz +0 -0
  286. codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js +1 -0
  287. codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js.br +0 -0
  288. codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js.gz +0 -0
  289. codex/static_root/assets/task-tab-VIpy1Ula.js +1 -0
  290. codex/static_root/assets/task-tab-VIpy1Ula.js.br +0 -0
  291. codex/static_root/assets/task-tab-VIpy1Ula.js.gz +0 -0
  292. codex/static_root/assets/{unauthorized-BgXQnNpn.f63b36d526b5.js → unauthorized-kEuFL5br.475729603aeb.js} +1 -1
  293. codex/static_root/assets/unauthorized-kEuFL5br.475729603aeb.js.br +0 -0
  294. codex/static_root/assets/unauthorized-kEuFL5br.475729603aeb.js.gz +0 -0
  295. codex/static_root/assets/{unauthorized-BgXQnNpn.js → unauthorized-kEuFL5br.js} +1 -1
  296. codex/static_root/assets/unauthorized-kEuFL5br.js.br +0 -0
  297. codex/static_root/assets/unauthorized-kEuFL5br.js.gz +0 -0
  298. codex/static_root/assets/{user-tab-cZoJDlb-.2a497e8b2b24.js → user-tab-CZNFTTEI.f3940a75daae.js} +1 -1
  299. codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js.br +0 -0
  300. codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js.gz +0 -0
  301. codex/static_root/assets/{user-tab-cZoJDlb-.js → user-tab-CZNFTTEI.js} +1 -1
  302. codex/static_root/assets/user-tab-CZNFTTEI.js.br +0 -0
  303. codex/static_root/assets/user-tab-CZNFTTEI.js.gz +0 -0
  304. codex/static_root/js/choices-admin.ef1ff3a8b9da.json +1 -0
  305. codex/static_root/js/choices-admin.ef1ff3a8b9da.json.br +0 -0
  306. codex/static_root/js/choices-admin.ef1ff3a8b9da.json.gz +0 -0
  307. codex/static_root/js/choices-admin.json +1 -1
  308. codex/static_root/js/choices-admin.json.br +0 -0
  309. codex/static_root/js/choices-admin.json.gz +0 -0
  310. codex/static_root/js/{choices.b60817ba0a93.json → choices.23fedac2b9ba.json} +1 -1
  311. codex/static_root/js/choices.23fedac2b9ba.json.br +0 -0
  312. codex/static_root/js/choices.23fedac2b9ba.json.gz +0 -0
  313. codex/static_root/js/choices.json +1 -1
  314. codex/static_root/js/choices.json.br +0 -0
  315. codex/static_root/js/choices.json.gz +0 -0
  316. codex/static_root/manifest.6ef112683dae.json +642 -0
  317. codex/static_root/manifest.6ef112683dae.json.br +0 -0
  318. codex/static_root/manifest.6ef112683dae.json.gz +0 -0
  319. codex/static_root/manifest.json +254 -254
  320. codex/static_root/manifest.json.br +0 -0
  321. codex/static_root/manifest.json.gz +0 -0
  322. codex/static_root/staticfiles.json +1 -1
  323. codex/status_controller.py +11 -2
  324. codex/views/admin/library.py +47 -27
  325. codex/views/admin/stats.py +4 -1
  326. codex/views/admin/tasks.py +6 -1
  327. codex/views/browser/base.py +2 -2
  328. codex/views/browser/browser.py +1 -2
  329. codex/views/browser/browser_annotations.py +87 -17
  330. codex/views/browser/browser_breadcrumbs.py +2 -2
  331. codex/views/browser/browser_order_by.py +31 -20
  332. codex/views/browser/filters/search.py +1 -1
  333. codex/views/browser/metadata.py +0 -1
  334. codex/views/cover.py +3 -2
  335. codex/views/session.py +1 -0
  336. {codex-1.6.0a4.dist-info → codex-1.6.0a5.dist-info}/METADATA +3 -2
  337. {codex-1.6.0a4.dist-info → codex-1.6.0a5.dist-info}/RECORD +340 -339
  338. codex/static_root/assets/VCheckbox-CN6Sn_k7.9bf27129091c.js.br +0 -0
  339. codex/static_root/assets/VCheckbox-CN6Sn_k7.9bf27129091c.js.gz +0 -0
  340. codex/static_root/assets/VCheckbox-CN6Sn_k7.js.br +0 -0
  341. codex/static_root/assets/VCheckbox-CN6Sn_k7.js.gz +0 -0
  342. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.b763c6a7abaf.js.br +0 -0
  343. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.b763c6a7abaf.js.gz +0 -0
  344. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.js.br +0 -0
  345. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.js.gz +0 -0
  346. codex/static_root/assets/VCombobox-CtLUGYtD.6e49097cea2c.js +0 -1
  347. codex/static_root/assets/VCombobox-CtLUGYtD.6e49097cea2c.js.br +0 -0
  348. codex/static_root/assets/VCombobox-CtLUGYtD.6e49097cea2c.js.gz +0 -0
  349. codex/static_root/assets/VCombobox-CtLUGYtD.js +0 -1
  350. codex/static_root/assets/VCombobox-CtLUGYtD.js.br +0 -0
  351. codex/static_root/assets/VCombobox-CtLUGYtD.js.gz +0 -0
  352. codex/static_root/assets/VDialog-DC3KGsMT.71e2a460e44b.js.br +0 -0
  353. codex/static_root/assets/VDialog-DC3KGsMT.71e2a460e44b.js.gz +0 -0
  354. codex/static_root/assets/VDialog-DC3KGsMT.js.br +0 -0
  355. codex/static_root/assets/VDialog-DC3KGsMT.js.gz +0 -0
  356. codex/static_root/assets/VExpansionPanels-DJQYXCn2.f0d471a1b80a.js.br +0 -0
  357. codex/static_root/assets/VExpansionPanels-DJQYXCn2.f0d471a1b80a.js.gz +0 -0
  358. codex/static_root/assets/VExpansionPanels-DJQYXCn2.js.br +0 -0
  359. codex/static_root/assets/VExpansionPanels-DJQYXCn2.js.gz +0 -0
  360. codex/static_root/assets/VRadioGroup-CgodTThy.b20290d57146.js.br +0 -0
  361. codex/static_root/assets/VRadioGroup-CgodTThy.b20290d57146.js.gz +0 -0
  362. codex/static_root/assets/VRadioGroup-CgodTThy.js.br +0 -0
  363. codex/static_root/assets/VRadioGroup-CgodTThy.js.gz +0 -0
  364. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +0 -1
  365. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
  366. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
  367. codex/static_root/assets/VSelect-ARDhJiQK.css +0 -1
  368. codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
  369. codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
  370. codex/static_root/assets/VSelect-B6s-x36h.de1030c82785.js +0 -1
  371. codex/static_root/assets/VSelect-B6s-x36h.de1030c82785.js.br +0 -0
  372. codex/static_root/assets/VSelect-B6s-x36h.de1030c82785.js.gz +0 -0
  373. codex/static_root/assets/VSelect-B6s-x36h.js +0 -1
  374. codex/static_root/assets/VSelect-B6s-x36h.js.br +0 -0
  375. codex/static_root/assets/VSelect-B6s-x36h.js.gz +0 -0
  376. codex/static_root/assets/VSelectionControl-zPp4DXWq.635b7f5aa882.js.br +0 -0
  377. codex/static_root/assets/VSelectionControl-zPp4DXWq.635b7f5aa882.js.gz +0 -0
  378. codex/static_root/assets/VSelectionControl-zPp4DXWq.js.br +0 -0
  379. codex/static_root/assets/VSelectionControl-zPp4DXWq.js.gz +0 -0
  380. codex/static_root/assets/VSlideGroup-pK9Xompk.67014b86fc74.js.br +0 -0
  381. codex/static_root/assets/VSlideGroup-pK9Xompk.67014b86fc74.js.gz +0 -0
  382. codex/static_root/assets/VSlideGroup-pK9Xompk.js.br +0 -0
  383. codex/static_root/assets/VSlideGroup-pK9Xompk.js.gz +0 -0
  384. codex/static_root/assets/VTable-CMi6sDtP.8445f91d9023.js.br +0 -0
  385. codex/static_root/assets/VTable-CMi6sDtP.8445f91d9023.js.gz +0 -0
  386. codex/static_root/assets/VTable-CMi6sDtP.js.br +0 -0
  387. codex/static_root/assets/VTable-CMi6sDtP.js.gz +0 -0
  388. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css +0 -1
  389. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
  390. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
  391. codex/static_root/assets/VTextField-DuIIp-VY.css +0 -1
  392. codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
  393. codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
  394. codex/static_root/assets/VTextField-dMTizQEN.e93eefec0fdf.js +0 -1
  395. codex/static_root/assets/VTextField-dMTizQEN.e93eefec0fdf.js.br +0 -0
  396. codex/static_root/assets/VTextField-dMTizQEN.e93eefec0fdf.js.gz +0 -0
  397. codex/static_root/assets/VTextField-dMTizQEN.js +0 -1
  398. codex/static_root/assets/VTextField-dMTizQEN.js.br +0 -0
  399. codex/static_root/assets/VTextField-dMTizQEN.js.gz +0 -0
  400. codex/static_root/assets/VWindowItem-DNsRQx9l.c42f636c386e.js.br +0 -0
  401. codex/static_root/assets/VWindowItem-DNsRQx9l.c42f636c386e.js.gz +0 -0
  402. codex/static_root/assets/VWindowItem-DNsRQx9l.js.br +0 -0
  403. codex/static_root/assets/VWindowItem-DNsRQx9l.js.gz +0 -0
  404. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
  405. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
  406. codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
  407. codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
  408. codex/static_root/assets/admin-CApphpty.5a41cbf0eefc.js +0 -1
  409. codex/static_root/assets/admin-CApphpty.5a41cbf0eefc.js.br +0 -0
  410. codex/static_root/assets/admin-CApphpty.5a41cbf0eefc.js.gz +0 -0
  411. codex/static_root/assets/admin-CApphpty.js +0 -1
  412. codex/static_root/assets/admin-CApphpty.js.br +0 -0
  413. codex/static_root/assets/admin-CApphpty.js.gz +0 -0
  414. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +0 -1
  415. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
  416. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
  417. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +0 -1
  418. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
  419. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
  420. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.a79eda3055a7.js +0 -30
  421. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.a79eda3055a7.js.br +0 -0
  422. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.a79eda3055a7.js.gz +0 -0
  423. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.js +0 -30
  424. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.js.br +0 -0
  425. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.js.gz +0 -0
  426. codex/static_root/assets/browser-C_PHQbyN.bb065e2ce21a.js +0 -1
  427. codex/static_root/assets/browser-C_PHQbyN.bb065e2ce21a.js.br +0 -0
  428. codex/static_root/assets/browser-C_PHQbyN.bb065e2ce21a.js.gz +0 -0
  429. codex/static_root/assets/browser-C_PHQbyN.js +0 -1
  430. codex/static_root/assets/browser-C_PHQbyN.js.br +0 -0
  431. codex/static_root/assets/browser-C_PHQbyN.js.gz +0 -0
  432. codex/static_root/assets/browser-DVI4LUNv.css +0 -1
  433. codex/static_root/assets/browser-DVI4LUNv.css.br +0 -0
  434. codex/static_root/assets/browser-DVI4LUNv.css.gz +0 -0
  435. codex/static_root/assets/browser-DVI4LUNv.fef2b997145a.css +0 -1
  436. codex/static_root/assets/browser-DVI4LUNv.fef2b997145a.css.br +0 -0
  437. codex/static_root/assets/browser-DVI4LUNv.fef2b997145a.css.gz +0 -0
  438. codex/static_root/assets/change-password-dialog-BHTkpC6Z.2d5d5dd57ad3.js.br +0 -0
  439. codex/static_root/assets/change-password-dialog-BHTkpC6Z.2d5d5dd57ad3.js.gz +0 -0
  440. codex/static_root/assets/change-password-dialog-BHTkpC6Z.js.br +0 -0
  441. codex/static_root/assets/change-password-dialog-BHTkpC6Z.js.gz +0 -0
  442. codex/static_root/assets/confirm-dialog-DmXGDg4z.284812bd376d.js.br +0 -0
  443. codex/static_root/assets/confirm-dialog-DmXGDg4z.284812bd376d.js.gz +0 -0
  444. codex/static_root/assets/confirm-dialog-DmXGDg4z.js.br +0 -0
  445. codex/static_root/assets/confirm-dialog-DmXGDg4z.js.gz +0 -0
  446. codex/static_root/assets/datetime-column-DKaH5S3z.5c6042016200.js.br +0 -0
  447. codex/static_root/assets/datetime-column-DKaH5S3z.5c6042016200.js.gz +0 -0
  448. codex/static_root/assets/datetime-column-DKaH5S3z.js.br +0 -0
  449. codex/static_root/assets/datetime-column-DKaH5S3z.js.gz +0 -0
  450. codex/static_root/assets/filter-DUVATQW3.e1e7aa6d0b18.js.br +0 -0
  451. codex/static_root/assets/filter-DUVATQW3.e1e7aa6d0b18.js.gz +0 -0
  452. codex/static_root/assets/filter-DUVATQW3.js.br +0 -0
  453. codex/static_root/assets/filter-DUVATQW3.js.gz +0 -0
  454. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +0 -1
  455. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
  456. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
  457. codex/static_root/assets/flag-tab-BacZLlVi.css +0 -1
  458. codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
  459. codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
  460. codex/static_root/assets/flag-tab-CSKMJEn-.5d5c0604541b.js +0 -1
  461. codex/static_root/assets/flag-tab-CSKMJEn-.5d5c0604541b.js.br +0 -0
  462. codex/static_root/assets/flag-tab-CSKMJEn-.5d5c0604541b.js.gz +0 -0
  463. codex/static_root/assets/flag-tab-CSKMJEn-.js +0 -1
  464. codex/static_root/assets/flag-tab-CSKMJEn-.js.br +0 -0
  465. codex/static_root/assets/flag-tab-CSKMJEn-.js.gz +0 -0
  466. codex/static_root/assets/group-tab-D3CyU-Il.fbf9ce5ef593.js.br +0 -0
  467. codex/static_root/assets/group-tab-D3CyU-Il.fbf9ce5ef593.js.gz +0 -0
  468. codex/static_root/assets/group-tab-D3CyU-Il.js.br +0 -0
  469. codex/static_root/assets/group-tab-D3CyU-Il.js.gz +0 -0
  470. codex/static_root/assets/http-error-0ef6-Ajc.e2879bcd242f.js.br +0 -0
  471. codex/static_root/assets/http-error-0ef6-Ajc.e2879bcd242f.js.gz +0 -0
  472. codex/static_root/assets/http-error-0ef6-Ajc.js.br +0 -0
  473. codex/static_root/assets/http-error-0ef6-Ajc.js.gz +0 -0
  474. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +0 -1
  475. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +0 -2
  476. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
  477. codex/static_root/assets/library-tab-CXbkxNZt.css +0 -1
  478. codex/static_root/assets/library-tab-CXbkxNZt.css.br +0 -2
  479. codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
  480. codex/static_root/assets/library-tab-IdDfLn4k.70b55460b8e1.js +0 -1
  481. codex/static_root/assets/library-tab-IdDfLn4k.70b55460b8e1.js.br +0 -0
  482. codex/static_root/assets/library-tab-IdDfLn4k.70b55460b8e1.js.gz +0 -0
  483. codex/static_root/assets/library-tab-IdDfLn4k.js +0 -1
  484. codex/static_root/assets/library-tab-IdDfLn4k.js.br +0 -0
  485. codex/static_root/assets/library-tab-IdDfLn4k.js.gz +0 -0
  486. codex/static_root/assets/main-CuzJev9T.f58771cc37e2.js.br +0 -0
  487. codex/static_root/assets/main-CuzJev9T.f58771cc37e2.js.gz +0 -0
  488. codex/static_root/assets/main-CuzJev9T.js.br +0 -0
  489. codex/static_root/assets/main-CuzJev9T.js.gz +0 -0
  490. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
  491. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
  492. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
  493. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
  494. codex/static_root/assets/pagination-toolbar-CLr9RVHd.66d3b756d974.js +0 -1
  495. codex/static_root/assets/pagination-toolbar-CLr9RVHd.66d3b756d974.js.br +0 -0
  496. codex/static_root/assets/pagination-toolbar-CLr9RVHd.66d3b756d974.js.gz +0 -0
  497. codex/static_root/assets/pagination-toolbar-CLr9RVHd.js +0 -1
  498. codex/static_root/assets/pagination-toolbar-CLr9RVHd.js.br +0 -0
  499. codex/static_root/assets/pagination-toolbar-CLr9RVHd.js.gz +0 -0
  500. codex/static_root/assets/pdf-doc-BlU5mm5n.0431bcdf48d1.js.br +0 -0
  501. codex/static_root/assets/pdf-doc-BlU5mm5n.0431bcdf48d1.js.gz +0 -0
  502. codex/static_root/assets/pdf-doc-BlU5mm5n.js.br +0 -0
  503. codex/static_root/assets/pdf-doc-BlU5mm5n.js.gz +0 -0
  504. codex/static_root/assets/reader-BiKxuxj7.css.br +0 -0
  505. codex/static_root/assets/reader-BiKxuxj7.css.gz +0 -0
  506. codex/static_root/assets/reader-BiKxuxj7.dabe592f46f1.css.br +0 -0
  507. codex/static_root/assets/reader-BiKxuxj7.dabe592f46f1.css.gz +0 -0
  508. codex/static_root/assets/reader-s7jttB_F.f7b43b04961a.js.br +0 -0
  509. codex/static_root/assets/reader-s7jttB_F.f7b43b04961a.js.gz +0 -0
  510. codex/static_root/assets/reader-s7jttB_F.js.br +0 -0
  511. codex/static_root/assets/reader-s7jttB_F.js.gz +0 -0
  512. codex/static_root/assets/relation-chips-BiVsYCd9.27eca8616ba4.js +0 -1
  513. codex/static_root/assets/relation-chips-BiVsYCd9.27eca8616ba4.js.br +0 -0
  514. codex/static_root/assets/relation-chips-BiVsYCd9.27eca8616ba4.js.gz +0 -0
  515. codex/static_root/assets/relation-chips-BiVsYCd9.js +0 -1
  516. codex/static_root/assets/relation-chips-BiVsYCd9.js.br +0 -0
  517. codex/static_root/assets/relation-chips-BiVsYCd9.js.gz +0 -0
  518. codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
  519. codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
  520. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
  521. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
  522. codex/static_root/assets/settings-drawer-DbJuixzh.352aaaab5925.js.br +0 -0
  523. codex/static_root/assets/settings-drawer-DbJuixzh.352aaaab5925.js.gz +0 -0
  524. codex/static_root/assets/settings-drawer-DbJuixzh.js.br +0 -0
  525. codex/static_root/assets/settings-drawer-DbJuixzh.js.gz +0 -0
  526. codex/static_root/assets/stats-tab-BhRLh7XB.7be95b5021c3.js.br +0 -0
  527. codex/static_root/assets/stats-tab-BhRLh7XB.7be95b5021c3.js.gz +0 -0
  528. codex/static_root/assets/stats-tab-BhRLh7XB.js.br +0 -0
  529. codex/static_root/assets/stats-tab-BhRLh7XB.js.gz +0 -0
  530. codex/static_root/assets/task-tab-DLrd54z2.c574111658b9.js +0 -1
  531. codex/static_root/assets/task-tab-DLrd54z2.c574111658b9.js.br +0 -0
  532. codex/static_root/assets/task-tab-DLrd54z2.c574111658b9.js.gz +0 -0
  533. codex/static_root/assets/task-tab-DLrd54z2.js +0 -1
  534. codex/static_root/assets/task-tab-DLrd54z2.js.br +0 -0
  535. codex/static_root/assets/task-tab-DLrd54z2.js.gz +0 -0
  536. codex/static_root/assets/unauthorized-BgXQnNpn.f63b36d526b5.js.br +0 -0
  537. codex/static_root/assets/unauthorized-BgXQnNpn.f63b36d526b5.js.gz +0 -0
  538. codex/static_root/assets/unauthorized-BgXQnNpn.js.br +0 -0
  539. codex/static_root/assets/unauthorized-BgXQnNpn.js.gz +0 -0
  540. codex/static_root/assets/user-tab-cZoJDlb-.2a497e8b2b24.js.br +0 -0
  541. codex/static_root/assets/user-tab-cZoJDlb-.2a497e8b2b24.js.gz +0 -0
  542. codex/static_root/assets/user-tab-cZoJDlb-.js.br +0 -0
  543. codex/static_root/assets/user-tab-cZoJDlb-.js.gz +0 -0
  544. codex/static_root/js/choices-admin.248384ece3f6.json +0 -1
  545. codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
  546. codex/static_root/js/choices-admin.248384ece3f6.json.gz +0 -0
  547. codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
  548. codex/static_root/js/choices.b60817ba0a93.json.gz +0 -0
  549. codex/static_root/manifest.3c2f342d5f5a.json +0 -642
  550. codex/static_root/manifest.3c2f342d5f5a.json.br +0 -0
  551. codex/static_root/manifest.3c2f342d5f5a.json.gz +0 -0
  552. {codex-1.6.0a4.dist-info → codex-1.6.0a5.dist-info}/LICENSE +0 -0
  553. {codex-1.6.0a4.dist-info → codex-1.6.0a5.dist-info}/WHEEL +0 -0
  554. {codex-1.6.0a4.dist-info → codex-1.6.0a5.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  """Query the missing foreign keys for comics and contributors."""
2
2
 
3
+ from itertools import chain
3
4
  from logging import DEBUG, INFO
4
5
  from pathlib import Path
5
6
  from types import MappingProxyType
@@ -35,6 +36,7 @@ from codex.models import (
35
36
  Volume,
36
37
  )
37
38
  from codex.models.named import Identifier
39
+ from codex.models.paths import CustomCover
38
40
  from codex.settings.settings import FILTER_BATCH_SIZE
39
41
  from codex.status import Status
40
42
  from codex.threads import QueuedThread
@@ -566,6 +568,30 @@ class QueryForeignKeysMixin(QueuedThread):
566
568
  self.log.info(f"Prepared {count} new Folders.")
567
569
  return count
568
570
 
571
+ @staticmethod
572
+ def _get_create_fks_totals(create_data):
573
+ (
574
+ create_groups,
575
+ update_groups,
576
+ create_folder_paths,
577
+ create_fks,
578
+ create_contributors,
579
+ create_story_arc_numbers,
580
+ create_identifiers,
581
+ ) = create_data
582
+ total_fks = 0
583
+ for data_group in chain(
584
+ create_groups.values(), update_groups.values(), create_fks.values()
585
+ ):
586
+ total_fks += len(data_group)
587
+ total_fks += (
588
+ len(create_folder_paths)
589
+ + len(create_contributors)
590
+ + len(create_story_arc_numbers)
591
+ + len(create_identifiers)
592
+ )
593
+ return total_fks
594
+
569
595
  def query_all_missing_fks(self, library_path, fks):
570
596
  """Get objects to create by querying existing objects for the proposed fks."""
571
597
  create_contributors = set()
@@ -619,7 +645,7 @@ class QueryForeignKeysMixin(QueuedThread):
619
645
  finally:
620
646
  self.status_controller.finish(status)
621
647
 
622
- return (
648
+ create_data = (
623
649
  create_groups,
624
650
  update_groups,
625
651
  create_folder_paths,
@@ -628,3 +654,41 @@ class QueryForeignKeysMixin(QueuedThread):
628
654
  create_story_arc_numbers,
629
655
  create_identifiers,
630
656
  )
657
+
658
+ total_fks = self._get_create_fks_totals(create_data)
659
+ status = Status(ImportStatusTypes.CREATE_FKS, 0, total_fks)
660
+ self.status_controller.update(status, notify=False)
661
+ return (*create_data, total_fks)
662
+
663
+ @status_notify(ImportStatusTypes.QUERY_MISSING_COVERS, updates=False)
664
+ def query_missing_custom_covers(
665
+ self, cover_paths: frozenset, library, query_data: list, status=None
666
+ ):
667
+ """Identify update & create covers."""
668
+ self.log.debug(f"Querying {len(cover_paths)} custom cover_paths")
669
+ if status:
670
+ status.total = len(cover_paths)
671
+
672
+ update_covers_qs = CustomCover.objects.filter(
673
+ library=library, path__in=cover_paths
674
+ )
675
+ query_data.append(update_covers_qs)
676
+ update_cover_paths = frozenset(update_covers_qs.values_list("path", flat=True))
677
+ update_count = len(update_cover_paths)
678
+ update_status = Status(ImportStatusTypes.COVERS_MODIFIED, 0, update_count)
679
+ self.status_controller.update(update_status, notify=False)
680
+
681
+ create_cover_paths = cover_paths - update_cover_paths
682
+ query_data.append(create_cover_paths)
683
+ create_count = len(create_cover_paths)
684
+ create_status = Status(ImportStatusTypes.COVERS_CREATED, 0, create_count)
685
+ self.status_controller.update(create_status, notify=False)
686
+
687
+ count = create_count + update_count
688
+ if count:
689
+ self.log.debug(
690
+ f"Discovered {update_count} custom covers to update and {create_count} to create."
691
+ )
692
+ if status:
693
+ status.add_complete(count)
694
+ return count
@@ -25,6 +25,12 @@ class ImportStatusTypes(Choices):
25
25
  DIRS_DELETED = "IDD"
26
26
  FILES_DELETED = "IFD"
27
27
  FAILED_IMPORTS = "IFI"
28
+ QUERY_MISSING_COVERS = "ICQ"
29
+ COVERS_MOVED = "ICM"
30
+ COVERS_MODIFIED = "ICU"
31
+ COVERS_CREATED = "ICC"
32
+ COVERS_DELETED = "ICD"
33
+ COVERS_LINK = "ICL"
28
34
 
29
35
 
30
36
  def status_notify(status_type: str | Enum = "", updates=True):
@@ -13,14 +13,22 @@ class ImportDBDiffTask(ImportTask):
13
13
  """For sending to the updater."""
14
14
 
15
15
  library_id: int
16
+
16
17
  dirs_moved: dict[str, str]
17
- files_moved: dict[str, str]
18
18
  dirs_modified: frozenset[str]
19
+ # dirs_created: frozenset[str]
20
+ dirs_deleted: frozenset[str]
21
+
22
+ files_moved: dict[str, str]
19
23
  files_modified: frozenset[str]
20
- # dirs_created
21
24
  files_created: frozenset[str]
22
- dirs_deleted: frozenset[str]
23
25
  files_deleted: frozenset[str]
26
+
27
+ covers_moved: dict[str, str]
28
+ covers_modified: frozenset[str]
29
+ covers_created: frozenset[str]
30
+ covers_deleted: frozenset[str]
31
+
24
32
  force_import_metadata: bool = False
25
33
 
26
34
 
@@ -3,7 +3,6 @@
3
3
  from django.db.models import NOT_PROVIDED
4
4
  from django.db.models.functions import Now
5
5
 
6
- from codex.librarian.covers.tasks import CoverRemoveTask
7
6
  from codex.librarian.importer.const import (
8
7
  BULK_UPDATE_COMIC_FIELDS,
9
8
  BULK_UPDATE_COMIC_FIELDS_WITH_VALUES,
@@ -78,9 +77,8 @@ class UpdateComicsMixin(LinkComicsMixin):
78
77
  Timestamp.touch(Timestamp.TimestampChoices.COVERS)
79
78
  count = len(update_comics)
80
79
 
81
- task = CoverRemoveTask(frozenset(comic_pks))
80
+ self._remove_covers(comic_pks, False) # type: ignore
82
81
  self.log.debug(f"Purging covers for {len(comic_pks)} updated comics.")
83
- self.librarian_queue.put(task)
84
82
  if count:
85
83
  self.log.info(f"Updated {count} comics.")
86
84
  except Exception:
@@ -1,6 +1,7 @@
1
1
  """Clean up the database after moves or imports."""
2
2
 
3
3
  import logging
4
+ from pathlib import Path
4
5
  from time import time
5
6
  from types import MappingProxyType
6
7
 
@@ -34,6 +35,7 @@ from codex.models import (
34
35
  Team,
35
36
  Volume,
36
37
  )
38
+ from codex.models.paths import CustomCover
37
39
  from codex.status import Status
38
40
  from codex.worker_base import WorkerBaseMixin
39
41
 
@@ -107,6 +109,28 @@ class CleanupMixin(WorkerBaseMixin):
107
109
  finally:
108
110
  self.status_controller.finish(status)
109
111
 
112
+ def cleanup_custom_covers(self):
113
+ """Clean up unused custom covers."""
114
+ start = time()
115
+ covers = CustomCover.objects.only("path")
116
+ status = Status(JanitorStatusTypes.CLEANUP_COVERS, 0, covers.count())
117
+ delete_pks = []
118
+ try:
119
+ self.status_controller.start(status)
120
+ self.log.debug("Cleaning up db custom covers with no source images...")
121
+ for cover in covers.iterator():
122
+ if not Path(cover.path).exists():
123
+ delete_pks.append(cover.pk)
124
+ status.increment_complete()
125
+ delete_qs = CustomCover.objects.filter(pk__in=delete_pks)
126
+ count = delete_qs.count()
127
+ delete_qs.delete()
128
+ level = logging.INFO if status.complete else logging.DEBUG
129
+ self.log.log(level, f"Deleted {count} CustomCovers without source images.")
130
+ finally:
131
+ until = start + 2
132
+ self.status_controller.finish(status, until=until)
133
+
110
134
  def cleanup_sessions(self):
111
135
  """Delete corrupt sessions."""
112
136
  start = time()
@@ -22,6 +22,6 @@ class UpdateFailedImportsMixin(WorkerBaseMixin):
22
22
 
23
23
  def force_update_all_failed_imports(self):
24
24
  """Force update events for failed imports in every library."""
25
- pks = Library.objects.values_list("pk", flat=True)
25
+ pks = Library.objects.filter(covers_only=False).values_list("pk", flat=True)
26
26
  for pk in pks:
27
27
  self._force_update_failed_imports(pk)
@@ -8,6 +8,7 @@ from codex.librarian.janitor.status import JanitorStatusTypes
8
8
  from codex.librarian.janitor.tasks import (
9
9
  ForceUpdateAllFailedImportsTask,
10
10
  JanitorBackupTask,
11
+ JanitorCleanCoversTask,
11
12
  JanitorCleanFKsTask,
12
13
  JanitorCleanupSessionsTask,
13
14
  JanitorClearStatusTask,
@@ -30,6 +31,7 @@ from codex.status import Status
30
31
 
31
32
  _JANITOR_STATII = (
32
33
  Status(JanitorStatusTypes.CLEANUP_FK, 0, TOTAL_NUM_FK_CLASSES),
34
+ Status(JanitorStatusTypes.CLEANUP_COVERS),
33
35
  Status(JanitorStatusTypes.CLEANUP_SESSIONS),
34
36
  Status(JanitorStatusTypes.DB_OPTIMIZE),
35
37
  Status(JanitorStatusTypes.DB_BACKUP),
@@ -59,6 +61,7 @@ class Janitor(CleanupMixin, UpdateMixin, VacuumMixin, UpdateFailedImportsMixin):
59
61
  tasks = (
60
62
  SearchIndexAbortTask(),
61
63
  JanitorCleanFKsTask(),
64
+ JanitorCleanCoversTask(),
62
65
  JanitorCleanupSessionsTask(),
63
66
  JanitorVacuumTask(),
64
67
  JanitorBackupTask(),
@@ -89,6 +92,8 @@ class Janitor(CleanupMixin, UpdateMixin, VacuumMixin, UpdateFailedImportsMixin):
89
92
  self.shutdown_codex()
90
93
  case JanitorCleanFKsTask():
91
94
  self.cleanup_fks()
95
+ case JanitorCleanCoversTask():
96
+ self.cleanup_custom_covers()
92
97
  case JanitorCleanupSessionsTask():
93
98
  self.cleanup_sessions()
94
99
  case JanitorClearStatusTask():
@@ -1,4 +1,4 @@
1
- """Perform maintence tasks."""
1
+ """Perform maintenance tasks."""
2
2
 
3
3
  from datetime import datetime, time, timedelta
4
4
  from threading import Condition, Event
@@ -13,3 +13,4 @@ class JanitorStatusTypes(Choices):
13
13
  DB_OPTIMIZE = "JDO"
14
14
  DB_BACKUP = "JDB"
15
15
  CLEANUP_SESSIONS = "JSD"
16
+ CLEANUP_COVERS = "JCD"
@@ -40,6 +40,11 @@ class JanitorCleanFKsTask(JanitorTask):
40
40
  """Clean unused foreign keys."""
41
41
 
42
42
 
43
+ @dataclass
44
+ class JanitorCleanCoversTask(JanitorTask):
45
+ """Clean unused custom covers."""
46
+
47
+
43
48
  @dataclass
44
49
  class JanitorClearStatusTask(JanitorTask):
45
50
  """Clear all librarian statuses."""
@@ -7,7 +7,7 @@ from typing import NamedTuple
7
7
 
8
8
  from caseconverter import snakecase
9
9
 
10
- from codex.librarian.covers.coverd import CoverContributorThread
10
+ from codex.librarian.covers.coverd import CoverThread
11
11
  from codex.librarian.covers.tasks import CoverTask
12
12
  from codex.librarian.delayed_taskd import DelayedTasksThread
13
13
  from codex.librarian.importer.importerd import ComicImporterThread
@@ -43,7 +43,7 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
43
43
  _THREAD_CLASSES = (
44
44
  NotifierThread,
45
45
  DelayedTasksThread,
46
- CoverContributorThread,
46
+ CoverThread,
47
47
  SearchIndexerThread,
48
48
  ComicImporterThread,
49
49
  WatchdogEventBatcherThread,
@@ -81,7 +81,7 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
81
81
  """Process an individual task popped off the queue."""
82
82
  match task:
83
83
  case CoverTask():
84
- self._threads.cover_contributor_thread.queue.put(task)
84
+ self._threads.cover_thread.queue.put(task)
85
85
  case WatchdogEventTask():
86
86
  self._threads.watchdog_event_batcher_thread.queue.put(task)
87
87
  case ImportTask():
@@ -319,7 +319,7 @@ class UpdateMixin(RemoveMixin):
319
319
  self.abort_event.clear()
320
320
  try:
321
321
  any_update_in_progress = Library.objects.filter(
322
- update_in_progress=True
322
+ covers_only=False, update_in_progress=True
323
323
  ).exists()
324
324
  if any_update_in_progress:
325
325
  self.log.debug(
@@ -7,19 +7,20 @@ from pathlib import Path
7
7
  from watchdog.utils.dirsnapshot import DirectorySnapshot
8
8
 
9
9
  from codex.logger_base import LoggerBaseMixin
10
- from codex.models import Comic, FailedImport, Folder
10
+ from codex.models import Comic, CustomCover, FailedImport, Folder
11
11
 
12
12
 
13
13
  class CodexDatabaseSnapshot(DirectorySnapshot, LoggerBaseMixin):
14
14
  """Take snapshots from the Codex database."""
15
15
 
16
- MODELS = (Folder, Comic, FailedImport)
16
+ MODELS = (Folder, Comic, FailedImport, CustomCover)
17
+ COVERS_ONLY_MODELS = (CustomCover,)
17
18
  _STAT_LEN = 10
18
19
 
19
- @classmethod
20
- def _walk(cls, root):
20
+ def _walk(self, root):
21
21
  """Populate the DirectorySnapshot structures from the database."""
22
- for model in cls.MODELS:
22
+ models = self.COVERS_ONLY_MODELS if self._covers_only else self.MODELS
23
+ for model in models:
23
24
  yield (
24
25
  model.objects.filter(library__path=root)
25
26
  .order_by("path")
@@ -57,7 +58,7 @@ class CodexDatabaseSnapshot(DirectorySnapshot, LoggerBaseMixin):
57
58
  i = (st.st_ino, st.st_dev)
58
59
  self._inode_to_path[i] = path
59
60
 
60
- def __init__(
61
+ def __init__( # noqa: PLR0913
61
62
  self,
62
63
  path,
63
64
  _recursive=True, # unused, always recursive
@@ -65,8 +66,10 @@ class CodexDatabaseSnapshot(DirectorySnapshot, LoggerBaseMixin):
65
66
  _listdir=os.listdir, # unused for database
66
67
  force=False,
67
68
  log_queue=None,
69
+ covers_only=False,
68
70
  ):
69
71
  """Initialize like DirectorySnapshot but use a database walk."""
72
+ self._covers_only = covers_only
70
73
  self.init_logger(log_queue)
71
74
  self._stat_info = {}
72
75
  self._inode_to_path = {}
@@ -54,6 +54,7 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
54
54
  timeout=DEFAULT_EMITTER_TIMEOUT,
55
55
  log_queue=None,
56
56
  librarian_queue=None,
57
+ covers_only=False,
57
58
  ):
58
59
  """Initialize snapshot methods."""
59
60
  self.init_worker(log_queue, librarian_queue)
@@ -61,6 +62,7 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
61
62
  self._force = False
62
63
  self._watch_path = Path(watch.path)
63
64
  self._watch_path_unmounted = self._watch_path / _DOCKER_UNMOUNTED_FN
65
+ self._covers_only = covers_only
64
66
  super().__init__(
65
67
  event_queue, watch, timeout=timeout, event_filter=_CODEX_EVENT_FILTER
66
68
  )
@@ -84,6 +86,7 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
84
86
  self.watch.is_recursive,
85
87
  force=self._force,
86
88
  log_queue=self.log_queue,
89
+ covers_only=self._covers_only,
87
90
  )
88
91
  self._force = False
89
92
  return db_snapshot
@@ -25,12 +25,16 @@ class WatchdogEventBatcherThread(AggregateMessageQueuedThread):
25
25
  {
26
26
  "library_id": None,
27
27
  "dirs_moved": {},
28
+ "dirs_modified": set(),
29
+ "dirs_deleted": set(),
28
30
  "files_moved": {},
29
31
  "files_modified": set(),
30
32
  "files_created": set(),
31
- "dirs_deleted": set(),
32
- "dirs_modified": set(),
33
33
  "files_deleted": set(),
34
+ "covers_moved": {},
35
+ "covers_modified": set(),
36
+ "covers_created": set(),
37
+ "covers_deleted": set(),
34
38
  }
35
39
  )
36
40
  MAX_DELAY = 60
@@ -54,7 +58,13 @@ class WatchdogEventBatcherThread(AggregateMessageQueuedThread):
54
58
  """Translate event class names into field names."""
55
59
  self._ensure_library_args(library_id)
56
60
 
57
- prefix = "dir" if event.is_directory else "file"
61
+ prefix = (
62
+ "dir"
63
+ if event.is_directory
64
+ else "cover"
65
+ if getattr(event, "is_cover", False)
66
+ else "file"
67
+ )
58
68
  field = f"{prefix}s_{event.event_type}"
59
69
 
60
70
  return self.cache[library_id].get(field)
@@ -90,17 +100,23 @@ class WatchdogEventBatcherThread(AggregateMessageQueuedThread):
90
100
  for src_path in args["files_deleted"]:
91
101
  with suppress(KeyError):
92
102
  del args["files_moved"][src_path]
103
+
93
104
  # created
94
105
  args["files_created"] -= args["files_deleted"]
95
106
  files_dest_paths = set(args["files_moved"].values())
96
107
  args["files_created"] -= files_dest_paths
108
+
97
109
  # modified
98
110
  args["dirs_modified"] -= args["dirs_deleted"]
99
111
  args["dirs_modified"] -= set(args["dirs_moved"].values())
112
+
100
113
  args["files_modified"] -= args["files_created"]
101
114
  args["files_modified"] -= args["files_deleted"]
102
115
  args["files_modified"] -= files_dest_paths
103
116
 
117
+ args["covers_modified"] -= args["covers_deleted"]
118
+ args["covers_modified"] -= args["covers_created"]
119
+
104
120
  def _create_task(self, library_id):
105
121
  """Create a task from cached aggregated message data."""
106
122
  self._deduplicate_events(library_id)