codex 1.6.0__py3-none-any.whl → 1.6.0a1__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 (768) hide show
  1. codex/exceptions.py +18 -22
  2. codex/integrity.py +3 -66
  3. codex/librarian/covers/coverd.py +2 -2
  4. codex/librarian/covers/create.py +29 -50
  5. codex/librarian/covers/path.py +6 -8
  6. codex/librarian/covers/purge.py +18 -28
  7. codex/librarian/covers/tasks.py +2 -3
  8. codex/librarian/importer/{aggregate.py → aggregate_metadata.py} +88 -58
  9. codex/librarian/importer/{extract.py → clean_metadata.py} +4 -33
  10. codex/librarian/importer/const.py +13 -46
  11. codex/librarian/importer/create_comics.py +17 -101
  12. codex/librarian/importer/create_fks.py +134 -99
  13. codex/librarian/importer/deleted.py +28 -103
  14. codex/librarian/importer/failed_imports.py +64 -42
  15. codex/librarian/importer/importerd.py +290 -71
  16. codex/librarian/importer/link_comics.py +26 -26
  17. codex/librarian/importer/moved.py +81 -128
  18. codex/librarian/importer/query_fks.py +122 -142
  19. codex/librarian/importer/status.py +39 -8
  20. codex/librarian/importer/tasks.py +9 -26
  21. codex/librarian/importer/update_comics.py +89 -0
  22. codex/librarian/janitor/cleanup.py +2 -26
  23. codex/librarian/janitor/failed_imports.py +1 -1
  24. codex/librarian/janitor/janitor.py +1 -13
  25. codex/librarian/janitor/janitord.py +1 -1
  26. codex/librarian/janitor/status.py +0 -1
  27. codex/librarian/janitor/tasks.py +0 -5
  28. codex/librarian/librariand.py +5 -9
  29. codex/librarian/notifier/notifierd.py +1 -1
  30. codex/librarian/notifier/tasks.py +4 -4
  31. codex/librarian/search/update.py +1 -1
  32. codex/librarian/watchdog/db_snapshot.py +6 -9
  33. codex/librarian/watchdog/emitter.py +0 -3
  34. codex/librarian/watchdog/event_batcherd.py +3 -19
  35. codex/librarian/watchdog/events.py +36 -176
  36. codex/librarian/watchdog/observers.py +3 -13
  37. codex/middleware.py +2 -1
  38. codex/migrations/0027_sort_name.py +149 -0
  39. codex/models/admin.py +2 -0
  40. codex/models/base.py +0 -3
  41. codex/models/comic.py +9 -13
  42. codex/models/{util.py → const.py} +2 -15
  43. codex/models/groups.py +14 -28
  44. codex/models/library.py +3 -11
  45. codex/models/paths.py +14 -61
  46. codex/permissions.py +1 -1
  47. codex/search/query.py +2 -25
  48. codex/search/writing.py +1 -1
  49. codex/serializers/admin.py +44 -53
  50. codex/serializers/browser/choices.py +42 -37
  51. codex/serializers/browser/filters.py +131 -32
  52. codex/serializers/browser/metadata.py +1 -45
  53. codex/serializers/browser/mixins.py +4 -37
  54. codex/serializers/browser/page.py +6 -3
  55. codex/serializers/browser/settings.py +11 -42
  56. codex/serializers/choices.py +1 -14
  57. codex/serializers/models/pycountry.py +51 -11
  58. codex/serializers/opds/v2.py +16 -10
  59. codex/serializers/opds/v2_unused.py +3 -5
  60. codex/serializers/reader.py +7 -21
  61. codex/serializers/redirect.py +2 -3
  62. codex/serializers/route.py +7 -16
  63. codex/settings/settings.py +8 -23
  64. codex/startup.py +3 -77
  65. codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js +1 -0
  66. codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js.br +0 -0
  67. codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js.gz +0 -0
  68. codex/static_root/assets/VCheckbox-u_aWQJA7.js +1 -0
  69. codex/static_root/assets/VCheckbox-u_aWQJA7.js.br +0 -0
  70. codex/static_root/assets/VCheckbox-u_aWQJA7.js.gz +0 -0
  71. codex/static_root/assets/{VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js → VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js} +1 -1
  72. codex/static_root/assets/VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js.br +0 -0
  73. codex/static_root/assets/VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js.gz +0 -0
  74. codex/static_root/assets/{VCheckboxBtn-DxNOKONG.js → VCheckboxBtn-yCN3TB6A.js} +1 -1
  75. codex/static_root/assets/VCheckboxBtn-yCN3TB6A.js.br +0 -0
  76. codex/static_root/assets/VCheckboxBtn-yCN3TB6A.js.gz +0 -0
  77. codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js +1 -0
  78. codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js.br +0 -0
  79. codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js.gz +0 -0
  80. codex/static_root/assets/VCombobox-dsIKbjps.js +1 -0
  81. codex/static_root/assets/VCombobox-dsIKbjps.js.br +0 -0
  82. codex/static_root/assets/VCombobox-dsIKbjps.js.gz +0 -0
  83. codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js +1 -0
  84. codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js.br +0 -0
  85. codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js.gz +0 -0
  86. codex/static_root/assets/VDialog-BNHg8txZ.js +1 -0
  87. codex/static_root/assets/VDialog-BNHg8txZ.js.br +0 -0
  88. codex/static_root/assets/VDialog-BNHg8txZ.js.gz +0 -0
  89. codex/static_root/assets/{VDialog-lQ13FnRR.1c801ad756b2.css → VDialog-C5Nxd_zP.css} +1 -1
  90. codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
  91. codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
  92. codex/static_root/assets/{VDialog-lQ13FnRR.css → VDialog-C5Nxd_zP.d327f505ef0e.css} +1 -1
  93. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
  94. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
  95. codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js +1 -0
  96. codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js.br +0 -0
  97. codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js.gz +0 -0
  98. codex/static_root/assets/VExpansionPanels-CX4W4vZK.js +1 -0
  99. codex/static_root/assets/VExpansionPanels-CX4W4vZK.js.br +0 -0
  100. codex/static_root/assets/VExpansionPanels-CX4W4vZK.js.gz +0 -0
  101. codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js +1 -0
  102. codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js.br +0 -0
  103. codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js.gz +0 -0
  104. codex/static_root/assets/VRadioGroup-jCr3CAsk.js +1 -0
  105. codex/static_root/assets/VRadioGroup-jCr3CAsk.js.br +0 -0
  106. codex/static_root/assets/VRadioGroup-jCr3CAsk.js.gz +0 -0
  107. codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js +1 -0
  108. codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js.br +0 -0
  109. codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js.gz +0 -0
  110. codex/static_root/assets/VSelect--XHNpxvB.js +1 -0
  111. codex/static_root/assets/VSelect--XHNpxvB.js.br +0 -0
  112. codex/static_root/assets/VSelect--XHNpxvB.js.gz +0 -0
  113. codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js +1 -0
  114. codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js.br +0 -0
  115. codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js.gz +0 -0
  116. codex/static_root/assets/VSelectionControl-CVIKc_ra.js +1 -0
  117. codex/static_root/assets/VSelectionControl-CVIKc_ra.js.br +0 -0
  118. codex/static_root/assets/VSelectionControl-CVIKc_ra.js.gz +0 -0
  119. codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js +1 -0
  120. codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js.br +0 -0
  121. codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js.gz +0 -0
  122. codex/static_root/assets/VSlideGroup-CZw5_eNv.js +1 -0
  123. codex/static_root/assets/VSlideGroup-CZw5_eNv.js.br +0 -0
  124. codex/static_root/assets/VSlideGroup-CZw5_eNv.js.gz +0 -0
  125. codex/static_root/assets/{VTable-2RHFqfg6.f0f2106b158b.js → VTable-hGJDFK5t.d2f84d68747b.js} +1 -1
  126. codex/static_root/assets/VTable-hGJDFK5t.d2f84d68747b.js.br +0 -0
  127. codex/static_root/assets/VTable-hGJDFK5t.d2f84d68747b.js.gz +0 -0
  128. codex/static_root/assets/{VTable-2RHFqfg6.js → VTable-hGJDFK5t.js} +1 -1
  129. codex/static_root/assets/VTable-hGJDFK5t.js.br +0 -0
  130. codex/static_root/assets/VTable-hGJDFK5t.js.gz +0 -0
  131. codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js +1 -0
  132. codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js.br +0 -0
  133. codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js.gz +0 -0
  134. codex/static_root/assets/VTextField-C8y3lyz9.js +1 -0
  135. codex/static_root/assets/VTextField-C8y3lyz9.js.br +0 -0
  136. codex/static_root/assets/VTextField-C8y3lyz9.js.gz +0 -0
  137. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css +1 -0
  138. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
  139. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
  140. codex/static_root/assets/VTextField-DuIIp-VY.css +1 -0
  141. codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
  142. codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
  143. codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js +1 -0
  144. codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js.br +0 -0
  145. codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js.gz +0 -0
  146. codex/static_root/assets/VWindowItem-BHpZIa_r.js +1 -0
  147. codex/static_root/assets/VWindowItem-BHpZIa_r.js.br +0 -0
  148. codex/static_root/assets/VWindowItem-BHpZIa_r.js.gz +0 -0
  149. codex/static_root/assets/{admin-B8Bp2Uih.82c98714bcf1.css → admin-BbrOU9Y9.29936748af9e.css} +1 -1
  150. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
  151. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
  152. codex/static_root/assets/{admin-B8Bp2Uih.css → admin-BbrOU9Y9.css} +1 -1
  153. codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
  154. codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
  155. codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js +1 -0
  156. codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js.br +0 -0
  157. codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js.gz +0 -0
  158. codex/static_root/assets/admin-Bvd-SvGS.js +1 -0
  159. codex/static_root/assets/admin-Bvd-SvGS.js.br +0 -0
  160. codex/static_root/assets/admin-Bvd-SvGS.js.gz +0 -0
  161. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +1 -0
  162. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
  163. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
  164. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +1 -0
  165. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
  166. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
  167. codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js +30 -0
  168. codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js.br +0 -0
  169. codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js.gz +0 -0
  170. codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js +30 -0
  171. codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js.br +0 -0
  172. codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js.gz +0 -0
  173. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css +1 -0
  174. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.br +0 -0
  175. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.gz +0 -0
  176. codex/static_root/assets/browser-CZeVqRNy.css +1 -0
  177. codex/static_root/assets/browser-CZeVqRNy.css.br +0 -0
  178. codex/static_root/assets/browser-CZeVqRNy.css.gz +0 -0
  179. codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js +1 -0
  180. codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js.br +0 -0
  181. codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js.gz +0 -0
  182. codex/static_root/assets/browser-rc5ObkeN.js +1 -0
  183. codex/static_root/assets/browser-rc5ObkeN.js.br +0 -0
  184. codex/static_root/assets/browser-rc5ObkeN.js.gz +0 -0
  185. codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js +1 -0
  186. codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js.br +0 -0
  187. codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js.gz +0 -0
  188. codex/static_root/assets/change-password-dialog-CRHk2tjq.js +1 -0
  189. codex/static_root/assets/change-password-dialog-CRHk2tjq.js.br +0 -0
  190. codex/static_root/assets/change-password-dialog-CRHk2tjq.js.gz +0 -0
  191. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +1 -0
  192. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
  193. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
  194. codex/static_root/assets/change-password-dialog-HvIvmmIE.css +1 -0
  195. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
  196. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
  197. codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js +1 -0
  198. codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js.br +0 -0
  199. codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js.gz +0 -0
  200. codex/static_root/assets/confirm-dialog-DnHKM44R.js +1 -0
  201. codex/static_root/assets/confirm-dialog-DnHKM44R.js.br +0 -0
  202. codex/static_root/assets/confirm-dialog-DnHKM44R.js.gz +0 -0
  203. codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js +1 -0
  204. codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js.br +0 -0
  205. codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js.gz +0 -0
  206. codex/static_root/assets/datetime-column-BEqDvdeI.js +1 -0
  207. codex/static_root/assets/datetime-column-BEqDvdeI.js.br +0 -0
  208. codex/static_root/assets/datetime-column-BEqDvdeI.js.gz +0 -0
  209. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +1 -0
  210. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
  211. codex/static_root/assets/datetime-column-Dg_RxTVM.css +1 -0
  212. codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
  213. codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js +1 -0
  214. codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js.br +0 -0
  215. codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js.gz +0 -0
  216. codex/static_root/assets/filter-_wYL6jQC.js +1 -0
  217. codex/static_root/assets/filter-_wYL6jQC.js.br +0 -0
  218. codex/static_root/assets/filter-_wYL6jQC.js.gz +0 -0
  219. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +1 -0
  220. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
  221. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
  222. codex/static_root/assets/flag-tab-BacZLlVi.css +1 -0
  223. codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
  224. codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
  225. codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js +1 -0
  226. codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js.br +0 -0
  227. codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js.gz +0 -0
  228. codex/static_root/assets/flag-tab-Dpyvhwb_.js +1 -0
  229. codex/static_root/assets/flag-tab-Dpyvhwb_.js.br +0 -0
  230. codex/static_root/assets/flag-tab-Dpyvhwb_.js.gz +0 -0
  231. codex/static_root/assets/{group-tab-D5vWrihT.1afb2ce453ff.js → group-tab-DAjvuSUO.4494d4846fa6.js} +1 -1
  232. codex/static_root/assets/group-tab-DAjvuSUO.4494d4846fa6.js.br +0 -0
  233. codex/static_root/assets/group-tab-DAjvuSUO.4494d4846fa6.js.gz +0 -0
  234. codex/static_root/assets/{group-tab-D5vWrihT.js → group-tab-DAjvuSUO.js} +1 -1
  235. codex/static_root/assets/group-tab-DAjvuSUO.js.br +0 -0
  236. codex/static_root/assets/group-tab-DAjvuSUO.js.gz +0 -0
  237. codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js +1 -0
  238. codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js.br +0 -0
  239. codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js.gz +0 -0
  240. codex/static_root/assets/http-error-CDQ-296a.js +1 -0
  241. codex/static_root/assets/http-error-CDQ-296a.js.br +0 -0
  242. codex/static_root/assets/http-error-CDQ-296a.js.gz +0 -0
  243. codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css +1 -0
  244. codex/static_root/assets/{http-error-DKulXuTl.95e42988d5d5.css.br → http-error-_wlDqEcp.5b78d3e332c9.css.br} +0 -0
  245. codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css.gz +0 -0
  246. codex/static_root/assets/http-error-_wlDqEcp.css +1 -0
  247. codex/static_root/assets/{http-error-DKulXuTl.css.br → http-error-_wlDqEcp.css.br} +0 -0
  248. codex/static_root/assets/http-error-_wlDqEcp.css.gz +0 -0
  249. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +1 -0
  250. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
  251. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
  252. codex/static_root/assets/index-DUjeNg4x.js +1 -0
  253. codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
  254. codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
  255. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +1 -0
  256. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +2 -0
  257. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
  258. codex/static_root/assets/library-tab-CXbkxNZt.css +1 -0
  259. codex/static_root/assets/library-tab-CXbkxNZt.css.br +2 -0
  260. codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
  261. codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js +1 -0
  262. codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js.br +0 -0
  263. codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js.gz +0 -0
  264. codex/static_root/assets/library-tab-LsexSRi2.js +1 -0
  265. codex/static_root/assets/library-tab-LsexSRi2.js.br +0 -0
  266. codex/static_root/assets/library-tab-LsexSRi2.js.gz +0 -0
  267. codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js +32 -0
  268. codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js.br +0 -0
  269. codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js.gz +0 -0
  270. codex/static_root/assets/main-CjPnwTfF.js +32 -0
  271. codex/static_root/assets/main-CjPnwTfF.js.br +0 -0
  272. codex/static_root/assets/main-CjPnwTfF.js.gz +0 -0
  273. codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css → main-sK7HC5h4.6f04ddfc5f1a.css} +1 -1
  274. codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
  275. codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css.gz → main-sK7HC5h4.6f04ddfc5f1a.css.gz} +0 -0
  276. codex/static_root/assets/{main-Ztp855PW.css → main-sK7HC5h4.css} +1 -1
  277. codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
  278. codex/static_root/assets/{main-Ztp855PW.css.gz → main-sK7HC5h4.css.gz} +0 -0
  279. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css +1 -0
  280. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
  281. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
  282. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css +1 -0
  283. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
  284. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
  285. codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js +1 -0
  286. codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js.br +0 -0
  287. codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js.gz +0 -0
  288. codex/static_root/assets/pagination-toolbar-B955aa-d.js +1 -0
  289. codex/static_root/assets/pagination-toolbar-B955aa-d.js.br +0 -0
  290. codex/static_root/assets/pagination-toolbar-B955aa-d.js.gz +0 -0
  291. codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js +35 -0
  292. codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js.br +0 -0
  293. codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js.gz +0 -0
  294. codex/static_root/assets/pdf-doc-DPDmmEEp.js +35 -0
  295. codex/static_root/assets/pdf-doc-DPDmmEEp.js.br +0 -0
  296. codex/static_root/assets/pdf-doc-DPDmmEEp.js.gz +0 -0
  297. codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js +2 -0
  298. codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js.br +0 -0
  299. codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js.gz +0 -0
  300. codex/static_root/assets/reader-BuMh4x3f.js +2 -0
  301. codex/static_root/assets/reader-BuMh4x3f.js.br +0 -0
  302. codex/static_root/assets/reader-BuMh4x3f.js.gz +0 -0
  303. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css +1 -0
  304. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.br +0 -0
  305. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.gz +0 -0
  306. codex/static_root/assets/reader-Dspy46e8.css +1 -0
  307. codex/static_root/assets/reader-Dspy46e8.css.br +0 -0
  308. codex/static_root/assets/reader-Dspy46e8.css.gz +0 -0
  309. codex/static_root/assets/{relation-chips-Bsc-gUxE.98e65384c3c9.css → relation-chips-D6T6FX7G.css} +1 -1
  310. codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
  311. codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
  312. codex/static_root/assets/{relation-chips-Bsc-gUxE.css → relation-chips-D6T6FX7G.db095b2497e4.css} +1 -1
  313. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
  314. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
  315. codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js +1 -0
  316. codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js.br +0 -0
  317. codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js.gz +0 -0
  318. codex/static_root/assets/relation-chips-DN_pPmnS.js +1 -0
  319. codex/static_root/assets/relation-chips-DN_pPmnS.js.br +0 -0
  320. codex/static_root/assets/relation-chips-DN_pPmnS.js.gz +0 -0
  321. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css +1 -0
  322. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.br +0 -0
  323. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.gz +0 -0
  324. codex/static_root/assets/settings-drawer-BgUazs7O.css +1 -0
  325. codex/static_root/assets/settings-drawer-BgUazs7O.css.br +0 -0
  326. codex/static_root/assets/settings-drawer-BgUazs7O.css.gz +0 -0
  327. codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js +2 -0
  328. codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js.br +0 -0
  329. codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js.gz +0 -0
  330. codex/static_root/assets/settings-drawer-D4FgDG4z.js +2 -0
  331. codex/static_root/assets/settings-drawer-D4FgDG4z.js.br +0 -0
  332. codex/static_root/assets/settings-drawer-D4FgDG4z.js.gz +0 -0
  333. codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js +1 -0
  334. codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js.br +0 -0
  335. codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js.gz +0 -0
  336. codex/static_root/assets/stats-tab-AwbCeW63.js +1 -0
  337. codex/static_root/assets/stats-tab-AwbCeW63.js.br +0 -0
  338. codex/static_root/assets/stats-tab-AwbCeW63.js.gz +0 -0
  339. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +1 -0
  340. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +2 -0
  341. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
  342. codex/static_root/assets/stats-tab-TsyG3wH_.css +1 -0
  343. codex/static_root/assets/stats-tab-TsyG3wH_.css.br +2 -0
  344. codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
  345. codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js +1 -0
  346. codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js.br +0 -0
  347. codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js.gz +0 -0
  348. codex/static_root/assets/task-tab-DeHQTHuz.js +1 -0
  349. codex/static_root/assets/task-tab-DeHQTHuz.js.br +0 -0
  350. codex/static_root/assets/task-tab-DeHQTHuz.js.gz +0 -0
  351. codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +1 -0
  352. codex/static_root/assets/to-case-kUm8NPpW.js +1 -0
  353. codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js +1 -0
  354. codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js.br +0 -0
  355. codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js.gz +0 -0
  356. codex/static_root/assets/unauthorized-BXCbvRsL.js +1 -0
  357. codex/static_root/assets/unauthorized-BXCbvRsL.js.br +0 -0
  358. codex/static_root/assets/unauthorized-BXCbvRsL.js.gz +0 -0
  359. codex/static_root/assets/{unauthorized-CibJYD8o.4c2de98c2b53.css → unauthorized-DWgytUow.ca2f13dbdbf5.css} +1 -1
  360. codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.br +0 -0
  361. codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.gz +0 -0
  362. codex/static_root/assets/{unauthorized-CibJYD8o.css → unauthorized-DWgytUow.css} +1 -1
  363. codex/static_root/assets/unauthorized-DWgytUow.css.br +0 -0
  364. codex/static_root/assets/unauthorized-DWgytUow.css.gz +0 -0
  365. codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js +1 -0
  366. codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js.br +0 -0
  367. codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js.gz +0 -0
  368. codex/static_root/assets/user-tab-Dq4qYzgp.js +1 -0
  369. codex/static_root/assets/user-tab-Dq4qYzgp.js.br +0 -0
  370. codex/static_root/assets/user-tab-Dq4qYzgp.js.gz +0 -0
  371. codex/static_root/js/choices-admin.248384ece3f6.json +1 -0
  372. codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
  373. codex/static_root/js/choices-admin.248384ece3f6.json.gz +0 -0
  374. codex/static_root/js/choices-admin.json +1 -1
  375. codex/static_root/js/choices-admin.json.br +0 -0
  376. codex/static_root/js/choices-admin.json.gz +0 -0
  377. codex/static_root/js/choices.b60817ba0a93.json +1 -0
  378. codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
  379. codex/static_root/js/choices.b60817ba0a93.json.gz +0 -0
  380. codex/static_root/js/choices.json +1 -1
  381. codex/static_root/js/choices.json.br +0 -0
  382. codex/static_root/js/choices.json.gz +0 -0
  383. codex/static_root/manifest.b5fe5d7ef44e.json +641 -0
  384. codex/static_root/manifest.b5fe5d7ef44e.json.br +0 -0
  385. codex/static_root/manifest.b5fe5d7ef44e.json.gz +0 -0
  386. codex/static_root/manifest.json +298 -279
  387. codex/static_root/manifest.json.br +0 -0
  388. codex/static_root/manifest.json.gz +0 -0
  389. codex/static_root/staticfiles.json +1 -1
  390. codex/status_controller.py +2 -11
  391. codex/urls/api/auth.py +1 -2
  392. codex/urls/api/browser.py +4 -13
  393. codex/urls/api/reader.py +9 -3
  394. codex/urls/api/v3.py +0 -2
  395. codex/urls/opds/binary.py +1 -1
  396. codex/views/admin/api_key.py +8 -7
  397. codex/views/admin/flag.py +7 -2
  398. codex/views/admin/group.py +6 -2
  399. codex/views/admin/library.py +37 -44
  400. codex/views/admin/stats.py +83 -20
  401. codex/views/admin/tasks.py +12 -26
  402. codex/views/admin/user.py +9 -3
  403. codex/views/auth.py +54 -31
  404. codex/views/bookmark.py +72 -92
  405. codex/views/browser/base.py +90 -42
  406. codex/views/browser/browser.py +371 -92
  407. codex/views/browser/{annotations.py → browser_annotations.py} +121 -67
  408. codex/views/browser/browser_breadcrumbs.py +175 -0
  409. codex/views/browser/browser_order_by.py +63 -0
  410. codex/views/browser/choices.py +85 -38
  411. codex/views/browser/const.py +3 -0
  412. codex/views/browser/filters/annotations.py +19 -87
  413. codex/views/browser/filters/bookmark.py +12 -24
  414. codex/views/browser/filters/field.py +6 -8
  415. codex/views/browser/filters/group.py +14 -35
  416. codex/views/browser/filters/search.py +20 -31
  417. codex/views/browser/metadata.py +73 -112
  418. codex/views/browser/session.py +12 -0
  419. codex/views/const.py +0 -140
  420. codex/views/cover.py +77 -0
  421. codex/views/download.py +8 -5
  422. codex/views/frontend.py +2 -4
  423. codex/views/opds/authentication_v1.py +45 -46
  424. codex/views/opds/binary.py +10 -5
  425. codex/views/opds/const.py +0 -13
  426. codex/views/opds/urls.py +4 -6
  427. codex/views/opds/util.py +12 -4
  428. codex/views/opds/v1/entry/links.py +33 -18
  429. codex/views/opds/v1/facets.py +7 -7
  430. codex/views/opds/v1/feed.py +45 -55
  431. codex/views/opds/v1/links.py +5 -7
  432. codex/views/opds/v1/opensearch_v1.py +6 -2
  433. codex/views/opds/v2/feed.py +26 -25
  434. codex/views/opds/v2/links.py +2 -4
  435. codex/views/opds/v2/publications.py +54 -34
  436. codex/views/opds/v2/top_links.py +2 -2
  437. codex/views/reader/page.py +8 -15
  438. codex/views/reader/reader.py +322 -19
  439. codex/views/reader/{settings.py → session.py} +2 -4
  440. codex/views/session.py +102 -83
  441. codex/views/template.py +3 -3
  442. codex/views/version.py +2 -2
  443. codex/websockets/consumers.py +10 -14
  444. {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/METADATA +15 -22
  445. {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/RECORD +448 -466
  446. codex/librarian/importer/cache.py +0 -93
  447. codex/librarian/importer/create_covers.py +0 -96
  448. codex/librarian/importer/importer.py +0 -96
  449. codex/librarian/importer/init.py +0 -265
  450. codex/librarian/importer/link_covers.py +0 -73
  451. codex/librarian/importer/query_covers.py +0 -52
  452. codex/migrations/0027_import_order_and_covers.py +0 -280
  453. codex/serializers/browser/mtime.py +0 -21
  454. codex/serializers/fields.py +0 -149
  455. codex/serializers/settings.py +0 -12
  456. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js +0 -1
  457. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.br +0 -0
  458. codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.gz +0 -0
  459. codex/static_root/assets/VCheckbox-DanrzdHo.js +0 -1
  460. codex/static_root/assets/VCheckbox-DanrzdHo.js.br +0 -0
  461. codex/static_root/assets/VCheckbox-DanrzdHo.js.gz +0 -0
  462. codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.br +0 -0
  463. codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.gz +0 -0
  464. codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.br +0 -0
  465. codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.gz +0 -0
  466. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js +0 -1
  467. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.br +0 -0
  468. codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.gz +0 -0
  469. codex/static_root/assets/VCombobox-DDvdoPTG.js +0 -1
  470. codex/static_root/assets/VCombobox-DDvdoPTG.js.br +0 -0
  471. codex/static_root/assets/VCombobox-DDvdoPTG.js.gz +0 -0
  472. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js +0 -1
  473. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.br +0 -0
  474. codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.gz +0 -0
  475. codex/static_root/assets/VDialog-Bd5dN_E1.js +0 -1
  476. codex/static_root/assets/VDialog-Bd5dN_E1.js.br +0 -0
  477. codex/static_root/assets/VDialog-Bd5dN_E1.js.gz +0 -0
  478. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
  479. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
  480. codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
  481. codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
  482. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js +0 -1
  483. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.br +0 -0
  484. codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.gz +0 -0
  485. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js +0 -1
  486. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.br +0 -0
  487. codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.gz +0 -0
  488. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js +0 -1
  489. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.br +0 -0
  490. codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.gz +0 -0
  491. codex/static_root/assets/VRadioGroup-DKb8XPr-.js +0 -1
  492. codex/static_root/assets/VRadioGroup-DKb8XPr-.js.br +0 -0
  493. codex/static_root/assets/VRadioGroup-DKb8XPr-.js.gz +0 -0
  494. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js +0 -1
  495. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.br +0 -0
  496. codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.gz +0 -0
  497. codex/static_root/assets/VSelect-C_yPbyCG.js +0 -1
  498. codex/static_root/assets/VSelect-C_yPbyCG.js.br +0 -0
  499. codex/static_root/assets/VSelect-C_yPbyCG.js.gz +0 -0
  500. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js +0 -1
  501. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.br +0 -0
  502. codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.gz +0 -0
  503. codex/static_root/assets/VSelectionControl-CmFPvGMS.js +0 -1
  504. codex/static_root/assets/VSelectionControl-CmFPvGMS.js.br +0 -0
  505. codex/static_root/assets/VSelectionControl-CmFPvGMS.js.gz +0 -0
  506. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js +0 -1
  507. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.br +0 -0
  508. codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.gz +0 -0
  509. codex/static_root/assets/VSlideGroup-D5FZWsM1.js +0 -1
  510. codex/static_root/assets/VSlideGroup-D5FZWsM1.js.br +0 -0
  511. codex/static_root/assets/VSlideGroup-D5FZWsM1.js.gz +0 -0
  512. codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.br +0 -0
  513. codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.gz +0 -0
  514. codex/static_root/assets/VTable-2RHFqfg6.js.br +0 -0
  515. codex/static_root/assets/VTable-2RHFqfg6.js.gz +0 -0
  516. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +0 -1
  517. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
  518. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
  519. codex/static_root/assets/VTextField-BssVoDhB.css +0 -1
  520. codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
  521. codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
  522. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js +0 -1
  523. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.br +0 -0
  524. codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.gz +0 -0
  525. codex/static_root/assets/VTextField-o1N9ylIb.js +0 -1
  526. codex/static_root/assets/VTextField-o1N9ylIb.js.br +0 -0
  527. codex/static_root/assets/VTextField-o1N9ylIb.js.gz +0 -0
  528. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js +0 -1
  529. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.br +0 -0
  530. codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.gz +0 -0
  531. codex/static_root/assets/VWindowItem-C_zvuz9C.js +0 -1
  532. codex/static_root/assets/VWindowItem-C_zvuz9C.js.br +0 -0
  533. codex/static_root/assets/VWindowItem-C_zvuz9C.js.gz +0 -0
  534. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
  535. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
  536. codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
  537. codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
  538. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js +0 -1
  539. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.br +0 -0
  540. codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.gz +0 -0
  541. codex/static_root/assets/admin-DHyHEWZ3.js +0 -1
  542. codex/static_root/assets/admin-DHyHEWZ3.js.br +0 -0
  543. codex/static_root/assets/admin-DHyHEWZ3.js.gz +0 -0
  544. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js +0 -30
  545. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.br +0 -0
  546. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.gz +0 -0
  547. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js +0 -30
  548. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.br +0 -0
  549. codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.gz +0 -0
  550. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css +0 -1
  551. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
  552. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
  553. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css +0 -1
  554. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
  555. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
  556. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js +0 -1
  557. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.br +0 -0
  558. codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.gz +0 -0
  559. codex/static_root/assets/browser-D0Vjmmp_.js +0 -1
  560. codex/static_root/assets/browser-D0Vjmmp_.js.br +0 -0
  561. codex/static_root/assets/browser-D0Vjmmp_.js.gz +0 -0
  562. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css +0 -1
  563. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.br +0 -0
  564. codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.gz +0 -0
  565. codex/static_root/assets/browser-D7QQDneO.css +0 -1
  566. codex/static_root/assets/browser-D7QQDneO.css.br +0 -0
  567. codex/static_root/assets/browser-D7QQDneO.css.gz +0 -0
  568. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +0 -1
  569. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
  570. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
  571. codex/static_root/assets/change-password-dialog-0u6IJA87.css +0 -1
  572. codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
  573. codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
  574. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js +0 -1
  575. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.br +0 -0
  576. codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.gz +0 -0
  577. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js +0 -1
  578. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.br +0 -0
  579. codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.gz +0 -0
  580. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js +0 -1
  581. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.br +0 -0
  582. codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.gz +0 -0
  583. codex/static_root/assets/confirm-dialog-DCKvg3ai.js +0 -1
  584. codex/static_root/assets/confirm-dialog-DCKvg3ai.js.br +0 -0
  585. codex/static_root/assets/confirm-dialog-DCKvg3ai.js.gz +0 -0
  586. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +0 -1
  587. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
  588. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +0 -1
  589. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
  590. codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +0 -1
  591. codex/static_root/assets/datetime-column-DeCthByU.css +0 -1
  592. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js +0 -1
  593. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.br +0 -0
  594. codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.gz +0 -0
  595. codex/static_root/assets/datetime-column-jHKA_uNm.js +0 -1
  596. codex/static_root/assets/datetime-column-jHKA_uNm.js.br +0 -0
  597. codex/static_root/assets/datetime-column-jHKA_uNm.js.gz +0 -0
  598. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js +0 -1
  599. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.br +0 -0
  600. codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.gz +0 -0
  601. codex/static_root/assets/filter-DgPg2ts9.js +0 -1
  602. codex/static_root/assets/filter-DgPg2ts9.js.br +0 -0
  603. codex/static_root/assets/filter-DgPg2ts9.js.gz +0 -0
  604. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js +0 -1
  605. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.br +0 -0
  606. codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.gz +0 -0
  607. codex/static_root/assets/flag-tab-9qxOALxP.js +0 -1
  608. codex/static_root/assets/flag-tab-9qxOALxP.js.br +0 -0
  609. codex/static_root/assets/flag-tab-9qxOALxP.js.gz +0 -0
  610. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +0 -1
  611. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
  612. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
  613. codex/static_root/assets/flag-tab-BNwbLfrN.css +0 -1
  614. codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
  615. codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
  616. codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.br +0 -0
  617. codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.gz +0 -0
  618. codex/static_root/assets/group-tab-D5vWrihT.js.br +0 -0
  619. codex/static_root/assets/group-tab-D5vWrihT.js.gz +0 -0
  620. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js +0 -1
  621. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.br +0 -0
  622. codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.gz +0 -0
  623. codex/static_root/assets/http-error-B4FHoKfs.js +0 -1
  624. codex/static_root/assets/http-error-B4FHoKfs.js.br +0 -0
  625. codex/static_root/assets/http-error-B4FHoKfs.js.gz +0 -0
  626. codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css +0 -1
  627. codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css.gz +0 -0
  628. codex/static_root/assets/http-error-DKulXuTl.css +0 -1
  629. codex/static_root/assets/http-error-DKulXuTl.css.gz +0 -0
  630. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +0 -1
  631. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
  632. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
  633. codex/static_root/assets/library-tab-BN0SfPcH.css +0 -1
  634. codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
  635. codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
  636. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js +0 -1
  637. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.br +0 -0
  638. codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.gz +0 -0
  639. codex/static_root/assets/library-tab-BWo0cmzk.js +0 -1
  640. codex/static_root/assets/library-tab-BWo0cmzk.js.br +0 -0
  641. codex/static_root/assets/library-tab-BWo0cmzk.js.gz +0 -0
  642. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  643. codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
  644. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js +0 -32
  645. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.br +0 -0
  646. codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.gz +0 -0
  647. codex/static_root/assets/main-ipmlipkF.js +0 -32
  648. codex/static_root/assets/main-ipmlipkF.js.br +0 -0
  649. codex/static_root/assets/main-ipmlipkF.js.gz +0 -0
  650. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +0 -1
  651. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
  652. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
  653. codex/static_root/assets/pagination-toolbar-BA9ifTed.css +0 -1
  654. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
  655. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
  656. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js +0 -1
  657. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.br +0 -0
  658. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.gz +0 -0
  659. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js +0 -1
  660. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.br +0 -0
  661. codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.gz +0 -0
  662. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js +0 -305
  663. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.br +0 -0
  664. codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.gz +0 -0
  665. codex/static_root/assets/pdf-doc-C2StaYUz.js +0 -305
  666. codex/static_root/assets/pdf-doc-C2StaYUz.js.br +0 -0
  667. codex/static_root/assets/pdf-doc-C2StaYUz.js.gz +0 -0
  668. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js +0 -2
  669. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.br +0 -0
  670. codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.gz +0 -0
  671. codex/static_root/assets/reader-CCnq3j1-.js +0 -2
  672. codex/static_root/assets/reader-CCnq3j1-.js.br +0 -0
  673. codex/static_root/assets/reader-CCnq3j1-.js.gz +0 -0
  674. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css +0 -1
  675. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.br +0 -0
  676. codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.gz +0 -0
  677. codex/static_root/assets/reader-CqXFa_gE.css +0 -1
  678. codex/static_root/assets/reader-CqXFa_gE.css.br +0 -0
  679. codex/static_root/assets/reader-CqXFa_gE.css.gz +0 -0
  680. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
  681. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
  682. codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
  683. codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
  684. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js +0 -1
  685. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.br +0 -0
  686. codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.gz +0 -0
  687. codex/static_root/assets/relation-chips-C9ysW0tU.js +0 -1
  688. codex/static_root/assets/relation-chips-C9ysW0tU.js.br +0 -0
  689. codex/static_root/assets/relation-chips-C9ysW0tU.js.gz +0 -0
  690. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js +0 -2
  691. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.br +0 -0
  692. codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.gz +0 -0
  693. codex/static_root/assets/settings-drawer-C0ofzAhW.js +0 -2
  694. codex/static_root/assets/settings-drawer-C0ofzAhW.js.br +0 -0
  695. codex/static_root/assets/settings-drawer-C0ofzAhW.js.gz +0 -0
  696. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +0 -1
  697. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
  698. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
  699. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +0 -1
  700. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
  701. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
  702. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +0 -1
  703. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
  704. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
  705. codex/static_root/assets/stats-tab-B4yKuY0c.css +0 -1
  706. codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
  707. codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
  708. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js +0 -1
  709. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.br +0 -0
  710. codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.gz +0 -0
  711. codex/static_root/assets/stats-tab-BqJj7j1w.js +0 -1
  712. codex/static_root/assets/stats-tab-BqJj7j1w.js.br +0 -0
  713. codex/static_root/assets/stats-tab-BqJj7j1w.js.gz +0 -0
  714. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js +0 -1
  715. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.br +0 -0
  716. codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.gz +0 -0
  717. codex/static_root/assets/task-tab-BsPzNk8S.js +0 -1
  718. codex/static_root/assets/task-tab-BsPzNk8S.js.br +0 -0
  719. codex/static_root/assets/task-tab-BsPzNk8S.js.gz +0 -0
  720. codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js +0 -1
  721. codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js.br +0 -0
  722. codex/static_root/assets/to-case-KvqtSug2.js +0 -1
  723. codex/static_root/assets/to-case-KvqtSug2.js.br +0 -0
  724. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
  725. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
  726. codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
  727. codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
  728. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js +0 -1
  729. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.br +0 -0
  730. codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.gz +0 -0
  731. codex/static_root/assets/unauthorized-DwSdNzX2.js +0 -1
  732. codex/static_root/assets/unauthorized-DwSdNzX2.js.br +0 -0
  733. codex/static_root/assets/unauthorized-DwSdNzX2.js.gz +0 -0
  734. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js +0 -1
  735. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.br +0 -0
  736. codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.gz +0 -0
  737. codex/static_root/assets/user-tab-DrJOb9Ys.js +0 -1
  738. codex/static_root/assets/user-tab-DrJOb9Ys.js.br +0 -0
  739. codex/static_root/assets/user-tab-DrJOb9Ys.js.gz +0 -0
  740. codex/static_root/js/choices-admin.3521e2a863df.json +0 -1
  741. codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
  742. codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
  743. codex/static_root/js/choices.2f6b0359e1ef.json +0 -1
  744. codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
  745. codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
  746. codex/static_root/manifest.1c84116d9a0a.json +0 -622
  747. codex/static_root/manifest.1c84116d9a0a.json.br +0 -0
  748. codex/static_root/manifest.1c84116d9a0a.json.gz +0 -0
  749. codex/util.py +0 -12
  750. codex/views/admin/auth.py +0 -28
  751. codex/views/browser/breadcrumbs.py +0 -202
  752. codex/views/browser/cover.py +0 -165
  753. codex/views/browser/mtime.py +0 -61
  754. codex/views/browser/order_by.py +0 -65
  755. codex/views/browser/paginate.py +0 -106
  756. codex/views/browser/settings.py +0 -66
  757. codex/views/browser/title.py +0 -48
  758. codex/views/browser/validate.py +0 -161
  759. codex/views/opds/auth.py +0 -17
  760. codex/views/public.py +0 -36
  761. codex/views/reader/arcs.py +0 -92
  762. codex/views/reader/books.py +0 -181
  763. codex/views/reader/init.py +0 -125
  764. codex/views/settings.py +0 -47
  765. codex/views/util.py +0 -77
  766. {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/LICENSE +0 -0
  767. {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/WHEEL +0 -0
  768. {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/entry_points.txt +0 -0
@@ -1,30 +1,293 @@
1
1
  """Views for reading comic books."""
2
2
 
3
+ from copy import deepcopy
4
+ from types import MappingProxyType
5
+
3
6
  from comicbox.box import Comicbox
7
+ from django.db.models import F, IntegerField, Value
4
8
  from django.urls import reverse
5
- from drf_spectacular.utils import extend_schema
6
- from rest_framework.exceptions import NotFound
9
+ from drf_spectacular.types import OpenApiTypes
10
+ from drf_spectacular.utils import OpenApiParameter, extend_schema
11
+ from rest_framework.exceptions import NotFound, ValidationError
7
12
  from rest_framework.response import Response
8
13
 
9
14
  from codex.librarian.importer.tasks import LazyImportComicsTask
10
15
  from codex.librarian.mp_queue import LIBRARIAN_QUEUE
11
16
  from codex.logger.logging import get_logger
12
- from codex.serializers.reader import ReaderComicsSerializer, ReaderViewInputSerializer
17
+ from codex.models import AdminFlag, Bookmark, Comic
18
+ from codex.serializers.reader import ReaderArcSerializer, ReaderComicsSerializer
13
19
  from codex.serializers.redirect import ReaderRedirectSerializer
14
- from codex.views.reader.arcs import ReaderArcsView
15
- from codex.views.util import pop_name
20
+ from codex.views.bookmark import BookmarkBaseView
21
+ from codex.views.mixins import SharedAnnotationsMixin
22
+ from codex.views.session import BrowserSessionViewBase
16
23
 
17
24
  LOG = get_logger(__name__)
18
25
 
19
26
 
20
- class ReaderView(ReaderArcsView):
27
+ _MIN_CRUMB_WALKBACK_LEN = 3
28
+
29
+
30
+ class ReaderView(
31
+ BookmarkBaseView,
32
+ BrowserSessionViewBase,
33
+ SharedAnnotationsMixin,
34
+ ):
21
35
  """Get info for displaying comic pages."""
22
36
 
23
37
  serializer_class = ReaderComicsSerializer
38
+ input_serializer_class = ReaderArcSerializer
24
39
 
25
- SESSION_KEY = "reader"
40
+ SETTINGS_ATTRS = ("fit_to", "two_pages", "reading_direction")
41
+ _COMIC_FIELDS = (
42
+ "file_type",
43
+ "issue_number",
44
+ "issue_suffix",
45
+ "page_count",
46
+ "series",
47
+ "volume",
48
+ "reading_direction",
49
+ "updated_at",
50
+ )
51
+ _VALID_ARC_GROUPS = frozenset({"f", "s", "a"})
26
52
  TARGET = "reader"
27
53
 
54
+ def __init__(self, *args, **kwargs):
55
+ """Initialize instance vars."""
56
+ super().__init__(*args, **kwargs)
57
+ self.series_pks: tuple[int, ...] = ()
58
+
59
+ def _get_series_pks_from_breadcrumbs(self):
60
+ """Get Multi-Group pks from the breadcrumbs."""
61
+ if self.series_pks:
62
+ return self.series_pks
63
+ breadcrumbs = self.get_from_session("breadcrumbs")
64
+ if breadcrumbs:
65
+ crumb = breadcrumbs[-1]
66
+ crumb_group = crumb.get("group")
67
+ if crumb_group == "v" and len(breadcrumbs) >= _MIN_CRUMB_WALKBACK_LEN:
68
+ crumb = breadcrumbs[-2]
69
+ crumb_group = crumb.get("group")
70
+ if crumb_group == "s":
71
+ self.series_pks = crumb.get("pks", ())
72
+
73
+ return self.series_pks
74
+
75
+ def _get_reader_arc_pks(
76
+ self, arc, arc_pk_select_related, prefetch_related, arc_pk_rel
77
+ ):
78
+ """Get the nav filter."""
79
+ arc_pks = arc.get("pks", ())
80
+ if not arc_pks:
81
+ comic_pk = self.kwargs["pk"]
82
+ try:
83
+ arc_pk_qs = Comic.objects.filter(pk=comic_pk)
84
+ arc_pk_qs = arc_pk_qs.select_related(*arc_pk_select_related)
85
+ arc_pk_qs = arc_pk_qs.prefetch_related(*prefetch_related)
86
+ arc_pk = arc_pk_qs.values_list(arc_pk_rel, flat=True)[0]
87
+ except IndexError:
88
+ arc_pk = 0
89
+
90
+ if arc_pk_rel == "series__pk":
91
+ multi_arc_pks = self._get_series_pks_from_breadcrumbs()
92
+ if not arc_pk or arc_pk in multi_arc_pks:
93
+ arc_pks = multi_arc_pks
94
+ if not arc_pks:
95
+ arc_pks = (arc_pk,)
96
+
97
+ return arc_pks
98
+
99
+ def _get_comics_list(self):
100
+ """Get the reader naviation group filter."""
101
+ select_related = ("series", "volume")
102
+ prefetch_related = ()
103
+
104
+ arc = self.params.get("arc", {})
105
+
106
+ arc_group = arc.get("group", "s")
107
+ if arc_group == "a":
108
+ # for story arcs
109
+ rel = "story_arc_numbers__story_arc"
110
+ fields = self._COMIC_FIELDS
111
+ arc_pk_rel = "story_arc_numbers__story_arc__pk"
112
+ prefetch_related = (*prefetch_related, "story_arc_numbers__story_arc")
113
+ arc_index = F("story_arc_numbers__number")
114
+ ordering = ("arc_index", "date")
115
+ arc_pk_select_related = ()
116
+ elif arc_group == self.FOLDER_GROUP:
117
+ # folder mode
118
+ rel = "parent_folder"
119
+ fields = (*self._COMIC_FIELDS, "parent_folder")
120
+ arc_pk_rel = "parent_folder__pk"
121
+ select_related = (*select_related, "parent_folder")
122
+ arc_index = Value(None, IntegerField())
123
+ ordering = ("path", "pk")
124
+ arc_pk_select_related = ("parent_folder",)
125
+ else:
126
+ # browser mode.
127
+ rel = "series"
128
+ fields = self._COMIC_FIELDS
129
+ arc_pk_rel = "series__pk"
130
+ arc_index = Value(None, IntegerField())
131
+ ordering = ()
132
+ arc_pk_select_related = ("series",)
133
+
134
+ arc_pks = self._get_reader_arc_pks(
135
+ arc,
136
+ arc_pk_select_related,
137
+ prefetch_related,
138
+ arc_pk_rel,
139
+ )
140
+ nav_filter = {f"{rel}__in": arc_pks}
141
+ group_acl_filter = self.get_group_acl_filter(Comic)
142
+
143
+ qs = (
144
+ Comic.objects.filter(group_acl_filter)
145
+ .filter(**nav_filter)
146
+ .select_related(*select_related)
147
+ .prefetch_related(*prefetch_related)
148
+ .only(*fields)
149
+ .annotate(
150
+ issue_count=F("volume__issue_count"),
151
+ )
152
+ .annotate(
153
+ arc_pk=F(arc_pk_rel),
154
+ arc_index=arc_index,
155
+ )
156
+ .annotate(mtime=F("updated_at"))
157
+ )
158
+ qs = self.annotate_group_names(qs, Comic)
159
+ if arc_group == "s":
160
+ show = deepcopy(self.get_from_session("show"))
161
+ show.pop("p", None)
162
+ show.pop("i", None)
163
+ qs, comic_sort_names = self.alias_sort_names(
164
+ qs, Comic, pks=arc_pks, model_group="i", show=show
165
+ )
166
+ ordering = (
167
+ *comic_sort_names,
168
+ "issue_number",
169
+ "issue_suffix",
170
+ "sort_name",
171
+ )
172
+ return qs.order_by(*ordering), arc_group
173
+
174
+ def _append_with_settings(self, book, bookmark_filter):
175
+ """Append bookmark to book list."""
176
+ book.settings = (
177
+ Bookmark.objects.filter(**bookmark_filter, comic=book)
178
+ .only(*self.SETTINGS_ATTRS)
179
+ .first()
180
+ )
181
+ return book
182
+
183
+ def _get_book_collection(self):
184
+ """Get the -1, +1 window around the current issue.
185
+
186
+ Uses iteration in python. There are some complicated ways of
187
+ doing this with __gt[0] & __lt[0] in the db, but I think they
188
+ might be even more expensive.
189
+
190
+ Yields 1 to 3 books
191
+ """
192
+ comics, arc_group = self._get_comics_list()
193
+ bookmark_filter = self.get_bookmark_filter()
194
+ books = {}
195
+ prev_book = None
196
+ pk = self.kwargs.get("pk")
197
+ for index, book in enumerate(comics):
198
+ if books:
199
+ # after match set next comic and break
200
+ books["next"] = self._append_with_settings(book, bookmark_filter)
201
+ break
202
+ if book.pk == pk:
203
+ # first match. set previous and current comic
204
+ if prev_book:
205
+ books["prev"] = self._append_with_settings(
206
+ prev_book, bookmark_filter
207
+ )
208
+ # create extra current book attrs:
209
+ if book.arc_index is None: # type: ignore
210
+ book.arc_index = index + 1 # type: ignore
211
+ book.filename = book.get_filename() # type: ignore
212
+ book.arc_group = arc_group
213
+ book.arc_count = comics.count()
214
+ books["current"] = self._append_with_settings(book, bookmark_filter)
215
+ else:
216
+ # Haven't matched yet, so set the previous comic
217
+ prev_book = book
218
+ return books
219
+
220
+ def _get_folder_arc(self, book):
221
+ """Create the folder arc."""
222
+ efv_flag = (
223
+ AdminFlag.objects.only("on")
224
+ .get(key=AdminFlag.FlagChoices.FOLDER_VIEW.value)
225
+ .on
226
+ )
227
+
228
+ if efv_flag:
229
+ folder = book.parent_folder
230
+ folder_arc = {
231
+ "group": self.FOLDER_GROUP,
232
+ "pks": (folder.pk,),
233
+ "name": folder.name,
234
+ }
235
+ else:
236
+ folder_arc = None
237
+ return folder_arc
238
+
239
+ def _get_series_arc(self, book):
240
+ """Create the series arc."""
241
+ series = book.series
242
+ if series:
243
+ arc_pks = self._get_series_pks_from_breadcrumbs()
244
+ if book.series.pk not in arc_pks:
245
+ arc_pks = (book.series.pk,)
246
+ arc = (
247
+ {"group": "s", "pks": arc_pks, "name": series.name} if series else None
248
+ )
249
+ else:
250
+ arc = None
251
+ return arc
252
+
253
+ def _get_story_arcs(self, book):
254
+ """Create story arcs."""
255
+ sas = []
256
+ for san in book.story_arc_numbers.all():
257
+ sa = san.story_arc
258
+ arc = {
259
+ "group": "a",
260
+ "pks": (sa.pk,),
261
+ "name": sa.name,
262
+ }
263
+ sas.append(arc)
264
+ return sorted(sas, key=lambda x: x["name"])
265
+
266
+ def _get_arcs(self, book):
267
+ """Get all series/folder/story arcs."""
268
+ # create top arcs
269
+ folder_arc = self._get_folder_arc(book)
270
+ series_arc = self._get_series_arc(book)
271
+
272
+ # order top arcs
273
+ top_group = self.params.get("top_group")
274
+ if top_group == self.FOLDER_GROUP and folder_arc:
275
+ arc = folder_arc
276
+ other_arc = series_arc
277
+ else:
278
+ arc = series_arc
279
+ other_arc = folder_arc
280
+
281
+ arcs = []
282
+ arcs.append(arc)
283
+ if other_arc:
284
+ arcs.append(other_arc)
285
+
286
+ # story arcs
287
+ sas = self._get_story_arcs(book)
288
+ arcs += sas
289
+ return arcs
290
+
28
291
  def _raise_not_found(self):
29
292
  """Raise not found exception."""
30
293
  pk = self.kwargs.get("pk")
@@ -62,7 +325,8 @@ class ReaderView(ReaderArcsView):
62
325
  def get_object(self):
63
326
  """Get the previous and next comics in a group or story arc."""
64
327
  # Books
65
- books = self.get_book_collection()
328
+ self.last_route = self.get_last_route()
329
+ books = self._get_book_collection()
66
330
 
67
331
  current = books.get("current")
68
332
  if not current:
@@ -79,7 +343,7 @@ class ReaderView(ReaderArcsView):
79
343
  }
80
344
 
81
345
  # Arcs
82
- arcs, mtime = self.get_arcs(current)
346
+ arcs = self._get_arcs(current)
83
347
 
84
348
  arc = self.params.get("arc", {})
85
349
  if not arc.get("group"):
@@ -88,19 +352,58 @@ class ReaderView(ReaderArcsView):
88
352
  arc["pks"] = (current.arc_pk,) # type: ignore
89
353
  arc["index"] = current.arc_index # type:ignore
90
354
  arc["count"] = current.arc_count # type: ignore
355
+ close_route = self.last_route
356
+ close_route.pop("name", None)
91
357
 
92
- close_route = self.get_last_route()
93
- close_route = pop_name(close_route)
358
+ return {"books": books, "arcs": arcs, "arc": arc, "close_route": close_route}
94
359
 
95
- return {
96
- "books": books,
97
- "arcs": arcs,
98
- "arc": arc,
99
- "close_route": close_route,
100
- "mtime": mtime,
101
- }
360
+ def _parse_params(self):
361
+ data = self.request.GET
362
+ # Hack for query_parm parser
363
+ arc_data = MappingProxyType(
364
+ {"group": data.get("arc[group]"), "pks": data.get("arc[pks]")}
365
+ )
366
+ serializer = self.input_serializer_class(data=arc_data)
367
+ arc = {}
368
+ try:
369
+ serializer.is_valid()
370
+ arc = serializer.validated_data
371
+ except ValidationError:
372
+ pass
373
+
374
+ # PARAMS
375
+ session = self.request.session.get(BrowserSessionViewBase.SESSION_KEY, {})
376
+ top_group = session.get("top_group", "s")
377
+
378
+ # arc.group validation
379
+ # Can't be in the serializer
380
+ arc_group = arc.get("group", "")
381
+ if not arc_group:
382
+ series_pks = self._get_series_pks_from_breadcrumbs()
383
+ if series_pks:
384
+ arc_group = "s"
385
+ arc["pks"] = series_pks
386
+ if not arc_group:
387
+ arc_group = top_group
388
+ if arc_group not in self._VALID_ARC_GROUPS:
389
+ arc_group = None
390
+
391
+ params = MappingProxyType(
392
+ {
393
+ "arc": arc,
394
+ "top_group": top_group,
395
+ }
396
+ )
397
+ self.params = params
102
398
 
103
- @extend_schema(parameters=[ReaderViewInputSerializer])
399
+ @extend_schema(
400
+ parameters=[
401
+ OpenApiParameter(
402
+ "arc[group]", OpenApiTypes.STR, enum=sorted(_VALID_ARC_GROUPS)
403
+ ),
404
+ OpenApiParameter("arc[pks]", OpenApiTypes.STR),
405
+ ]
406
+ )
104
407
  def get(self, *args, **kwargs):
105
408
  """Get the book info."""
106
409
  self._parse_params()
@@ -1,12 +1,10 @@
1
1
  """Reader session view."""
2
2
 
3
3
  from codex.serializers.reader import ReaderSettingsSerializer
4
- from codex.views.settings import SettingsView
4
+ from codex.views.session import ReaderSessionViewBase, SessionViewBase
5
5
 
6
6
 
7
- class ReaderSettingsView(SettingsView):
7
+ class ReaderSessionView(ReaderSessionViewBase, SessionViewBase):
8
8
  """Get Reader Settings."""
9
9
 
10
10
  serializer_class = ReaderSettingsSerializer # type: ignore
11
-
12
- SESSION_KEY = "reader"
codex/views/session.py CHANGED
@@ -3,21 +3,64 @@
3
3
  from abc import ABC
4
4
  from copy import deepcopy
5
5
  from types import MappingProxyType
6
+ from typing import ClassVar
7
+
8
+ from drf_spectacular.utils import extend_schema
9
+ from rest_framework.generics import GenericAPIView
10
+ from rest_framework.response import Response
6
11
 
7
12
  from codex.logger.logging import get_logger
8
13
  from codex.serializers.choices import DEFAULTS
9
- from codex.views.auth import AuthFilterGenericAPIView
10
- from codex.views.util import pop_name
14
+ from codex.views.auth import IsAuthenticatedOrEnabledNonUsers
11
15
 
12
16
  LOG = get_logger(__name__)
13
17
 
14
18
 
15
- class SessionView(AuthFilterGenericAPIView, ABC):
19
+ class SessionViewBaseBase(GenericAPIView, ABC):
16
20
  """Generic Session View."""
17
21
 
18
22
  # Must override both of these
19
23
  SESSION_KEY = ""
24
+ SESSION_DEFAULTS = MappingProxyType({})
25
+
26
+ @classmethod
27
+ def _get_source_values_or_set_defaults(cls, defaults_dict, source_dict, data):
28
+ """Recursively copy source_dict values into data or use defaults."""
29
+ result = deepcopy(data)
30
+ for key, default_value in defaults_dict.items():
31
+ result[key] = source_dict.get(key, default_value)
32
+ if result[key] is None:
33
+ # extra check for migrated or corrupt data
34
+ result[key] = default_value
35
+ if isinstance(default_value, dict):
36
+ result[key] = cls._get_source_values_or_set_defaults(
37
+ default_value, source_dict.get(key, {}), result[key]
38
+ )
39
+ return result
40
+
41
+ def get_from_session(self, key): # only used by frontend
42
+ """Get one key from the session or its default."""
43
+ session = self.request.session.get(self.SESSION_KEY, self.SESSION_DEFAULTS)
44
+ return session.get(key, self.SESSION_DEFAULTS[key])
20
45
 
46
+ def save_params_to_session(self, params): # reader session & browser final
47
+ """Save the session from params with defaults for missing values."""
48
+ try:
49
+ # Deepcopy this so serializing the values later later for http response doesn't alter them
50
+ params = deepcopy(dict(params))
51
+ data = self._get_source_values_or_set_defaults(
52
+ self.SESSION_DEFAULTS, params, {}
53
+ )
54
+ self.request.session[self.SESSION_KEY] = data
55
+ self.request.session.save()
56
+ except Exception as exc:
57
+ LOG.warning(f"Saving params to session: {exc}")
58
+
59
+
60
+ class BrowserSessionViewBase(SessionViewBaseBase):
61
+ """Browser session base."""
62
+
63
+ SESSION_KEY = "browser" # type: ignore
21
64
  CONTRIBUTOR_PERSON_UI_FIELD = "contributors"
22
65
  STORY_ARC_UI_FIELD = "story_arcs"
23
66
  IDENTIFIER_TYPE_UI_FIELD = "identifier_type"
@@ -37,6 +80,7 @@ class SessionView(AuthFilterGenericAPIView, ABC):
37
80
  "locations": [],
38
81
  "monochrome": [],
39
82
  "original_format": [],
83
+ "q": "",
40
84
  "reading_direction": [],
41
85
  "series_groups": [],
42
86
  "stories": [],
@@ -48,100 +92,75 @@ class SessionView(AuthFilterGenericAPIView, ABC):
48
92
  }
49
93
  )
50
94
  FILTER_ATTRIBUTES = frozenset(_DYNAMIC_FILTER_DEFAULTS.keys())
51
- BROWSER_SESSION_KEY = "browser"
52
- READER_SESSION_KEY = "reader"
53
95
  SESSION_DEFAULTS = MappingProxyType(
54
96
  {
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"],
97
+ "breadcrumbs": DEFAULTS["breadcrumbs"],
98
+ "filters": {
99
+ "bookmark": DEFAULTS["bookmarkFilter"],
100
+ **_DYNAMIC_FILTER_DEFAULTS,
77
101
  },
102
+ "order_by": DEFAULTS["orderBy"],
103
+ "order_reverse": DEFAULTS["orderReverse"],
104
+ "q": DEFAULTS["q"],
105
+ "search_results_limit": DEFAULTS["searchResultsLimit"],
106
+ "show": DEFAULTS["show"],
107
+ "twenty_four_hour_time": False,
108
+ "top_group": DEFAULTS["topGroup"],
78
109
  }
79
110
  )
80
111
 
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
112
  def get_last_route(self, name=True):
95
113
  """Get the last route from the breadcrumbs."""
96
- breadcrumbs = self.get_from_session(
97
- "breadcrumbs", session_key=self.BROWSER_SESSION_KEY
98
- )
114
+ breadcrumbs = self.get_from_session("breadcrumbs")
99
115
  if not breadcrumbs:
100
116
  breadcrumbs = DEFAULTS["breadcrumbs"]
101
117
  last_route = breadcrumbs[-1]
102
118
  if not name:
103
- last_route = pop_name(last_route)
119
+ last_route.pop("name", None)
104
120
 
105
121
  return last_route
106
122
 
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
121
123
 
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, {})
124
+ class ReaderSessionViewBase(SessionViewBaseBase):
125
+ """Reader session base."""
136
126
 
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}")
127
+ SESSION_KEY = "reader" # type: ignore
128
+ SESSION_DEFAULTS = MappingProxyType(
129
+ {
130
+ "fit_to": DEFAULTS["fitTo"],
131
+ "two_pages": False,
132
+ "reading_direction": DEFAULTS["readingDirection"],
133
+ }
134
+ )
135
+
136
+
137
+ class SessionViewBase(SessionViewBaseBase, GenericAPIView, ABC):
138
+ """Session view for retrieving stored settings."""
139
+
140
+ permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
141
+
142
+ def load_params_from_session(self):
143
+ """Set the params from view session, creating missing values from defaults."""
144
+ session = self.request.session.get(self.SESSION_KEY, self.SESSION_DEFAULTS)
145
+ return self._get_source_values_or_set_defaults(
146
+ self.SESSION_DEFAULTS, session, {}
147
+ )
148
+
149
+ def get(self, *args, **kwargs):
150
+ """Get session settings."""
151
+ params = self.load_params_from_session()
152
+ data = {}
153
+ for key, filter_name in params.get("filters", {}).items():
154
+ if filter_name:
155
+ data[key] = filter_name
156
+ params["filters"] = data
157
+ serializer = self.get_serializer(params)
158
+ return Response(serializer.data)
159
+
160
+ @extend_schema(responses=None)
161
+ def put(self, *args, **kwargs):
162
+ """Update session settings."""
163
+ serializer = self.get_serializer(data=self.request.data)
164
+ serializer.is_valid(raise_exception=True)
165
+ self.save_params_to_session(serializer.validated_data)
166
+ return Response()
codex/views/template.py CHANGED
@@ -13,7 +13,7 @@ class TemplateXMLRenderer(TemplateHTMLRenderer):
13
13
  format = "xml"
14
14
 
15
15
 
16
- class CodexAPIView(APIView):
16
+ class SimpleAPIView(APIView):
17
17
  """APIView with a simple getter and no data."""
18
18
 
19
19
  content_type = "application/json"
@@ -26,14 +26,14 @@ class CodexAPIView(APIView):
26
26
  )
27
27
 
28
28
 
29
- class CodexTemplateView(CodexAPIView):
29
+ class CodexTemplateView(SimpleAPIView):
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(CodexAPIView):
36
+ class CodexXMLTemplateView(SimpleAPIView):
37
37
  """XML Template View."""
38
38
 
39
39
  renderer_classes = (TemplateXMLRenderer,)
codex/views/version.py CHANGED
@@ -1,13 +1,13 @@
1
1
  """Version View."""
2
2
 
3
+ from rest_framework.generics import GenericAPIView
3
4
  from rest_framework.response import Response
4
5
 
5
6
  from codex.serializers.versions import VersionsSerializer
6
7
  from codex.version import PACKAGE_NAME, VERSION, get_latest_version
7
- from codex.views.auth import AuthGenericAPIView
8
8
 
9
9
 
10
- class VersionView(AuthGenericAPIView):
10
+ class VersionView(GenericAPIView):
11
11
  """Return Codex Versions."""
12
12
 
13
13
  serializer_class = VersionsSerializer