codex 1.5.19__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 (911) 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 -40
  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 +62 -14
  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-CzTNt4cV.39d03f0eaf4b.js → VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js} +1 -1
  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-CzTNt4cV.js → VCheckboxBtn-DxNOKONG.js} +1 -1
  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-TEvK4Tmn.c68c176d93db.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-TEvK4Tmn.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-D380D1SI.f4bc34623651.js → flag-tab-9qxOALxP.f89bc9891671.js} +1 -1
  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-D380D1SI.js → flag-tab-9qxOALxP.js} +1 -1
  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-ED57DJUb.css → main-Ztp855PW.css} +1 -1
  298. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  299. codex/static_root/assets/{main-ED57DJUb.css.gz → main-Ztp855PW.css.gz} +0 -0
  300. codex/static_root/assets/{main-ED57DJUb.b84231dc384d.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-ED57DJUb.b84231dc384d.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 +337 -322
  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.19.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-D7WF5wMC.c98c1beb207e.js +0 -1
  573. codex/static_root/assets/VCheckbox-D7WF5wMC.c98c1beb207e.js.br +0 -0
  574. codex/static_root/assets/VCheckbox-D7WF5wMC.c98c1beb207e.js.gz +0 -0
  575. codex/static_root/assets/VCheckbox-D7WF5wMC.js +0 -1
  576. codex/static_root/assets/VCheckbox-D7WF5wMC.js.br +0 -0
  577. codex/static_root/assets/VCheckbox-D7WF5wMC.js.gz +0 -0
  578. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.39d03f0eaf4b.js.br +0 -0
  579. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.39d03f0eaf4b.js.gz +0 -0
  580. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.js.br +0 -0
  581. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.js.gz +0 -0
  582. codex/static_root/assets/VCombobox-BWrSBDP9.52f0c0067437.js +0 -1
  583. codex/static_root/assets/VCombobox-BWrSBDP9.52f0c0067437.js.br +0 -0
  584. codex/static_root/assets/VCombobox-BWrSBDP9.52f0c0067437.js.gz +0 -0
  585. codex/static_root/assets/VCombobox-BWrSBDP9.js +0 -1
  586. codex/static_root/assets/VCombobox-BWrSBDP9.js.br +0 -0
  587. codex/static_root/assets/VCombobox-BWrSBDP9.js.gz +0 -0
  588. codex/static_root/assets/VDataTableVirtual-BMondxto.8b48739e5470.js +0 -1
  589. codex/static_root/assets/VDataTableVirtual-BMondxto.8b48739e5470.js.br +0 -0
  590. codex/static_root/assets/VDataTableVirtual-BMondxto.8b48739e5470.js.gz +0 -0
  591. codex/static_root/assets/VDataTableVirtual-BMondxto.js +0 -1
  592. codex/static_root/assets/VDataTableVirtual-BMondxto.js.br +0 -0
  593. codex/static_root/assets/VDataTableVirtual-BMondxto.js.gz +0 -0
  594. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.a73a7dd5614a.css +0 -1
  595. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.a73a7dd5614a.css.br +0 -0
  596. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.a73a7dd5614a.css.gz +0 -0
  597. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.css +0 -1
  598. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.css.br +0 -0
  599. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.css.gz +0 -0
  600. codex/static_root/assets/VExpansionPanels-CDS9qp1D.9fdf68e2f68f.js +0 -1
  601. codex/static_root/assets/VExpansionPanels-CDS9qp1D.9fdf68e2f68f.js.br +0 -0
  602. codex/static_root/assets/VExpansionPanels-CDS9qp1D.9fdf68e2f68f.js.gz +0 -0
  603. codex/static_root/assets/VExpansionPanels-CDS9qp1D.js +0 -1
  604. codex/static_root/assets/VExpansionPanels-CDS9qp1D.js.br +0 -0
  605. codex/static_root/assets/VExpansionPanels-CDS9qp1D.js.gz +0 -0
  606. codex/static_root/assets/VMain-8vjSGbNt.4a97ed735dc3.js +0 -1
  607. codex/static_root/assets/VMain-8vjSGbNt.4a97ed735dc3.js.br +0 -0
  608. codex/static_root/assets/VMain-8vjSGbNt.4a97ed735dc3.js.gz +0 -0
  609. codex/static_root/assets/VMain-8vjSGbNt.js +0 -1
  610. codex/static_root/assets/VMain-8vjSGbNt.js.br +0 -0
  611. codex/static_root/assets/VMain-8vjSGbNt.js.gz +0 -0
  612. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css +0 -1
  613. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css.br +0 -0
  614. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css.gz +0 -0
  615. codex/static_root/assets/VMain-Byt37V4c.css +0 -1
  616. codex/static_root/assets/VMain-Byt37V4c.css.br +0 -0
  617. codex/static_root/assets/VMain-Byt37V4c.css.gz +0 -0
  618. codex/static_root/assets/VRadioGroup-DGplihzh.3f4a5d26a1cb.js +0 -1
  619. codex/static_root/assets/VRadioGroup-DGplihzh.3f4a5d26a1cb.js.br +0 -0
  620. codex/static_root/assets/VRadioGroup-DGplihzh.3f4a5d26a1cb.js.gz +0 -0
  621. codex/static_root/assets/VRadioGroup-DGplihzh.js +0 -1
  622. codex/static_root/assets/VRadioGroup-DGplihzh.js.br +0 -0
  623. codex/static_root/assets/VRadioGroup-DGplihzh.js.gz +0 -0
  624. codex/static_root/assets/VSelect-DfV5vhqb.d24dcc61a20f.js +0 -1
  625. codex/static_root/assets/VSelect-DfV5vhqb.d24dcc61a20f.js.br +0 -0
  626. codex/static_root/assets/VSelect-DfV5vhqb.d24dcc61a20f.js.gz +0 -0
  627. codex/static_root/assets/VSelect-DfV5vhqb.js +0 -1
  628. codex/static_root/assets/VSelect-DfV5vhqb.js.br +0 -0
  629. codex/static_root/assets/VSelect-DfV5vhqb.js.gz +0 -0
  630. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css +0 -1
  631. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.br +0 -0
  632. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.gz +0 -0
  633. codex/static_root/assets/VSelect-MGVSeLgr.css +0 -1
  634. codex/static_root/assets/VSelect-MGVSeLgr.css.br +0 -0
  635. codex/static_root/assets/VSelect-MGVSeLgr.css.gz +0 -0
  636. codex/static_root/assets/VSelectionControl-DvHEDoJy.30ca0aef8bd2.js +0 -1
  637. codex/static_root/assets/VSelectionControl-DvHEDoJy.30ca0aef8bd2.js.br +0 -0
  638. codex/static_root/assets/VSelectionControl-DvHEDoJy.30ca0aef8bd2.js.gz +0 -0
  639. codex/static_root/assets/VSelectionControl-DvHEDoJy.js +0 -1
  640. codex/static_root/assets/VSelectionControl-DvHEDoJy.js.br +0 -0
  641. codex/static_root/assets/VSelectionControl-DvHEDoJy.js.gz +0 -0
  642. codex/static_root/assets/VSlideGroup-BdgkaRxE.ba78a4e0efc0.js +0 -1
  643. codex/static_root/assets/VSlideGroup-BdgkaRxE.ba78a4e0efc0.js.br +0 -0
  644. codex/static_root/assets/VSlideGroup-BdgkaRxE.ba78a4e0efc0.js.gz +0 -0
  645. codex/static_root/assets/VSlideGroup-BdgkaRxE.js +0 -1
  646. codex/static_root/assets/VSlideGroup-BdgkaRxE.js.br +0 -0
  647. codex/static_root/assets/VSlideGroup-BdgkaRxE.js.gz +0 -0
  648. codex/static_root/assets/VSlideGroup-CTW-HJl4.8b75b66602c9.css +0 -1
  649. codex/static_root/assets/VSlideGroup-CTW-HJl4.8b75b66602c9.css.br +0 -0
  650. codex/static_root/assets/VSlideGroup-CTW-HJl4.8b75b66602c9.css.gz +0 -0
  651. codex/static_root/assets/VSlideGroup-CTW-HJl4.css +0 -1
  652. codex/static_root/assets/VSlideGroup-CTW-HJl4.css.br +0 -0
  653. codex/static_root/assets/VSlideGroup-CTW-HJl4.css.gz +0 -0
  654. codex/static_root/assets/VTable-TEvK4Tmn.c68c176d93db.js.br +0 -0
  655. codex/static_root/assets/VTable-TEvK4Tmn.c68c176d93db.js.gz +0 -0
  656. codex/static_root/assets/VTable-TEvK4Tmn.js.br +0 -0
  657. codex/static_root/assets/VTable-TEvK4Tmn.js.gz +0 -0
  658. codex/static_root/assets/VToolbar-D7BgLWdG.css +0 -1
  659. codex/static_root/assets/VToolbar-D7BgLWdG.css.br +0 -0
  660. codex/static_root/assets/VToolbar-D7BgLWdG.css.gz +0 -0
  661. codex/static_root/assets/VToolbar-D7BgLWdG.e4d8a79cc655.css +0 -1
  662. codex/static_root/assets/VToolbar-D7BgLWdG.e4d8a79cc655.css.br +0 -0
  663. codex/static_root/assets/VToolbar-D7BgLWdG.e4d8a79cc655.css.gz +0 -0
  664. codex/static_root/assets/VToolbar-DCXw9xBt.576c107a78d3.js +0 -2
  665. codex/static_root/assets/VToolbar-DCXw9xBt.576c107a78d3.js.br +0 -0
  666. codex/static_root/assets/VToolbar-DCXw9xBt.576c107a78d3.js.gz +0 -0
  667. codex/static_root/assets/VToolbar-DCXw9xBt.js +0 -2
  668. codex/static_root/assets/VToolbar-DCXw9xBt.js.br +0 -0
  669. codex/static_root/assets/VToolbar-DCXw9xBt.js.gz +0 -0
  670. codex/static_root/assets/VWindowItem-CCYriq5i.8a6d4a38dabd.js +0 -1
  671. codex/static_root/assets/VWindowItem-CCYriq5i.8a6d4a38dabd.js.br +0 -0
  672. codex/static_root/assets/VWindowItem-CCYriq5i.8a6d4a38dabd.js.gz +0 -0
  673. codex/static_root/assets/VWindowItem-CCYriq5i.js +0 -1
  674. codex/static_root/assets/VWindowItem-CCYriq5i.js.br +0 -0
  675. codex/static_root/assets/VWindowItem-CCYriq5i.js.gz +0 -0
  676. codex/static_root/assets/admin-A2T028Mn.be3fa80456de.js +0 -1
  677. codex/static_root/assets/admin-A2T028Mn.be3fa80456de.js.br +0 -0
  678. codex/static_root/assets/admin-A2T028Mn.be3fa80456de.js.gz +0 -0
  679. codex/static_root/assets/admin-A2T028Mn.js +0 -1
  680. codex/static_root/assets/admin-A2T028Mn.js.br +0 -0
  681. codex/static_root/assets/admin-A2T028Mn.js.gz +0 -0
  682. codex/static_root/assets/admin-C8Kk7_Ni.255594400b96.css +0 -1
  683. codex/static_root/assets/admin-C8Kk7_Ni.255594400b96.css.br +0 -0
  684. codex/static_root/assets/admin-C8Kk7_Ni.255594400b96.css.gz +0 -0
  685. codex/static_root/assets/admin-C8Kk7_Ni.css +0 -1
  686. codex/static_root/assets/admin-C8Kk7_Ni.css.br +0 -0
  687. codex/static_root/assets/admin-C8Kk7_Ni.css.gz +0 -0
  688. codex/static_root/assets/admin-drawer-panel-DffXGW1d.8f7f39ffb127.js +0 -30
  689. codex/static_root/assets/admin-drawer-panel-DffXGW1d.8f7f39ffb127.js.br +0 -0
  690. codex/static_root/assets/admin-drawer-panel-DffXGW1d.8f7f39ffb127.js.gz +0 -0
  691. codex/static_root/assets/admin-drawer-panel-DffXGW1d.js +0 -30
  692. codex/static_root/assets/admin-drawer-panel-DffXGW1d.js.br +0 -0
  693. codex/static_root/assets/admin-drawer-panel-DffXGW1d.js.gz +0 -0
  694. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.af4fa9980c09.css +0 -1
  695. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.af4fa9980c09.css.br +0 -0
  696. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.af4fa9980c09.css.gz +0 -0
  697. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.css +0 -1
  698. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.css.br +0 -0
  699. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.css.gz +0 -0
  700. codex/static_root/assets/browser-CY9BdW6B.c9c3e5dffcbf.js +0 -1
  701. codex/static_root/assets/browser-CY9BdW6B.c9c3e5dffcbf.js.br +0 -0
  702. codex/static_root/assets/browser-CY9BdW6B.c9c3e5dffcbf.js.gz +0 -0
  703. codex/static_root/assets/browser-CY9BdW6B.js +0 -1
  704. codex/static_root/assets/browser-CY9BdW6B.js.br +0 -0
  705. codex/static_root/assets/browser-CY9BdW6B.js.gz +0 -0
  706. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css +0 -1
  707. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css.br +0 -0
  708. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css.gz +0 -0
  709. codex/static_root/assets/browser-HtKThxA9.css +0 -1
  710. codex/static_root/assets/browser-HtKThxA9.css.br +0 -0
  711. codex/static_root/assets/browser-HtKThxA9.css.gz +0 -0
  712. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css +0 -1
  713. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css.br +0 -0
  714. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css.gz +0 -0
  715. codex/static_root/assets/change-password-dialog-BRmvkNir.css +0 -1
  716. codex/static_root/assets/change-password-dialog-BRmvkNir.css.br +0 -0
  717. codex/static_root/assets/change-password-dialog-BRmvkNir.css.gz +0 -0
  718. codex/static_root/assets/change-password-dialog-Dr1CYQ46.afdd502a985d.js +0 -1
  719. codex/static_root/assets/change-password-dialog-Dr1CYQ46.afdd502a985d.js.br +0 -0
  720. codex/static_root/assets/change-password-dialog-Dr1CYQ46.afdd502a985d.js.gz +0 -0
  721. codex/static_root/assets/change-password-dialog-Dr1CYQ46.js +0 -1
  722. codex/static_root/assets/change-password-dialog-Dr1CYQ46.js.br +0 -0
  723. codex/static_root/assets/change-password-dialog-Dr1CYQ46.js.gz +0 -0
  724. codex/static_root/assets/confirm-dialog-BoVvCI5y.8549e2429bdf.js +0 -1
  725. codex/static_root/assets/confirm-dialog-BoVvCI5y.8549e2429bdf.js.br +0 -0
  726. codex/static_root/assets/confirm-dialog-BoVvCI5y.8549e2429bdf.js.gz +0 -0
  727. codex/static_root/assets/confirm-dialog-BoVvCI5y.js +0 -1
  728. codex/static_root/assets/confirm-dialog-BoVvCI5y.js.br +0 -0
  729. codex/static_root/assets/confirm-dialog-BoVvCI5y.js.gz +0 -0
  730. codex/static_root/assets/confirm-footer-BPuy6r_H.3aa74b8e07c1.css +0 -1
  731. codex/static_root/assets/confirm-footer-BPuy6r_H.3aa74b8e07c1.css.br +0 -0
  732. codex/static_root/assets/confirm-footer-BPuy6r_H.3aa74b8e07c1.css.gz +0 -0
  733. codex/static_root/assets/confirm-footer-BPuy6r_H.css +0 -1
  734. codex/static_root/assets/confirm-footer-BPuy6r_H.css.br +0 -0
  735. codex/static_root/assets/confirm-footer-BPuy6r_H.css.gz +0 -0
  736. codex/static_root/assets/confirm-footer-CIu2tkFD.0aa16c0fc333.js +0 -1
  737. codex/static_root/assets/confirm-footer-CIu2tkFD.0aa16c0fc333.js.br +0 -0
  738. codex/static_root/assets/confirm-footer-CIu2tkFD.0aa16c0fc333.js.gz +0 -0
  739. codex/static_root/assets/confirm-footer-CIu2tkFD.js +0 -1
  740. codex/static_root/assets/confirm-footer-CIu2tkFD.js.br +0 -0
  741. codex/static_root/assets/confirm-footer-CIu2tkFD.js.gz +0 -0
  742. codex/static_root/assets/datetime-column-BWWfvyhn.a4a94ba57c73.css +0 -1
  743. codex/static_root/assets/datetime-column-BWWfvyhn.a4a94ba57c73.css.br +0 -0
  744. codex/static_root/assets/datetime-column-BWWfvyhn.css +0 -1
  745. codex/static_root/assets/datetime-column-BWWfvyhn.css.br +0 -0
  746. codex/static_root/assets/datetime-column-C3t430gN.97e5c5364aef.js +0 -1
  747. codex/static_root/assets/datetime-column-C3t430gN.97e5c5364aef.js.br +0 -0
  748. codex/static_root/assets/datetime-column-C3t430gN.97e5c5364aef.js.gz +0 -0
  749. codex/static_root/assets/datetime-column-C3t430gN.js +0 -1
  750. codex/static_root/assets/datetime-column-C3t430gN.js.br +0 -0
  751. codex/static_root/assets/datetime-column-C3t430gN.js.gz +0 -0
  752. codex/static_root/assets/filter-BQ_FuhIp.ee8b78c4aa55.js +0 -1
  753. codex/static_root/assets/filter-BQ_FuhIp.ee8b78c4aa55.js.br +0 -0
  754. codex/static_root/assets/filter-BQ_FuhIp.ee8b78c4aa55.js.gz +0 -0
  755. codex/static_root/assets/filter-BQ_FuhIp.js +0 -1
  756. codex/static_root/assets/filter-BQ_FuhIp.js.br +0 -0
  757. codex/static_root/assets/filter-BQ_FuhIp.js.gz +0 -0
  758. codex/static_root/assets/flag-tab-D380D1SI.f4bc34623651.js.br +0 -0
  759. codex/static_root/assets/flag-tab-D380D1SI.f4bc34623651.js.gz +0 -0
  760. codex/static_root/assets/flag-tab-D380D1SI.js.br +0 -0
  761. codex/static_root/assets/flag-tab-D380D1SI.js.gz +0 -0
  762. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css +0 -1
  763. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css.br +0 -0
  764. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css.gz +0 -0
  765. codex/static_root/assets/flag-tab-D_HjkAta.css +0 -1
  766. codex/static_root/assets/flag-tab-D_HjkAta.css.br +0 -0
  767. codex/static_root/assets/flag-tab-D_HjkAta.css.gz +0 -0
  768. codex/static_root/assets/group-tab-CDcOyQyw.9c85a4fb987a.js +0 -1
  769. codex/static_root/assets/group-tab-CDcOyQyw.9c85a4fb987a.js.br +0 -0
  770. codex/static_root/assets/group-tab-CDcOyQyw.9c85a4fb987a.js.gz +0 -0
  771. codex/static_root/assets/group-tab-CDcOyQyw.js +0 -1
  772. codex/static_root/assets/group-tab-CDcOyQyw.js.br +0 -0
  773. codex/static_root/assets/group-tab-CDcOyQyw.js.gz +0 -0
  774. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css +0 -1
  775. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css.br +0 -0
  776. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css.gz +0 -0
  777. codex/static_root/assets/group-tab-GFacGljN.css +0 -1
  778. codex/static_root/assets/group-tab-GFacGljN.css.br +0 -0
  779. codex/static_root/assets/group-tab-GFacGljN.css.gz +0 -0
  780. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css +0 -1
  781. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css.br +0 -0
  782. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css.gz +0 -0
  783. codex/static_root/assets/http-error-C25WdjWC.css +0 -1
  784. codex/static_root/assets/http-error-C25WdjWC.css.br +0 -0
  785. codex/static_root/assets/http-error-C25WdjWC.css.gz +0 -0
  786. codex/static_root/assets/http-error-D4BCbtF2.d3d31021f2ca.js +0 -1
  787. codex/static_root/assets/http-error-D4BCbtF2.d3d31021f2ca.js.br +0 -0
  788. codex/static_root/assets/http-error-D4BCbtF2.d3d31021f2ca.js.gz +0 -0
  789. codex/static_root/assets/http-error-D4BCbtF2.js +0 -1
  790. codex/static_root/assets/http-error-D4BCbtF2.js.br +0 -0
  791. codex/static_root/assets/http-error-D4BCbtF2.js.gz +0 -0
  792. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +0 -1
  793. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
  794. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
  795. codex/static_root/assets/index-DUjeNg4x.js +0 -1
  796. codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
  797. codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
  798. codex/static_root/assets/library-tab-BpWTK9vs.b2a752770db5.js +0 -1
  799. codex/static_root/assets/library-tab-BpWTK9vs.b2a752770db5.js.br +0 -0
  800. codex/static_root/assets/library-tab-BpWTK9vs.b2a752770db5.js.gz +0 -0
  801. codex/static_root/assets/library-tab-BpWTK9vs.js +0 -1
  802. codex/static_root/assets/library-tab-BpWTK9vs.js.br +0 -0
  803. codex/static_root/assets/library-tab-BpWTK9vs.js.gz +0 -0
  804. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css +0 -1
  805. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css.br +0 -3
  806. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css.gz +0 -0
  807. codex/static_root/assets/library-tab-DZKDtQ5v.css +0 -1
  808. codex/static_root/assets/library-tab-DZKDtQ5v.css.br +0 -3
  809. codex/static_root/assets/library-tab-DZKDtQ5v.css.gz +0 -0
  810. codex/static_root/assets/main-DB0zDxIA.e71734100695.js +0 -32
  811. codex/static_root/assets/main-DB0zDxIA.e71734100695.js.br +0 -0
  812. codex/static_root/assets/main-DB0zDxIA.e71734100695.js.gz +0 -0
  813. codex/static_root/assets/main-DB0zDxIA.js +0 -32
  814. codex/static_root/assets/main-DB0zDxIA.js.br +0 -0
  815. codex/static_root/assets/main-DB0zDxIA.js.gz +0 -0
  816. codex/static_root/assets/main-ED57DJUb.b84231dc384d.css.br +0 -0
  817. codex/static_root/assets/main-ED57DJUb.css.br +0 -0
  818. codex/static_root/assets/metadata-dialog-BlvHIpPe.aca38025f67b.js +0 -1
  819. codex/static_root/assets/metadata-dialog-BlvHIpPe.aca38025f67b.js.br +0 -0
  820. codex/static_root/assets/metadata-dialog-BlvHIpPe.aca38025f67b.js.gz +0 -0
  821. codex/static_root/assets/metadata-dialog-BlvHIpPe.js +0 -1
  822. codex/static_root/assets/metadata-dialog-BlvHIpPe.js.br +0 -0
  823. codex/static_root/assets/metadata-dialog-BlvHIpPe.js.gz +0 -0
  824. codex/static_root/assets/metadata-dialog-CNrYFyyx.87cbe14f8752.css +0 -1
  825. codex/static_root/assets/metadata-dialog-CNrYFyyx.87cbe14f8752.css.br +0 -0
  826. codex/static_root/assets/metadata-dialog-CNrYFyyx.87cbe14f8752.css.gz +0 -0
  827. codex/static_root/assets/metadata-dialog-CNrYFyyx.css +0 -1
  828. codex/static_root/assets/metadata-dialog-CNrYFyyx.css.br +0 -0
  829. codex/static_root/assets/metadata-dialog-CNrYFyyx.css.gz +0 -0
  830. codex/static_root/assets/pdf-doc-x5fJF5N8.67b5456d222e.js +0 -35
  831. codex/static_root/assets/pdf-doc-x5fJF5N8.67b5456d222e.js.br +0 -0
  832. codex/static_root/assets/pdf-doc-x5fJF5N8.67b5456d222e.js.gz +0 -0
  833. codex/static_root/assets/pdf-doc-x5fJF5N8.js +0 -35
  834. codex/static_root/assets/pdf-doc-x5fJF5N8.js.br +0 -0
  835. codex/static_root/assets/pdf-doc-x5fJF5N8.js.gz +0 -0
  836. codex/static_root/assets/reader-BDo-P8Ww.46ce70253011.css +0 -1
  837. codex/static_root/assets/reader-BDo-P8Ww.46ce70253011.css.br +0 -0
  838. codex/static_root/assets/reader-BDo-P8Ww.46ce70253011.css.gz +0 -0
  839. codex/static_root/assets/reader-BDo-P8Ww.css +0 -1
  840. codex/static_root/assets/reader-BDo-P8Ww.css.br +0 -0
  841. codex/static_root/assets/reader-BDo-P8Ww.css.gz +0 -0
  842. codex/static_root/assets/reader-CXmsYhy4.3f98149c37d9.js +0 -2
  843. codex/static_root/assets/reader-CXmsYhy4.3f98149c37d9.js.br +0 -0
  844. codex/static_root/assets/reader-CXmsYhy4.3f98149c37d9.js.gz +0 -0
  845. codex/static_root/assets/reader-CXmsYhy4.js +0 -2
  846. codex/static_root/assets/reader-CXmsYhy4.js.br +0 -0
  847. codex/static_root/assets/reader-CXmsYhy4.js.gz +0 -0
  848. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css +0 -1
  849. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css.br +0 -0
  850. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css.gz +0 -0
  851. codex/static_root/assets/stats-tab-BTZLMBCU.css +0 -1
  852. codex/static_root/assets/stats-tab-BTZLMBCU.css.br +0 -0
  853. codex/static_root/assets/stats-tab-BTZLMBCU.css.gz +0 -0
  854. codex/static_root/assets/stats-tab-xK8fU37K.24f65a4c7479.js +0 -1
  855. codex/static_root/assets/stats-tab-xK8fU37K.24f65a4c7479.js.br +0 -0
  856. codex/static_root/assets/stats-tab-xK8fU37K.24f65a4c7479.js.gz +0 -0
  857. codex/static_root/assets/stats-tab-xK8fU37K.js +0 -1
  858. codex/static_root/assets/stats-tab-xK8fU37K.js.br +0 -0
  859. codex/static_root/assets/stats-tab-xK8fU37K.js.gz +0 -0
  860. codex/static_root/assets/task-tab-D-j9C5kb.f8defdafa5d1.js +0 -1
  861. codex/static_root/assets/task-tab-D-j9C5kb.f8defdafa5d1.js.br +0 -0
  862. codex/static_root/assets/task-tab-D-j9C5kb.f8defdafa5d1.js.gz +0 -0
  863. codex/static_root/assets/task-tab-D-j9C5kb.js +0 -1
  864. codex/static_root/assets/task-tab-D-j9C5kb.js.br +0 -0
  865. codex/static_root/assets/task-tab-D-j9C5kb.js.gz +0 -0
  866. codex/static_root/assets/task-tab-DoX9OA37.9bb355f36dbf.css +0 -1
  867. codex/static_root/assets/task-tab-DoX9OA37.9bb355f36dbf.css.gz +0 -0
  868. codex/static_root/assets/task-tab-DoX9OA37.css +0 -1
  869. codex/static_root/assets/task-tab-DoX9OA37.css.gz +0 -0
  870. codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +0 -1
  871. codex/static_root/assets/to-case-kUm8NPpW.js +0 -1
  872. codex/static_root/assets/user-tab-DMcrO04A.501813c958bd.js +0 -1
  873. codex/static_root/assets/user-tab-DMcrO04A.501813c958bd.js.br +0 -0
  874. codex/static_root/assets/user-tab-DMcrO04A.501813c958bd.js.gz +0 -0
  875. codex/static_root/assets/user-tab-DMcrO04A.js +0 -1
  876. codex/static_root/assets/user-tab-DMcrO04A.js.br +0 -0
  877. codex/static_root/assets/user-tab-DMcrO04A.js.gz +0 -0
  878. codex/static_root/img/logo-32.dbb98f6dfde4.webp +0 -0
  879. codex/static_root/img/logo-maskable-180.201cbde62d55.webp +0 -0
  880. codex/static_root/img/logo-maskable.d618c2d7943f.svg +0 -83
  881. codex/static_root/img/logo-maskable.d618c2d7943f.svg.br +0 -0
  882. codex/static_root/img/logo-maskable.d618c2d7943f.svg.gz +0 -0
  883. codex/static_root/img/logo.3fb129bfbd92.svg +0 -80
  884. codex/static_root/img/logo.3fb129bfbd92.svg.br +0 -0
  885. codex/static_root/img/logo.3fb129bfbd92.svg.gz +0 -0
  886. codex/static_root/img/missing-cover.8c8c1d4f6782.webp +0 -0
  887. codex/static_root/img/missing-cover.webp +0 -0
  888. codex/static_root/img/missing-page.71bb10f7c72a.svg +0 -13
  889. codex/static_root/img/missing-page.71bb10f7c72a.svg.br +0 -0
  890. codex/static_root/img/missing-page.71bb10f7c72a.svg.gz +0 -0
  891. codex/static_root/img/missing-page.svg +0 -13
  892. codex/static_root/img/missing-page.svg.br +0 -0
  893. codex/static_root/img/missing-page.svg.gz +0 -0
  894. codex/static_root/js/choices-admin.bb730e4f26ac.json +0 -1
  895. codex/static_root/js/choices-admin.bb730e4f26ac.json.br +0 -0
  896. codex/static_root/js/choices-admin.bb730e4f26ac.json.gz +0 -0
  897. codex/static_root/js/choices.fedcbd1ec085.json +0 -1
  898. codex/static_root/js/choices.fedcbd1ec085.json.br +0 -0
  899. codex/static_root/js/choices.fedcbd1ec085.json.gz +0 -0
  900. codex/static_root/manifest.8e6c35516db7.json +0 -607
  901. codex/static_root/manifest.8e6c35516db7.json.br +0 -0
  902. codex/static_root/manifest.8e6c35516db7.json.gz +0 -0
  903. codex/views/browser/browser_annotations.py +0 -298
  904. codex/views/browser/browser_order_by.py +0 -108
  905. codex/views/browser/const.py +0 -3
  906. codex/views/browser/session.py +0 -12
  907. codex/views/cover.py +0 -59
  908. codex-1.5.19.dist-info/RECORD +0 -936
  909. {codex-1.5.19.dist-info → codex-1.6.0.dist-info}/LICENSE +0 -0
  910. {codex-1.5.19.dist-info → codex-1.6.0.dist-info}/WHEEL +0 -0
  911. {codex-1.5.19.dist-info → codex-1.6.0.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,9 @@
1
1
  """Views for reading comic books."""
2
2
 
3
+ from io import BytesIO
4
+
3
5
  from comicbox.box import Comicbox
4
- from django.http import HttpResponse
6
+ from django.http.response import StreamingHttpResponse
5
7
  from drf_spectacular.types import OpenApiTypes
6
8
  from drf_spectacular.utils import OpenApiParameter, extend_schema
7
9
  from rest_framework.exceptions import NotFound
@@ -11,9 +13,13 @@ from codex.logger.logging import get_logger
11
13
  from codex.models import Comic
12
14
  from codex.views.bookmark import BookmarkBaseView
13
15
  from codex.views.const import FALSY
16
+ from codex.views.util import chunker
14
17
 
15
18
  LOG = get_logger(__name__)
16
- PDF_MIME_TYPE = "application/pdf"
19
+ _PDF_MIME_TYPE = "application/pdf"
20
+ # Most pages seem to be 2.5 Mb
21
+ # largest pages I've seen were 9 Mb
22
+ _PAGE_CHUNK_SIZE = (1024**2) * 3 # 3 Mb
17
23
 
18
24
 
19
25
  class IgnoreClientContentNegotiation(BaseContentNegotiation):
@@ -63,11 +69,15 @@ class ReaderPageView(BookmarkBaseView):
63
69
  page = self.kwargs.get("page")
64
70
  to_pixmap = self.request.GET.get("pixmap", "").lower() not in FALSY
65
71
  if comic.file_type == Comic.FileType.PDF.value and not to_pixmap:
66
- content_type = PDF_MIME_TYPE
72
+ content_type = _PDF_MIME_TYPE
67
73
  else:
68
74
  content_type = self.content_type
75
+
69
76
  with Comicbox(comic.path) as cb:
70
77
  page_image = cb.get_page_by_index(page, to_pixmap=to_pixmap)
78
+ if not page_image:
79
+ page_image = b""
80
+
71
81
  return page_image, content_type
72
82
 
73
83
  @extend_schema(
@@ -77,7 +87,7 @@ class ReaderPageView(BookmarkBaseView):
77
87
  ],
78
88
  responses={
79
89
  (200, content_type): OpenApiTypes.BINARY,
80
- (200, PDF_MIME_TYPE): OpenApiTypes.BINARY,
90
+ (200, _PDF_MIME_TYPE): OpenApiTypes.BINARY,
81
91
  },
82
92
  )
83
93
  def get(self, *_args, **_kwargs):
@@ -98,4 +108,5 @@ class ReaderPageView(BookmarkBaseView):
98
108
  LOG.warning(exc)
99
109
  raise NotFound(detail="comic page not found") from exc
100
110
  else:
101
- return HttpResponse(page_image, content_type=content_type)
111
+ page_chunker = chunker(BytesIO(page_image), _PAGE_CHUNK_SIZE)
112
+ return StreamingHttpResponse(page_chunker, content_type=content_type)
@@ -1,214 +1,29 @@
1
1
  """Views for reading comic books."""
2
2
 
3
3
  from comicbox.box import Comicbox
4
- from django.db.models import F, IntegerField, Value
5
4
  from django.urls import reverse
6
- from drf_spectacular.types import OpenApiTypes
7
- from drf_spectacular.utils import OpenApiParameter, extend_schema
5
+ from drf_spectacular.utils import extend_schema
8
6
  from rest_framework.exceptions import NotFound
9
7
  from rest_framework.response import Response
10
8
 
11
9
  from codex.librarian.importer.tasks import LazyImportComicsTask
12
10
  from codex.librarian.mp_queue import LIBRARIAN_QUEUE
13
11
  from codex.logger.logging import get_logger
14
- from codex.models import AdminFlag, Bookmark, Comic
15
- from codex.serializers.reader import ReaderComicsSerializer
12
+ from codex.serializers.reader import ReaderComicsSerializer, ReaderViewInputSerializer
16
13
  from codex.serializers.redirect import ReaderRedirectSerializer
17
- from codex.views.bookmark import BookmarkBaseView
18
- from codex.views.session import BrowserSessionViewBase
14
+ from codex.views.reader.arcs import ReaderArcsView
15
+ from codex.views.util import pop_name
19
16
 
20
17
  LOG = get_logger(__name__)
21
18
 
22
19
 
23
- class ReaderView(BookmarkBaseView):
20
+ class ReaderView(ReaderArcsView):
24
21
  """Get info for displaying comic pages."""
25
22
 
26
23
  serializer_class = ReaderComicsSerializer
27
24
 
28
- SETTINGS_ATTRS = ("fit_to", "two_pages", "reading_direction")
29
- _COMIC_FIELDS = (
30
- "file_type",
31
- "issue_number",
32
- "issue_suffix",
33
- "page_count",
34
- "series",
35
- "volume",
36
- "reading_direction",
37
- "updated_at",
38
- )
39
- _VALID_ARC_GROUPS = frozenset({"f", "s", "a"})
40
-
41
- def _get_comics_list(self):
42
- """Get the reader naviation group filter."""
43
- select_related = ("series", "volume")
44
- prefetch_related = ()
45
-
46
- arc_group = self.params.get("arc_group")
47
- if arc_group == "a":
48
- # for story arcs
49
- rel = "story_arc_numbers__story_arc"
50
- fields = self._COMIC_FIELDS
51
- arc_name_rel = "story_arc_numbers__story_arc__name"
52
- arc_pk_rel = "story_arc_numbers__story_arc__pk"
53
- prefetch_related = (*prefetch_related, "story_arc_numbers__story_arc")
54
- arc_index = F("story_arc_numbers__number")
55
- ordering = ("arc_index", "date", *Comic.ORDERING)
56
- arc_pk_select_related = ()
57
- arc_pk_only = ("pk",)
58
- elif arc_group == self.FOLDER_GROUP:
59
- # folder mode
60
- rel = "parent_folder"
61
- fields = (*self._COMIC_FIELDS, "parent_folder")
62
- arc_pk_rel = "parent_folder__pk"
63
- arc_name_rel = "parent_folder__name"
64
- select_related = (*select_related, "parent_folder")
65
- arc_index = Value(None, IntegerField())
66
- ordering = ("path", "pk")
67
- arc_pk_select_related = ("parent_folder",)
68
- arc_pk_only = arc_pk_select_related
69
- else:
70
- # browser mode.
71
- rel = "series"
72
- fields = self._COMIC_FIELDS
73
- arc_pk_rel = "series__pk"
74
- arc_name_rel = "series__name"
75
- arc_index = Value(None, IntegerField())
76
- ordering = Comic.ORDERING
77
- arc_pk_select_related = ("series",)
78
- arc_pk_only = arc_pk_select_related
79
-
80
- arc_pk = self.params.get("arc_pk")
81
- if not arc_pk:
82
- # Get the correct arc/folder/series if not submitted with a post.
83
- pk = self.kwargs.get("pk")
84
- arc_pk_qs = Comic.objects.filter(pk=pk)
85
- arc_pk_qs = arc_pk_qs.select_related(*arc_pk_select_related)
86
- arc_pk_qs = arc_pk_qs.prefetch_related(*prefetch_related)
87
- arc_pk_comic = (
88
- arc_pk_qs.annotate(arc_pk=F(arc_pk_rel)).only(*arc_pk_only).first()
89
- )
90
- arc_pk = arc_pk_comic.arc_pk # type: ignore
91
-
92
- group_acl_filter = self.get_group_acl_filter(Comic)
93
- nav_filter = {rel: arc_pk}
94
-
95
- qs = (
96
- Comic.objects.filter(group_acl_filter)
97
- .filter(**nav_filter)
98
- .select_related(*select_related)
99
- .prefetch_related(*prefetch_related)
100
- .only(*fields)
101
- .annotate(
102
- series_name=F("series__name"),
103
- volume_name=F("volume__name"),
104
- issue_count=F("volume__issue_count"),
105
- )
106
- .annotate(
107
- arc_pk=F(arc_pk_rel),
108
- arc_name=F(arc_name_rel),
109
- arc_index=arc_index,
110
- )
111
- .annotate(mtime=F("updated_at"))
112
- )
113
-
114
- return qs.order_by(*ordering)
115
-
116
- def _append_with_settings(self, book, bookmark_filter):
117
- """Get bookmarks and filename and append to book list."""
118
- book.settings = (
119
- Bookmark.objects.filter(**bookmark_filter, comic=book)
120
- .only(*self.SETTINGS_ATTRS)
121
- .first()
122
- )
123
- return book
124
-
125
- def _get_book_collection(self):
126
- """Get the -1, +1 window around the current issue.
127
-
128
- Uses iteration in python. There are some complicated ways of
129
- doing this with __gt[0] & __lt[0] in the db, but I think they
130
- might be even more expensive.
131
-
132
- Yields 1 to 3 books
133
- """
134
- comics = self._get_comics_list()
135
- bookmark_filter = self.get_bookmark_filter()
136
- books = {}
137
- prev_book = None
138
- pk = self.kwargs.get("pk")
139
- for index, book in enumerate(comics):
140
- if books:
141
- # after match set next comic and break
142
- books["next"] = self._append_with_settings(book, bookmark_filter)
143
- break
144
- if book.pk == pk:
145
- # first match. set previous and current comic
146
- if prev_book:
147
- books["prev"] = self._append_with_settings(
148
- prev_book, bookmark_filter
149
- )
150
- # create extra current book attrs:
151
- if book.arc_index is None: # type: ignore
152
- book.arc_index = index + 1 # type: ignore
153
- book.filename = book.filename() # type: ignore
154
-
155
- books["current"] = self._append_with_settings(book, bookmark_filter)
156
- else:
157
- # Haven't matched yet, so set the previous comic
158
- prev_book = book
159
- return books, comics.count()
160
-
161
- def _get_folder_arc(self, book):
162
- """Create the folder arc."""
163
- efv_flag = (
164
- AdminFlag.objects.only("on")
165
- .get(key=AdminFlag.FlagChoices.FOLDER_VIEW.value)
166
- .on
167
- )
168
-
169
- if efv_flag:
170
- folder_arc = {
171
- "group": self.FOLDER_GROUP,
172
- "pk": book.parent_folder.pk,
173
- "name": book.parent_folder.name,
174
- }
175
- else:
176
- folder_arc = None
177
- return folder_arc
178
-
179
- def _get_arcs(self, book):
180
- """Get all series/folder/story arcs."""
181
- # create top arcs
182
- folder_arc = self._get_folder_arc(book)
183
- series_arc = {"group": "s", "pk": book.series.pk, "name": book.series.name}
184
-
185
- # order top arcs
186
- top_group = self.params.get("top_group")
187
- if top_group == self.FOLDER_GROUP and folder_arc:
188
- arc = folder_arc
189
- other_arc = series_arc
190
- else:
191
- arc = series_arc
192
- other_arc = folder_arc
193
-
194
- arcs = []
195
- arcs.append(arc)
196
- if other_arc:
197
- arcs.append(other_arc)
198
-
199
- # story arcs
200
- sas = []
201
- for san in book.story_arc_numbers.all():
202
- sa = san.story_arc
203
- arc = {
204
- "group": "a",
205
- "pk": sa.pk,
206
- "name": sa.name,
207
- }
208
- sas.append(arc)
209
- sas = sorted(sas, key=lambda x: x["name"])
210
- arcs += sas
211
- return arcs
25
+ SESSION_KEY = "reader"
26
+ TARGET = "reader"
212
27
 
213
28
  def _raise_not_found(self):
214
29
  """Raise not found exception."""
@@ -247,7 +62,7 @@ class ReaderView(BookmarkBaseView):
247
62
  def get_object(self):
248
63
  """Get the previous and next comics in a group or story arc."""
249
64
  # Books
250
- books, arc_count = self._get_book_collection()
65
+ books = self.get_book_collection()
251
66
 
252
67
  current = books.get("current")
253
68
  if not current:
@@ -264,57 +79,28 @@ class ReaderView(BookmarkBaseView):
264
79
  }
265
80
 
266
81
  # Arcs
267
- arcs = self._get_arcs(current)
82
+ arcs, mtime = self.get_arcs(current)
268
83
 
269
- arc_group = self.params.get("arc_group")
270
- arc = {
271
- "group": arc_group,
272
- "pk": current.arc_pk, # type: ignore
273
- "index": current.arc_index, # type: ignore
274
- "count": arc_count,
275
- }
84
+ arc = self.params.get("arc", {})
85
+ if not arc.get("group"):
86
+ arc["group"] = current.arc_group # type: ignore
87
+ if not arc.get("pks"):
88
+ arc["pks"] = (current.arc_pk,) # type: ignore
89
+ arc["index"] = current.arc_index # type:ignore
90
+ arc["count"] = current.arc_count # type: ignore
91
+
92
+ close_route = self.get_last_route()
93
+ close_route = pop_name(close_route)
276
94
 
277
95
  return {
278
96
  "books": books,
279
97
  "arcs": arcs,
280
98
  "arc": arc,
99
+ "close_route": close_route,
100
+ "mtime": mtime,
281
101
  }
282
102
 
283
- def _parse_params(self):
284
- data = self.request.GET
285
-
286
- # PARAMS
287
- session = self.request.session.get(BrowserSessionViewBase.SESSION_KEY, {})
288
- top_group = session.get("top_group", "s")
289
- arc_group = data.get("arcGroup")
290
- if not arc_group:
291
- arc_group = top_group
292
-
293
- if arc_group not in self._VALID_ARC_GROUPS:
294
- arc_group = "s"
295
-
296
- arc_pk = data.get("arcPk")
297
- if arc_pk is not None:
298
- arc_pk = int(arc_pk)
299
- elif top_group == "a":
300
- last_route = session.get("route", {})
301
- arc_pk = last_route.get("pk")
302
-
303
- params = {
304
- "arc_group": arc_group,
305
- "arc_pk": arc_pk,
306
- "top_group": top_group,
307
- }
308
- self.params = params
309
-
310
- @extend_schema(
311
- parameters=[
312
- OpenApiParameter(
313
- "arcGroup", OpenApiTypes.STR, enum=sorted(_VALID_ARC_GROUPS)
314
- ),
315
- OpenApiParameter("arcPk", OpenApiTypes.INT),
316
- ]
317
- )
103
+ @extend_schema(parameters=[ReaderViewInputSerializer])
318
104
  def get(self, *args, **kwargs):
319
105
  """Get the book info."""
320
106
  self._parse_params()
@@ -1,10 +1,12 @@
1
1
  """Reader session view."""
2
2
 
3
3
  from codex.serializers.reader import ReaderSettingsSerializer
4
- from codex.views.session import ReaderSessionViewBase, SessionViewBase
4
+ from codex.views.settings import SettingsView
5
5
 
6
6
 
7
- class ReaderSessionView(ReaderSessionViewBase, SessionViewBase):
7
+ class ReaderSettingsView(SettingsView):
8
8
  """Get Reader Settings."""
9
9
 
10
10
  serializer_class = ReaderSettingsSerializer # type: ignore
11
+
12
+ SESSION_KEY = "reader"
codex/views/session.py CHANGED
@@ -1,69 +1,23 @@
1
1
  """Manage user sessions with appropriate defaults."""
2
2
 
3
- from abc import ABC, abstractmethod
3
+ from abc import ABC
4
4
  from copy import deepcopy
5
5
  from types import MappingProxyType
6
6
 
7
- from drf_spectacular.utils import extend_schema
8
- from rest_framework.generics import GenericAPIView
9
- from rest_framework.response import Response
10
-
11
7
  from codex.logger.logging import get_logger
12
8
  from codex.serializers.choices import DEFAULTS
13
- from codex.views.auth import IsAuthenticatedOrEnabledNonUsers
9
+ from codex.views.auth import AuthFilterGenericAPIView
10
+ from codex.views.util import pop_name
14
11
 
15
12
  LOG = get_logger(__name__)
16
13
 
17
14
 
18
- class SessionViewBaseBase(GenericAPIView, ABC):
15
+ class SessionView(AuthFilterGenericAPIView, ABC):
19
16
  """Generic Session View."""
20
17
 
21
18
  # Must override both of these
22
- @property
23
- @classmethod
24
- @abstractmethod
25
- def SESSION_KEY(cls): # noqa: N802, type: ignore
26
- """Implement the session key string."""
27
- raise NotImplementedError
28
-
29
- SESSION_DEFAULTS = MappingProxyType({})
30
-
31
- @classmethod
32
- def _get_source_values_or_set_defaults(cls, defaults_dict, source_dict, data):
33
- """Recursively copy source_dict values into data or use defaults."""
34
- result = deepcopy(data)
35
- for key, default_value in defaults_dict.items():
36
- result[key] = source_dict.get(key, default_value)
37
- if result[key] is None:
38
- # extra check for migrated or corrupt data
39
- result[key] = default_value
40
- if isinstance(default_value, dict):
41
- result[key] = cls._get_source_values_or_set_defaults(
42
- default_value, source_dict.get(key, {}), result[key]
43
- )
44
- return result
45
-
46
- def get_from_session(self, key): # only used by frontend
47
- """Get one key from the session or its default."""
48
- session = self.request.session.get(self.SESSION_KEY, self.SESSION_DEFAULTS)
49
- return session.get(key, self.SESSION_DEFAULTS[key])
50
-
51
- def save_params_to_session(self, params): # reader session & browser final
52
- """Save the session from params with defaults for missing values."""
53
- try:
54
- data = self._get_source_values_or_set_defaults(
55
- self.SESSION_DEFAULTS, params, {}
56
- )
57
- self.request.session[self.SESSION_KEY] = data
58
- self.request.session.save()
59
- except Exception as exc:
60
- LOG.warning(f"Saving params to session: {exc}")
19
+ SESSION_KEY = ""
61
20
 
62
-
63
- class BrowserSessionViewBase(SessionViewBaseBase):
64
- """Browser session base."""
65
-
66
- SESSION_KEY = "browser" # type: ignore
67
21
  CONTRIBUTOR_PERSON_UI_FIELD = "contributors"
68
22
  STORY_ARC_UI_FIELD = "story_arcs"
69
23
  IDENTIFIER_TYPE_UI_FIELD = "identifier_type"
@@ -83,7 +37,6 @@ class BrowserSessionViewBase(SessionViewBaseBase):
83
37
  "locations": [],
84
38
  "monochrome": [],
85
39
  "original_format": [],
86
- "q": "",
87
40
  "reading_direction": [],
88
41
  "series_groups": [],
89
42
  "stories": [],
@@ -95,63 +48,100 @@ class BrowserSessionViewBase(SessionViewBaseBase):
95
48
  }
96
49
  )
97
50
  FILTER_ATTRIBUTES = frozenset(_DYNAMIC_FILTER_DEFAULTS.keys())
51
+ BROWSER_SESSION_KEY = "browser"
52
+ READER_SESSION_KEY = "reader"
98
53
  SESSION_DEFAULTS = MappingProxyType(
99
54
  {
100
- "filters": {
101
- "bookmark": DEFAULTS["bookmarkFilter"],
102
- **_DYNAMIC_FILTER_DEFAULTS,
55
+ BROWSER_SESSION_KEY: {
56
+ "breadcrumbs": DEFAULTS["breadcrumbs"],
57
+ "filters": {
58
+ "bookmark": DEFAULTS["bookmarkFilter"],
59
+ **_DYNAMIC_FILTER_DEFAULTS,
60
+ },
61
+ "order_by": DEFAULTS["orderBy"],
62
+ "order_reverse": DEFAULTS["orderReverse"],
63
+ "q": DEFAULTS["q"],
64
+ "search_results_limit": DEFAULTS["searchResultsLimit"],
65
+ "show": DEFAULTS["show"],
66
+ "dynamic_covers": DEFAULTS["dynamicCovers"],
67
+ "custom_covers": DEFAULTS["customCovers"],
68
+ "twenty_four_hour_time": DEFAULTS["twentyFourHourTime"],
69
+ "top_group": DEFAULTS["topGroup"],
70
+ },
71
+ READER_SESSION_KEY: {
72
+ "finish_on_last_page": DEFAULTS["finishOnLastPage"],
73
+ "fit_to": DEFAULTS["fitTo"],
74
+ "two_pages": DEFAULTS["twoPages"],
75
+ "read_rtl_in_reverse": DEFAULTS["readRtlInReverse"],
76
+ "reading_direction": DEFAULTS["readingDirection"],
103
77
  },
104
- "order_by": DEFAULTS["orderBy"],
105
- "order_reverse": False,
106
- "q": DEFAULTS["q"],
107
- "route": DEFAULTS["route"],
108
- "show": DEFAULTS["show"],
109
- "twenty_four_hour_time": False,
110
- "top_group": DEFAULTS["topGroup"],
111
- }
112
- )
113
-
114
-
115
- class ReaderSessionViewBase(SessionViewBaseBase):
116
- """Reader session base."""
117
-
118
- SESSION_KEY = "reader" # type: ignore
119
- SESSION_DEFAULTS = MappingProxyType(
120
- {
121
- "fit_to": DEFAULTS["fitTo"],
122
- "two_pages": False,
123
- "reading_direction": DEFAULTS["readingDirection"],
124
78
  }
125
79
  )
126
80
 
81
+ def get_from_session(
82
+ self, key, default=None, session_key=None
83
+ ): # only used by frontend
84
+ """Get one key from the session or its default."""
85
+ if session_key is None:
86
+ session_key = self.SESSION_KEY
87
+ session = self.request.session.get(
88
+ session_key, self.SESSION_DEFAULTS[session_key]
89
+ )
90
+ if default is None:
91
+ default = self.SESSION_DEFAULTS[session_key][key]
92
+ return session.get(key, default)
93
+
94
+ def get_last_route(self, name=True):
95
+ """Get the last route from the breadcrumbs."""
96
+ breadcrumbs = self.get_from_session(
97
+ "breadcrumbs", session_key=self.BROWSER_SESSION_KEY
98
+ )
99
+ if not breadcrumbs:
100
+ breadcrumbs = DEFAULTS["breadcrumbs"]
101
+ last_route = breadcrumbs[-1]
102
+ if not name:
103
+ last_route = pop_name(last_route)
127
104
 
128
- class SessionViewBase(SessionViewBaseBase, ABC):
129
- """Session view for retrieving stored settings."""
130
-
131
- permission_classes = (IsAuthenticatedOrEnabledNonUsers,)
105
+ return last_route
132
106
 
133
- def load_params_from_session(self):
134
- """Set the params from view session, creating missing values from defaults."""
135
- session = self.request.session.get(self.SESSION_KEY, self.SESSION_DEFAULTS)
136
- return self._get_source_values_or_set_defaults(
137
- self.SESSION_DEFAULTS, session, {}
138
- )
107
+ @classmethod
108
+ def _get_source_values_or_set_defaults(cls, defaults_dict, source_dict, data):
109
+ """Recursively copy source_dict values into data or use defaults."""
110
+ result = deepcopy(data)
111
+ for key, default_value in defaults_dict.items():
112
+ result[key] = source_dict.get(key, default_value)
113
+ if result[key] is None:
114
+ # extra check for migrated or corrupt data
115
+ result[key] = default_value
116
+ if isinstance(default_value, dict):
117
+ result[key] = cls._get_source_values_or_set_defaults(
118
+ default_value, source_dict.get(key, {}), result[key]
119
+ )
120
+ return result
139
121
 
140
- def get(self, *args, **kwargs):
141
- """Get session settings."""
142
- params = self.load_params_from_session()
143
- data = {}
144
- for key, filter_name in params.get("filters", {}).items():
145
- if filter_name:
146
- data[key] = filter_name
147
- params["filters"] = data
148
- serializer = self.get_serializer(params)
149
- return Response(serializer.data)
122
+ def load_params_from_session(self, session_key=None, only=None):
123
+ """Get session settings with defaults."""
124
+ if not session_key:
125
+ session_key = self.SESSION_KEY
126
+ if only:
127
+ defaults = {}
128
+ for key in only:
129
+ if key:
130
+ defaults[key] = self.SESSION_DEFAULTS[session_key][key]
131
+ else:
132
+ defaults = self.SESSION_DEFAULTS[session_key]
133
+
134
+ session = self.request.session.get(session_key, defaults)
135
+ return self._get_source_values_or_set_defaults(defaults, session, {})
150
136
 
151
- @extend_schema(responses=None)
152
- def put(self, *args, **kwargs):
153
- """Update session settings."""
154
- serializer = self.get_serializer(data=self.request.data)
155
- serializer.is_valid(raise_exception=True)
156
- self.save_params_to_session(serializer.validated_data)
157
- return Response()
137
+ def save_params_to_session(self, params): # reader session & browser final
138
+ """Save the session from params with defaults for missing values."""
139
+ try:
140
+ # Deepcopy this so serializing the values later later for http response doesn't alter them
141
+ params = deepcopy(dict(params))
142
+ defaults = self.SESSION_DEFAULTS[self.SESSION_KEY]
143
+ data = self._get_source_values_or_set_defaults(defaults, params, {})
144
+ self.request.session[self.SESSION_KEY] = data
145
+ self.request.session.save()
146
+ except Exception as exc:
147
+ LOG.warning(f"Saving params to session: {exc}")
@@ -0,0 +1,47 @@
1
+ """Settings View."""
2
+
3
+ from abc import ABC
4
+
5
+ from drf_spectacular.utils import extend_schema
6
+ from rest_framework.response import Response
7
+
8
+ from codex.serializers.settings import SettingsSerializer
9
+ from codex.views.session import SessionView
10
+ from codex.views.util import reparse_json_query_params
11
+
12
+ _GET_JSON_PARAMS = frozenset({"only"})
13
+
14
+
15
+ class SettingsView(SessionView, ABC):
16
+ """Settings View."""
17
+
18
+ input_serializer_class = SettingsSerializer
19
+
20
+ def validate_settings_get(self, _validated_data, params: dict) -> dict:
21
+ """Change bad settings."""
22
+ return params
23
+
24
+ @extend_schema(parameters=[SettingsSerializer])
25
+ def get(self, *args, **kwargs):
26
+ """Get session settings."""
27
+ data = self.request.GET
28
+ data = reparse_json_query_params(data, _GET_JSON_PARAMS)
29
+ serializer = self.input_serializer_class(data=data)
30
+ serializer.is_valid(raise_exception=True)
31
+ validated_data = serializer.validated_data
32
+ only = validated_data.get("only") if validated_data else None # type: ignore
33
+ params = self.load_params_from_session(only=only)
34
+ params = self.validate_settings_get(validated_data, params)
35
+ serializer = self.get_serializer(params)
36
+ return Response(serializer.data)
37
+
38
+ @extend_schema(responses=None)
39
+ def patch(self, *args, **kwargs):
40
+ """Update session settings."""
41
+ data = self.request.data # type: ignore
42
+ serializer = self.get_serializer(data=data)
43
+ serializer.is_valid(raise_exception=True)
44
+ params = self.load_params_from_session()
45
+ params.update(serializer.validated_data)
46
+ self.save_params_to_session(params)
47
+ return Response()
codex/views/template.py CHANGED
@@ -13,7 +13,7 @@ class TemplateXMLRenderer(TemplateHTMLRenderer):
13
13
  format = "xml"
14
14
 
15
15
 
16
- class SimpleAPIView(APIView):
16
+ class CodexAPIView(APIView):
17
17
  """APIView with a simple getter and no data."""
18
18
 
19
19
  content_type = "application/json"
@@ -26,14 +26,14 @@ class SimpleAPIView(APIView):
26
26
  )
27
27
 
28
28
 
29
- class CodexTemplateView(SimpleAPIView):
29
+ class CodexTemplateView(CodexAPIView):
30
30
  """HTML Template View."""
31
31
 
32
32
  renderer_classes = (TemplateHTMLRenderer,)
33
33
  content_type = "text/html"
34
34
 
35
35
 
36
- class CodexXMLTemplateView(SimpleAPIView):
36
+ class CodexXMLTemplateView(CodexAPIView):
37
37
  """XML Template View."""
38
38
 
39
39
  renderer_classes = (TemplateXMLRenderer,)