codex 1.6.0a9__py3-none-any.whl → 1.6.0a10__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 (547) hide show
  1. codex/librarian/importer/cache.py +46 -133
  2. codex/librarian/importer/create_fks.py +3 -1
  3. codex/librarian/importer/importer.py +1 -1
  4. codex/librarian/importer/importerd.py +5 -5
  5. codex/librarian/importer/tasks.py +2 -2
  6. codex/librarian/janitor/janitor.py +0 -3
  7. codex/librarian/librariand.py +0 -2
  8. codex/librarian/notifier/notifierd.py +1 -1
  9. codex/librarian/notifier/tasks.py +4 -4
  10. codex/middleware.py +1 -2
  11. codex/migrations/0027_import_order_and_covers.py +1 -66
  12. codex/models/comic.py +0 -1
  13. codex/models/groups.py +0 -3
  14. codex/serializers/admin.py +41 -40
  15. codex/serializers/browser/choices.py +22 -6
  16. codex/serializers/browser/metadata.py +45 -1
  17. codex/serializers/browser/page.py +2 -4
  18. codex/serializers/browser/settings.py +34 -14
  19. codex/serializers/fields.py +19 -1
  20. codex/serializers/reader.py +11 -1
  21. codex/serializers/route.py +1 -1
  22. codex/serializers/settings.py +12 -0
  23. codex/settings/settings.py +10 -1
  24. codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js +1 -0
  25. codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js.br +0 -0
  26. codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js.gz +0 -0
  27. codex/static_root/assets/VCheckbox-CY3rcqlS.js +1 -0
  28. codex/static_root/assets/VCheckbox-CY3rcqlS.js.br +0 -0
  29. codex/static_root/assets/VCheckbox-CY3rcqlS.js.gz +0 -0
  30. codex/static_root/assets/{VCheckboxBtn-DLzF_WHB.9a47873b8548.js → VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js} +1 -1
  31. codex/static_root/assets/VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js.br +0 -0
  32. codex/static_root/assets/VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js.gz +0 -0
  33. codex/static_root/assets/{VCheckboxBtn-DLzF_WHB.js → VCheckboxBtn-D2tPTt6P.js} +1 -1
  34. codex/static_root/assets/VCheckboxBtn-D2tPTt6P.js.br +0 -0
  35. codex/static_root/assets/VCheckboxBtn-D2tPTt6P.js.gz +0 -0
  36. codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js +1 -0
  37. codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js.br +0 -0
  38. codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js.gz +0 -0
  39. codex/static_root/assets/VCombobox-aJr--Ou7.js +1 -0
  40. codex/static_root/assets/VCombobox-aJr--Ou7.js.br +0 -0
  41. codex/static_root/assets/VCombobox-aJr--Ou7.js.gz +0 -0
  42. codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js +1 -0
  43. codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js.br +0 -0
  44. codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js.gz +0 -0
  45. codex/static_root/assets/VDialog-61pzfUB2.js +1 -0
  46. codex/static_root/assets/VDialog-61pzfUB2.js.br +0 -0
  47. codex/static_root/assets/VDialog-61pzfUB2.js.gz +0 -0
  48. codex/static_root/assets/{VDialog-C5Nxd_zP.css → VDialog-lQ13FnRR.1c801ad756b2.css} +1 -1
  49. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
  50. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
  51. codex/static_root/assets/{VDialog-C5Nxd_zP.d327f505ef0e.css → VDialog-lQ13FnRR.css} +1 -1
  52. codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
  53. codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
  54. codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js +1 -0
  55. codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js.br +0 -0
  56. codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js.gz +0 -0
  57. codex/static_root/assets/VExpansionPanels-rewm0P4z.js +1 -0
  58. codex/static_root/assets/VExpansionPanels-rewm0P4z.js.br +0 -0
  59. codex/static_root/assets/VExpansionPanels-rewm0P4z.js.gz +0 -0
  60. codex/static_root/assets/{VRadioGroup-B--7K_v5.4a4e9c2d6c42.js → VRadioGroup-QFQbK1qo.750a42b1b814.js} +1 -1
  61. codex/static_root/assets/VRadioGroup-QFQbK1qo.750a42b1b814.js.br +0 -0
  62. codex/static_root/assets/VRadioGroup-QFQbK1qo.750a42b1b814.js.gz +0 -0
  63. codex/static_root/assets/{VRadioGroup-B--7K_v5.js → VRadioGroup-QFQbK1qo.js} +1 -1
  64. codex/static_root/assets/VRadioGroup-QFQbK1qo.js.br +0 -0
  65. codex/static_root/assets/VRadioGroup-QFQbK1qo.js.gz +0 -0
  66. codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js +1 -0
  67. codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js.br +0 -0
  68. codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js.gz +0 -0
  69. codex/static_root/assets/VSelect-Dixy6okt.js +1 -0
  70. codex/static_root/assets/VSelect-Dixy6okt.js.br +0 -0
  71. codex/static_root/assets/VSelect-Dixy6okt.js.gz +0 -0
  72. codex/static_root/assets/{VSelectionControl-CVQ9wkE2.c02186cceba6.js → VSelectionControl-MNmxKc3J.08055d00917e.js} +1 -1
  73. codex/static_root/assets/VSelectionControl-MNmxKc3J.08055d00917e.js.br +0 -0
  74. codex/static_root/assets/VSelectionControl-MNmxKc3J.08055d00917e.js.gz +0 -0
  75. codex/static_root/assets/{VSelectionControl-CVQ9wkE2.js → VSelectionControl-MNmxKc3J.js} +1 -1
  76. codex/static_root/assets/VSelectionControl-MNmxKc3J.js.br +0 -0
  77. codex/static_root/assets/VSelectionControl-MNmxKc3J.js.gz +0 -0
  78. codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js +1 -0
  79. codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js.br +0 -0
  80. codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js.gz +0 -0
  81. codex/static_root/assets/VSlideGroup-DZEV0FoV.js +1 -0
  82. codex/static_root/assets/VSlideGroup-DZEV0FoV.js.br +0 -0
  83. codex/static_root/assets/VSlideGroup-DZEV0FoV.js.gz +0 -0
  84. codex/static_root/assets/{VTable-CrWkhkiP.6d530eea0c09.js → VTable-lcnYg9Ee.d72927b13c2e.js} +1 -1
  85. codex/static_root/assets/VTable-lcnYg9Ee.d72927b13c2e.js.br +0 -0
  86. codex/static_root/assets/VTable-lcnYg9Ee.d72927b13c2e.js.gz +0 -0
  87. codex/static_root/assets/{VTable-CrWkhkiP.js → VTable-lcnYg9Ee.js} +1 -1
  88. codex/static_root/assets/VTable-lcnYg9Ee.js.br +0 -0
  89. codex/static_root/assets/VTable-lcnYg9Ee.js.gz +0 -0
  90. codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js +1 -0
  91. codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js.br +0 -0
  92. codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js.gz +0 -0
  93. codex/static_root/assets/VTextField-BkFrjhdV.js +1 -0
  94. codex/static_root/assets/VTextField-BkFrjhdV.js.br +0 -0
  95. codex/static_root/assets/VTextField-BkFrjhdV.js.gz +0 -0
  96. codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js +1 -0
  97. codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js.br +0 -0
  98. codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js.gz +0 -0
  99. codex/static_root/assets/VWindowItem-C__tAqdc.js +1 -0
  100. codex/static_root/assets/VWindowItem-C__tAqdc.js.br +0 -0
  101. codex/static_root/assets/VWindowItem-C__tAqdc.js.gz +0 -0
  102. codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js +1 -0
  103. codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js.br +0 -0
  104. codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js.gz +0 -0
  105. codex/static_root/assets/admin-CXES2HXe.js +1 -0
  106. codex/static_root/assets/admin-CXES2HXe.js.br +0 -0
  107. codex/static_root/assets/admin-CXES2HXe.js.gz +0 -0
  108. codex/static_root/assets/{admin-drawer-panel-o87BwD38.ce39427c8201.css → admin-drawer-panel-mne3FIj6.561b3f75fe65.css} +1 -1
  109. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
  110. codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
  111. codex/static_root/assets/{admin-drawer-panel-o87BwD38.css → admin-drawer-panel-mne3FIj6.css} +1 -1
  112. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
  113. codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
  114. codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js +30 -0
  115. codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js.br +0 -0
  116. codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js.gz +0 -0
  117. codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js +30 -0
  118. codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js.br +0 -0
  119. codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js.gz +0 -0
  120. codex/static_root/assets/browser-BuDeXGEe.css +1 -0
  121. codex/static_root/assets/browser-BuDeXGEe.css.br +0 -0
  122. codex/static_root/assets/browser-BuDeXGEe.css.gz +0 -0
  123. codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css +1 -0
  124. codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css.br +0 -0
  125. codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css.gz +0 -0
  126. codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js +1 -0
  127. codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js.br +0 -0
  128. codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js.gz +0 -0
  129. codex/static_root/assets/browser-s6mFzrK9.js +1 -0
  130. codex/static_root/assets/browser-s6mFzrK9.js.br +0 -0
  131. codex/static_root/assets/browser-s6mFzrK9.js.gz +0 -0
  132. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +1 -0
  133. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
  134. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
  135. codex/static_root/assets/change-password-dialog-0u6IJA87.css +1 -0
  136. codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
  137. codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
  138. codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js +1 -0
  139. codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js.br +0 -0
  140. codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js.gz +0 -0
  141. codex/static_root/assets/change-password-dialog-Bn20jYzD.js +1 -0
  142. codex/static_root/assets/change-password-dialog-Bn20jYzD.js.br +0 -0
  143. codex/static_root/assets/change-password-dialog-Bn20jYzD.js.gz +0 -0
  144. codex/static_root/assets/{confirm-dialog-jwL7B98r.1cfd579cbcfe.js → confirm-dialog-CWszioQE.859051c35dd9.js} +1 -1
  145. codex/static_root/assets/confirm-dialog-CWszioQE.859051c35dd9.js.br +0 -0
  146. codex/static_root/assets/confirm-dialog-CWszioQE.859051c35dd9.js.gz +0 -0
  147. codex/static_root/assets/{confirm-dialog-jwL7B98r.js → confirm-dialog-CWszioQE.js} +1 -1
  148. codex/static_root/assets/confirm-dialog-CWszioQE.js.br +0 -0
  149. codex/static_root/assets/confirm-dialog-CWszioQE.js.gz +0 -0
  150. codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js +1 -0
  151. codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js.br +0 -0
  152. codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js.gz +0 -0
  153. codex/static_root/assets/datetime-column-BSdlLt-4.js +1 -0
  154. codex/static_root/assets/datetime-column-BSdlLt-4.js.br +0 -0
  155. codex/static_root/assets/datetime-column-BSdlLt-4.js.gz +0 -0
  156. codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js +1 -0
  157. codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js.br +0 -0
  158. codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js.gz +0 -0
  159. codex/static_root/assets/filter-D3gaGf87.js +1 -0
  160. codex/static_root/assets/filter-D3gaGf87.js.br +0 -0
  161. codex/static_root/assets/filter-D3gaGf87.js.gz +0 -0
  162. codex/static_root/assets/{flag-tab-Da2DBNyz.ffbe5ca37e1c.js → flag-tab-8vzpdySX.a982c7f0776a.js} +1 -1
  163. codex/static_root/assets/flag-tab-8vzpdySX.a982c7f0776a.js.br +0 -0
  164. codex/static_root/assets/flag-tab-8vzpdySX.a982c7f0776a.js.gz +0 -0
  165. codex/static_root/assets/{flag-tab-Da2DBNyz.js → flag-tab-8vzpdySX.js} +1 -1
  166. codex/static_root/assets/flag-tab-8vzpdySX.js.br +0 -0
  167. codex/static_root/assets/flag-tab-8vzpdySX.js.gz +0 -0
  168. codex/static_root/assets/{group-tab-BONYWOc1.6ddf321a4a56.js → group-tab-Cz9t_ECi.a9e8aea79254.js} +1 -1
  169. codex/static_root/assets/group-tab-Cz9t_ECi.a9e8aea79254.js.br +0 -0
  170. codex/static_root/assets/group-tab-Cz9t_ECi.a9e8aea79254.js.gz +0 -0
  171. codex/static_root/assets/{group-tab-BONYWOc1.js → group-tab-Cz9t_ECi.js} +1 -1
  172. codex/static_root/assets/group-tab-Cz9t_ECi.js.br +0 -0
  173. codex/static_root/assets/group-tab-Cz9t_ECi.js.gz +0 -0
  174. codex/static_root/assets/{http-error-D2Jnc20C.32846bc3a43d.js → http-error-Cqtszzf1.b31df3d9c29f.js} +1 -1
  175. codex/static_root/assets/http-error-Cqtszzf1.b31df3d9c29f.js.br +0 -0
  176. codex/static_root/assets/http-error-Cqtszzf1.b31df3d9c29f.js.gz +0 -0
  177. codex/static_root/assets/{http-error-D2Jnc20C.js → http-error-Cqtszzf1.js} +1 -1
  178. codex/static_root/assets/http-error-Cqtszzf1.js.br +0 -0
  179. codex/static_root/assets/http-error-Cqtszzf1.js.gz +0 -0
  180. codex/static_root/assets/{library-tab-B419agXA.js → library-tab-DMlnswUU.30f454e84184.js} +1 -1
  181. codex/static_root/assets/library-tab-DMlnswUU.30f454e84184.js.br +0 -0
  182. codex/static_root/assets/library-tab-DMlnswUU.30f454e84184.js.gz +0 -0
  183. codex/static_root/assets/{library-tab-B419agXA.d4b778172aaa.js → library-tab-DMlnswUU.js} +1 -1
  184. codex/static_root/assets/library-tab-DMlnswUU.js.br +0 -0
  185. codex/static_root/assets/library-tab-DMlnswUU.js.gz +0 -0
  186. codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js +32 -0
  187. codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js.br +0 -0
  188. codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js.gz +0 -0
  189. codex/static_root/assets/main-vjXu_QYL.js +32 -0
  190. codex/static_root/assets/main-vjXu_QYL.js.br +0 -0
  191. codex/static_root/assets/main-vjXu_QYL.js.gz +0 -0
  192. codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css +1 -0
  193. codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css.br +0 -0
  194. codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css.gz +0 -0
  195. codex/static_root/assets/pagination-toolbar-DGdMiBRs.css +1 -0
  196. codex/static_root/assets/pagination-toolbar-DGdMiBRs.css.br +0 -0
  197. codex/static_root/assets/pagination-toolbar-DGdMiBRs.css.gz +0 -0
  198. codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js +1 -0
  199. codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js.br +0 -0
  200. codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js.gz +0 -0
  201. codex/static_root/assets/pagination-toolbar-JW78pa0q.js +1 -0
  202. codex/static_root/assets/pagination-toolbar-JW78pa0q.js.br +0 -0
  203. codex/static_root/assets/pagination-toolbar-JW78pa0q.js.gz +0 -0
  204. codex/static_root/assets/{pdf-doc-8RVZqILx.2e7166dd26fb.js → pdf-doc-BiamrjRZ.4c9be1ade43c.js} +1 -1
  205. codex/static_root/assets/pdf-doc-BiamrjRZ.4c9be1ade43c.js.br +0 -0
  206. codex/static_root/assets/pdf-doc-BiamrjRZ.4c9be1ade43c.js.gz +0 -0
  207. codex/static_root/assets/{pdf-doc-8RVZqILx.js → pdf-doc-BiamrjRZ.js} +1 -1
  208. codex/static_root/assets/pdf-doc-BiamrjRZ.js.br +0 -0
  209. codex/static_root/assets/pdf-doc-BiamrjRZ.js.gz +0 -0
  210. codex/static_root/assets/{reader-CCCfpM3x.1553e45bd775.css → reader-BpsctRsY.5d72e393911a.css} +1 -1
  211. codex/static_root/assets/reader-BpsctRsY.5d72e393911a.css.br +0 -0
  212. codex/static_root/assets/reader-BpsctRsY.5d72e393911a.css.gz +0 -0
  213. codex/static_root/assets/{reader-CCCfpM3x.css → reader-BpsctRsY.css} +1 -1
  214. codex/static_root/assets/reader-BpsctRsY.css.br +0 -0
  215. codex/static_root/assets/reader-BpsctRsY.css.gz +0 -0
  216. codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js +2 -0
  217. codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js.br +0 -0
  218. codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js.gz +0 -0
  219. codex/static_root/assets/reader-CDk8oZzB.js +2 -0
  220. codex/static_root/assets/reader-CDk8oZzB.js.br +0 -0
  221. codex/static_root/assets/reader-CDk8oZzB.js.gz +0 -0
  222. codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js +1 -0
  223. codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js.br +0 -0
  224. codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js.gz +0 -0
  225. codex/static_root/assets/relation-chips-C5acBjpy.js +1 -0
  226. codex/static_root/assets/relation-chips-C5acBjpy.js.br +0 -0
  227. codex/static_root/assets/relation-chips-C5acBjpy.js.gz +0 -0
  228. codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js +2 -0
  229. codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js.br +0 -0
  230. codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js.gz +0 -0
  231. codex/static_root/assets/settings-drawer-D_JuRi7F.js +2 -0
  232. codex/static_root/assets/settings-drawer-D_JuRi7F.js.br +0 -0
  233. codex/static_root/assets/settings-drawer-D_JuRi7F.js.gz +0 -0
  234. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +1 -0
  235. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
  236. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
  237. codex/static_root/assets/stats-tab-B4yKuY0c.css +1 -0
  238. codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
  239. codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
  240. codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js +1 -0
  241. codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js.br +0 -0
  242. codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js.gz +0 -0
  243. codex/static_root/assets/stats-tab-ClqRTk1z.js +1 -0
  244. codex/static_root/assets/stats-tab-ClqRTk1z.js.br +0 -0
  245. codex/static_root/assets/stats-tab-ClqRTk1z.js.gz +0 -0
  246. codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js +1 -0
  247. codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js.br +0 -0
  248. codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js.gz +0 -0
  249. codex/static_root/assets/task-tab-DF9qa6LO.js +1 -0
  250. codex/static_root/assets/task-tab-DF9qa6LO.js.br +0 -0
  251. codex/static_root/assets/task-tab-DF9qa6LO.js.gz +0 -0
  252. codex/static_root/assets/to-case-CGbULRaE.4c80644f460d.js +1 -0
  253. codex/static_root/assets/to-case-CGbULRaE.4c80644f460d.js.br +0 -0
  254. codex/static_root/assets/to-case-CGbULRaE.js +1 -0
  255. codex/static_root/assets/to-case-CGbULRaE.js.br +0 -0
  256. codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js +1 -0
  257. codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js.br +0 -0
  258. codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js.gz +0 -0
  259. codex/static_root/assets/unauthorized-BjKuLH6k.js +1 -0
  260. codex/static_root/assets/unauthorized-BjKuLH6k.js.br +0 -0
  261. codex/static_root/assets/unauthorized-BjKuLH6k.js.gz +0 -0
  262. codex/static_root/assets/{unauthorized-DHV6Q1n-.b7847b2872a1.css → unauthorized-CibJYD8o.4c2de98c2b53.css} +1 -1
  263. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
  264. codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
  265. codex/static_root/assets/{unauthorized-DHV6Q1n-.css → unauthorized-CibJYD8o.css} +1 -1
  266. codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
  267. codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
  268. codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js +1 -0
  269. codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js.br +0 -0
  270. codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js.gz +0 -0
  271. codex/static_root/assets/user-tab-Dpxe3ygA.js +1 -0
  272. codex/static_root/assets/user-tab-Dpxe3ygA.js.br +0 -0
  273. codex/static_root/assets/user-tab-Dpxe3ygA.js.gz +0 -0
  274. codex/static_root/js/choices-admin.3521e2a863df.json +1 -0
  275. codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
  276. codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
  277. codex/static_root/js/choices-admin.json +1 -1
  278. codex/static_root/js/choices-admin.json.br +0 -0
  279. codex/static_root/js/choices-admin.json.gz +0 -0
  280. codex/static_root/manifest.35e46a3ec8fa.json +622 -0
  281. codex/static_root/manifest.35e46a3ec8fa.json.br +0 -0
  282. codex/static_root/manifest.35e46a3ec8fa.json.gz +0 -0
  283. codex/static_root/manifest.json +245 -252
  284. codex/static_root/manifest.json.br +0 -0
  285. codex/static_root/manifest.json.gz +0 -0
  286. codex/static_root/staticfiles.json +1 -1
  287. codex/views/admin/api_key.py +5 -2
  288. codex/views/admin/stats.py +13 -20
  289. codex/views/admin/tasks.py +2 -3
  290. codex/views/auth.py +1 -1
  291. codex/views/bookmark.py +30 -16
  292. codex/views/browser/annotations.py +2 -0
  293. codex/views/browser/base.py +22 -6
  294. codex/views/browser/breadcrumbs.py +5 -5
  295. codex/views/browser/browser.py +14 -22
  296. codex/views/browser/choices.py +18 -15
  297. codex/views/browser/cover.py +30 -66
  298. codex/views/browser/filters/annotations.py +14 -6
  299. codex/views/browser/filters/group.py +24 -15
  300. codex/views/browser/filters/search.py +3 -4
  301. codex/views/browser/metadata.py +86 -47
  302. codex/views/browser/mtime.py +3 -14
  303. codex/views/browser/order_by.py +1 -6
  304. codex/views/browser/settings.py +53 -0
  305. codex/views/browser/validate.py +3 -0
  306. codex/views/const.py +24 -1
  307. codex/views/opds/v1/feed.py +3 -5
  308. codex/views/opds/v2/feed.py +3 -3
  309. codex/views/reader/arcs.py +42 -51
  310. codex/views/reader/books.py +47 -63
  311. codex/views/reader/init.py +121 -0
  312. codex/views/reader/reader.py +9 -70
  313. codex/views/session.py +15 -11
  314. codex/views/settings.py +23 -10
  315. codex/views/util.py +2 -1
  316. codex/websockets/consumers.py +13 -9
  317. {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/METADATA +1 -1
  318. {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/RECORD +321 -317
  319. codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js +0 -1
  320. codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.br +0 -0
  321. codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.gz +0 -0
  322. codex/static_root/assets/VCheckbox-eNspVUje.js +0 -1
  323. codex/static_root/assets/VCheckbox-eNspVUje.js.br +0 -0
  324. codex/static_root/assets/VCheckbox-eNspVUje.js.gz +0 -0
  325. codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.br +0 -0
  326. codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.gz +0 -0
  327. codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.br +0 -0
  328. codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.gz +0 -0
  329. codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js +0 -1
  330. codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.br +0 -0
  331. codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.gz +0 -0
  332. codex/static_root/assets/VCombobox-BIO7lU_B.js +0 -1
  333. codex/static_root/assets/VCombobox-BIO7lU_B.js.br +0 -0
  334. codex/static_root/assets/VCombobox-BIO7lU_B.js.gz +0 -0
  335. codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js +0 -1
  336. codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.br +0 -0
  337. codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.gz +0 -0
  338. codex/static_root/assets/VDialog-87AymJC5.js +0 -1
  339. codex/static_root/assets/VDialog-87AymJC5.js.br +0 -0
  340. codex/static_root/assets/VDialog-87AymJC5.js.gz +0 -0
  341. codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
  342. codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
  343. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
  344. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
  345. codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js +0 -1
  346. codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.br +0 -0
  347. codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.gz +0 -0
  348. codex/static_root/assets/VExpansionPanels-CgA3shsd.js +0 -1
  349. codex/static_root/assets/VExpansionPanels-CgA3shsd.js.br +0 -0
  350. codex/static_root/assets/VExpansionPanels-CgA3shsd.js.gz +0 -0
  351. codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.br +0 -0
  352. codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.gz +0 -0
  353. codex/static_root/assets/VRadioGroup-B--7K_v5.js.br +0 -0
  354. codex/static_root/assets/VRadioGroup-B--7K_v5.js.gz +0 -0
  355. codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js +0 -1
  356. codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.br +0 -0
  357. codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.gz +0 -0
  358. codex/static_root/assets/VSelect-BoJq222l.js +0 -1
  359. codex/static_root/assets/VSelect-BoJq222l.js.br +0 -0
  360. codex/static_root/assets/VSelect-BoJq222l.js.gz +0 -0
  361. codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.br +0 -0
  362. codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.gz +0 -0
  363. codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.br +0 -0
  364. codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.gz +0 -0
  365. codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js +0 -1
  366. codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.br +0 -0
  367. codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.gz +0 -0
  368. codex/static_root/assets/VSlideGroup-Ce0j2BKK.js +0 -1
  369. codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.br +0 -0
  370. codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.gz +0 -0
  371. codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.br +0 -0
  372. codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.gz +0 -0
  373. codex/static_root/assets/VTable-CrWkhkiP.js.br +0 -0
  374. codex/static_root/assets/VTable-CrWkhkiP.js.gz +0 -0
  375. codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js +0 -1
  376. codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.br +0 -0
  377. codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.gz +0 -0
  378. codex/static_root/assets/VTextField-CkWbil3K.js +0 -1
  379. codex/static_root/assets/VTextField-CkWbil3K.js.br +0 -0
  380. codex/static_root/assets/VTextField-CkWbil3K.js.gz +0 -0
  381. codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js +0 -1
  382. codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.br +0 -0
  383. codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.gz +0 -0
  384. codex/static_root/assets/VWindowItem-7z55Y4lK.js +0 -1
  385. codex/static_root/assets/VWindowItem-7z55Y4lK.js.br +0 -0
  386. codex/static_root/assets/VWindowItem-7z55Y4lK.js.gz +0 -0
  387. codex/static_root/assets/admin-D3OK784R.f69891726ec5.js +0 -1
  388. codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.br +0 -0
  389. codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.gz +0 -0
  390. codex/static_root/assets/admin-D3OK784R.js +0 -1
  391. codex/static_root/assets/admin-D3OK784R.js.br +0 -0
  392. codex/static_root/assets/admin-D3OK784R.js.gz +0 -0
  393. codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js +0 -30
  394. codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.br +0 -0
  395. codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.gz +0 -0
  396. codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js +0 -30
  397. codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.br +0 -0
  398. codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.gz +0 -0
  399. codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.br +0 -0
  400. codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.gz +0 -0
  401. codex/static_root/assets/admin-drawer-panel-o87BwD38.css.br +0 -0
  402. codex/static_root/assets/admin-drawer-panel-o87BwD38.css.gz +0 -0
  403. codex/static_root/assets/browser-BuU2x9y7.css +0 -1
  404. codex/static_root/assets/browser-BuU2x9y7.css.br +0 -0
  405. codex/static_root/assets/browser-BuU2x9y7.css.gz +0 -0
  406. codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css +0 -1
  407. codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.br +0 -0
  408. codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.gz +0 -0
  409. codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js +0 -1
  410. codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.br +0 -0
  411. codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.gz +0 -0
  412. codex/static_root/assets/browser-CO9EYHIr.js +0 -1
  413. codex/static_root/assets/browser-CO9EYHIr.js.br +0 -0
  414. codex/static_root/assets/browser-CO9EYHIr.js.gz +0 -0
  415. codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js +0 -1
  416. codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.br +0 -0
  417. codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.gz +0 -0
  418. codex/static_root/assets/change-password-dialog-CjZdzaIk.js +0 -1
  419. codex/static_root/assets/change-password-dialog-CjZdzaIk.js.br +0 -0
  420. codex/static_root/assets/change-password-dialog-CjZdzaIk.js.gz +0 -0
  421. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +0 -1
  422. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
  423. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
  424. codex/static_root/assets/change-password-dialog-HvIvmmIE.css +0 -1
  425. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
  426. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
  427. codex/static_root/assets/confirm-dialog-jwL7B98r.1cfd579cbcfe.js.br +0 -0
  428. codex/static_root/assets/confirm-dialog-jwL7B98r.1cfd579cbcfe.js.gz +0 -0
  429. codex/static_root/assets/confirm-dialog-jwL7B98r.js.br +0 -0
  430. codex/static_root/assets/confirm-dialog-jwL7B98r.js.gz +0 -0
  431. codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js +0 -1
  432. codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.br +0 -0
  433. codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.gz +0 -0
  434. codex/static_root/assets/datetime-column-9w3GcKvo.js +0 -1
  435. codex/static_root/assets/datetime-column-9w3GcKvo.js.br +0 -0
  436. codex/static_root/assets/datetime-column-9w3GcKvo.js.gz +0 -0
  437. codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js +0 -1
  438. codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.br +0 -0
  439. codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.gz +0 -0
  440. codex/static_root/assets/filter-C-pghOri.js +0 -1
  441. codex/static_root/assets/filter-C-pghOri.js.br +0 -0
  442. codex/static_root/assets/filter-C-pghOri.js.gz +0 -0
  443. codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.br +0 -0
  444. codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.gz +0 -0
  445. codex/static_root/assets/flag-tab-Da2DBNyz.js.br +0 -0
  446. codex/static_root/assets/flag-tab-Da2DBNyz.js.gz +0 -0
  447. codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.br +0 -0
  448. codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.gz +0 -0
  449. codex/static_root/assets/group-tab-BONYWOc1.js.br +0 -0
  450. codex/static_root/assets/group-tab-BONYWOc1.js.gz +0 -0
  451. codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.br +0 -0
  452. codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.gz +0 -0
  453. codex/static_root/assets/http-error-D2Jnc20C.js.br +0 -0
  454. codex/static_root/assets/http-error-D2Jnc20C.js.gz +0 -0
  455. codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.br +0 -0
  456. codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.gz +0 -0
  457. codex/static_root/assets/library-tab-B419agXA.js.br +0 -0
  458. codex/static_root/assets/library-tab-B419agXA.js.gz +0 -0
  459. codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js +0 -32
  460. codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.br +0 -0
  461. codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.gz +0 -0
  462. codex/static_root/assets/main-CMQwJ9aG.js +0 -32
  463. codex/static_root/assets/main-CMQwJ9aG.js.br +0 -0
  464. codex/static_root/assets/main-CMQwJ9aG.js.gz +0 -0
  465. codex/static_root/assets/pagination-toolbar-DWdp_kmz.css +0 -1
  466. codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.br +0 -0
  467. codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.gz +0 -0
  468. codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css +0 -1
  469. codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.br +0 -0
  470. codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.gz +0 -0
  471. codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js +0 -1
  472. codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.br +0 -0
  473. codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.gz +0 -0
  474. codex/static_root/assets/pagination-toolbar-LTuj5U-G.js +0 -1
  475. codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.br +0 -0
  476. codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.gz +0 -0
  477. codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.br +0 -0
  478. codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.gz +0 -0
  479. codex/static_root/assets/pdf-doc-8RVZqILx.js.br +0 -0
  480. codex/static_root/assets/pdf-doc-8RVZqILx.js.gz +0 -0
  481. codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.br +0 -0
  482. codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.gz +0 -0
  483. codex/static_root/assets/reader-CCCfpM3x.css.br +0 -0
  484. codex/static_root/assets/reader-CCCfpM3x.css.gz +0 -0
  485. codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js +0 -2
  486. codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.br +0 -0
  487. codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.gz +0 -0
  488. codex/static_root/assets/reader-DNnK_Chr.js +0 -2
  489. codex/static_root/assets/reader-DNnK_Chr.js.br +0 -0
  490. codex/static_root/assets/reader-DNnK_Chr.js.gz +0 -0
  491. codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js +0 -1
  492. codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.br +0 -0
  493. codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.gz +0 -0
  494. codex/static_root/assets/relation-chips-PYua7aXv.js +0 -1
  495. codex/static_root/assets/relation-chips-PYua7aXv.js.br +0 -0
  496. codex/static_root/assets/relation-chips-PYua7aXv.js.gz +0 -0
  497. codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js +0 -2
  498. codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.br +0 -0
  499. codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.gz +0 -0
  500. codex/static_root/assets/settings-drawer-DbN6ISRH.js +0 -2
  501. codex/static_root/assets/settings-drawer-DbN6ISRH.js.br +0 -0
  502. codex/static_root/assets/settings-drawer-DbN6ISRH.js.gz +0 -0
  503. codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js +0 -1
  504. codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.br +0 -0
  505. codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.gz +0 -0
  506. codex/static_root/assets/stats-tab-DImNhH8O.js +0 -1
  507. codex/static_root/assets/stats-tab-DImNhH8O.js.br +0 -0
  508. codex/static_root/assets/stats-tab-DImNhH8O.js.gz +0 -0
  509. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +0 -1
  510. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +0 -2
  511. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
  512. codex/static_root/assets/stats-tab-TsyG3wH_.css +0 -1
  513. codex/static_root/assets/stats-tab-TsyG3wH_.css.br +0 -2
  514. codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
  515. codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js +0 -1
  516. codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.br +0 -0
  517. codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.gz +0 -0
  518. codex/static_root/assets/task-tab-DPD53kjv.js +0 -1
  519. codex/static_root/assets/task-tab-DPD53kjv.js.br +0 -0
  520. codex/static_root/assets/task-tab-DPD53kjv.js.gz +0 -0
  521. codex/static_root/assets/to-case-ehC9Ccyj.a7b8e25b391f.js +0 -1
  522. codex/static_root/assets/to-case-ehC9Ccyj.js +0 -1
  523. codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js +0 -1
  524. codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.br +0 -0
  525. codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.gz +0 -0
  526. codex/static_root/assets/unauthorized-BOIx7Y18.js +0 -1
  527. codex/static_root/assets/unauthorized-BOIx7Y18.js.br +0 -0
  528. codex/static_root/assets/unauthorized-BOIx7Y18.js.gz +0 -0
  529. codex/static_root/assets/unauthorized-DHV6Q1n-.b7847b2872a1.css.br +0 -0
  530. codex/static_root/assets/unauthorized-DHV6Q1n-.b7847b2872a1.css.gz +0 -0
  531. codex/static_root/assets/unauthorized-DHV6Q1n-.css.br +0 -0
  532. codex/static_root/assets/unauthorized-DHV6Q1n-.css.gz +0 -0
  533. codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js +0 -1
  534. codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.br +0 -0
  535. codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.gz +0 -0
  536. codex/static_root/assets/user-tab-BABwfxLO.js +0 -1
  537. codex/static_root/assets/user-tab-BABwfxLO.js.br +0 -0
  538. codex/static_root/assets/user-tab-BABwfxLO.js.gz +0 -0
  539. codex/static_root/js/choices-admin.d790df01c20a.json +0 -1
  540. codex/static_root/js/choices-admin.d790df01c20a.json.br +0 -0
  541. codex/static_root/js/choices-admin.d790df01c20a.json.gz +0 -0
  542. codex/static_root/manifest.cbea5663003a.json +0 -629
  543. codex/static_root/manifest.cbea5663003a.json.br +0 -0
  544. codex/static_root/manifest.cbea5663003a.json.gz +0 -0
  545. {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/LICENSE +0 -0
  546. {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/WHEEL +0 -0
  547. {codex-1.6.0a9.dist-info → codex-1.6.0a10.dist-info}/entry_points.txt +0 -0
@@ -11,10 +11,10 @@ from rest_framework.throttling import ScopedRateThrottle
11
11
  from codex.librarian.importer.tasks import LazyImportComicsTask
12
12
  from codex.librarian.mp_queue import LIBRARIAN_QUEUE
13
13
  from codex.logger.logging import get_logger
14
+ from codex.serializers.browser.settings import OPDSSettingsSerializer
14
15
  from codex.serializers.opds.v1 import (
15
16
  OPDS1TemplateSerializer,
16
17
  )
17
- from codex.views.browser.browser import BrowserView
18
18
  from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
19
19
  from codex.views.opds.auth import OPDSTemplateView
20
20
  from codex.views.opds.const import BLANK_TITLE, MimeType
@@ -56,6 +56,7 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
56
56
 
57
57
  template_name = "opds_v1/index.xml"
58
58
  serializer_class = OPDS1TemplateSerializer
59
+ input_serializer_class = OPDSSettingsSerializer
59
60
  throttle_classes = (ScopedRateThrottle,)
60
61
  throttle_scope = "opds"
61
62
  TARGET = "opds1"
@@ -254,10 +255,7 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
254
255
  self._set_user_agent_variables()
255
256
  self.skip_order_facets |= self.kwargs.get("group") == "c"
256
257
 
257
- @extend_schema(
258
- request=BrowserView.input_serializer_class,
259
- parameters=[BrowserView.input_serializer_class],
260
- )
258
+ @extend_schema(parameters=[input_serializer_class])
261
259
  def get(self, *_args, **_kwargs):
262
260
  """Get the feed."""
263
261
  self.init_request()
@@ -7,8 +7,8 @@ from rest_framework.response import Response
7
7
 
8
8
  from codex.logger.logging import get_logger
9
9
  from codex.models import AdminFlag
10
+ from codex.serializers.browser.settings import OPDSSettingsSerializer
10
11
  from codex.serializers.opds.v2 import OPDS2FeedSerializer
11
- from codex.views.browser.browser import BrowserView
12
12
  from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
13
13
  from codex.views.opds.auth import OPDSAuthMixin
14
14
  from codex.views.opds.const import BLANK_TITLE
@@ -36,6 +36,7 @@ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
36
36
  throttle_scope = "opds"
37
37
 
38
38
  serializer_class = OPDS2FeedSerializer
39
+ input_serializer_class = OPDSSettingsSerializer
39
40
 
40
41
  def _title(self, browser_title):
41
42
  """Create the feed title."""
@@ -233,8 +234,7 @@ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
233
234
  # self._detect_user_agent()
234
235
 
235
236
  @extend_schema(
236
- request=BrowserView.input_serializer_class,
237
- parameters=[BrowserView.input_serializer_class],
237
+ parameters=[input_serializer_class],
238
238
  )
239
239
  def get(self, *_args, **_kwargs):
240
240
  """Get the feed."""
@@ -4,7 +4,7 @@ from types import MappingProxyType
4
4
 
5
5
  from codex.models import AdminFlag
6
6
  from codex.util import max_none
7
- from codex.views.const import FOLDER_GROUP
7
+ from codex.views.const import FOLDER_GROUP, STORY_ARC_GROUP
8
8
  from codex.views.reader.books import ReaderBooksView
9
9
 
10
10
 
@@ -16,6 +16,22 @@ class ReaderArcsView(ReaderBooksView):
16
16
  super().__init__(*args, **kwargs)
17
17
  self.params: MappingProxyType = MappingProxyType({})
18
18
 
19
+ def _get_group_arc(self, book, attr):
20
+ """Create the volume arc."""
21
+ group = getattr(book, attr, None)
22
+ if not group:
23
+ return None
24
+
25
+ _, arc_pks = self.get_group_pks_from_breadcrumbs([group])
26
+ if group.pk not in arc_pks:
27
+ arc_pks = (group.pk,)
28
+ return {
29
+ "group": attr[0],
30
+ "pks": arc_pks,
31
+ "name": group.name,
32
+ "mtime": group.updated_at,
33
+ }
34
+
19
35
  def _get_folder_arc(self, book):
20
36
  """Create the folder arc."""
21
37
  efv_flag = (
@@ -23,39 +39,16 @@ class ReaderArcsView(ReaderBooksView):
23
39
  .get(key=AdminFlag.FlagChoices.FOLDER_VIEW.value)
24
40
  .on
25
41
  )
42
+ if not efv_flag:
43
+ return None
26
44
 
27
- if efv_flag:
28
- folder = book.parent_folder
29
- folder_arc = {
30
- "group": FOLDER_GROUP,
31
- "pks": (folder.pk,),
32
- "name": folder.name,
33
- "mtime": folder.updated_at,
34
- }
35
- else:
36
- folder_arc = None
37
- return folder_arc
38
-
39
- def _get_series_arc(self, book):
40
- """Create the series arc."""
41
- series = book.series
42
- if series:
43
- arc_pks = self.get_series_pks_from_breadcrumbs()
44
- if book.series.pk not in arc_pks:
45
- arc_pks = (book.series.pk,)
46
- arc = (
47
- {
48
- "group": "s",
49
- "pks": arc_pks,
50
- "name": series.name,
51
- "mtime": series.updated_at,
52
- }
53
- if series
54
- else None
55
- )
56
- else:
57
- arc = None
58
- return arc
45
+ folder = book.parent_folder
46
+ return {
47
+ "group": FOLDER_GROUP,
48
+ "pks": (folder.pk,),
49
+ "name": folder.name,
50
+ "mtime": folder.updated_at,
51
+ }
59
52
 
60
53
  def _get_story_arcs(self, book):
61
54
  """Create story arcs."""
@@ -74,26 +67,24 @@ class ReaderArcsView(ReaderBooksView):
74
67
  def get_arcs(self, book):
75
68
  """Get all series/folder/story arcs."""
76
69
  # create top arcs
77
- folder_arc = self._get_folder_arc(book)
78
- series_arc = self._get_series_arc(book)
79
-
80
- # order top arcs
70
+ arcs = []
81
71
  top_group = self.params.get("top_group")
82
- if top_group == FOLDER_GROUP and folder_arc:
83
- arc = folder_arc
84
- other_arc = series_arc
85
- else:
72
+ arc = None
73
+ if series_arc := self._get_group_arc(book, "series"):
74
+ arcs.append(series_arc)
86
75
  arc = series_arc
87
- other_arc = folder_arc
88
-
89
- arcs = []
90
- arcs.append(arc)
91
- if other_arc:
92
- arcs.append(other_arc)
93
-
94
- # story arcs
95
- sas = self._get_story_arcs(book)
96
- arcs += sas
76
+ if volume_arc := self._get_group_arc(book, "volume"):
77
+ arcs.append(volume_arc)
78
+ if not arc:
79
+ arc = volume_arc
80
+ if folder_arc := self._get_folder_arc(book):
81
+ arcs.append(folder_arc)
82
+ if top_group == FOLDER_GROUP:
83
+ arc = folder_arc
84
+ if story_arcs := self._get_story_arcs(book):
85
+ arcs = [*arcs, *story_arcs]
86
+ if top_group == STORY_ARC_GROUP:
87
+ arc = story_arcs[0]
97
88
 
98
89
  max_mtime = None
99
90
  for arc in arcs:
@@ -2,18 +2,17 @@
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from django.db.models import F, IntegerField, Value
5
+ from django.db.models import F
6
6
 
7
7
  from codex.models import Bookmark, Comic
8
8
  from codex.views.bookmark import BookmarkBaseView
9
- from codex.views.const import FOLDER_GROUP
9
+ from codex.views.const import FOLDER_GROUP, NONE_INTEGERFIELD
10
10
  from codex.views.mixins import SharedAnnotationsMixin
11
- from codex.views.session import SessionView
11
+ from codex.views.reader.init import ReaderInitView
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from collections.abc import Mapping
15
15
 
16
- _MIN_CRUMB_WALKBACK_LEN = 3
17
16
  _SETTINGS_ATTRS = ("fit_to", "two_pages", "reading_direction")
18
17
  _COMIC_FIELDS = (
19
18
  "file_type",
@@ -25,60 +24,40 @@ _COMIC_FIELDS = (
25
24
  "reading_direction",
26
25
  "updated_at",
27
26
  )
28
- _SHOW = {
29
- "series": True,
30
- "volume": True,
31
- }
32
27
 
33
28
 
34
- class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
29
+ class ReaderBooksView(BookmarkBaseView, ReaderInitView, SharedAnnotationsMixin):
35
30
  """Get Books methods."""
36
31
 
37
- def get_series_pks_from_breadcrumbs(self):
38
- """Get Multi-Group pks from the breadcrumbs."""
39
- if self.series_pks:
40
- return self.series_pks
41
- breadcrumbs = self.get_from_session(
42
- "breadcrumbs", session_key=self.BROWSER_SESSION_KEY
43
- )
44
- if breadcrumbs:
45
- crumb = breadcrumbs[-1]
46
- crumb_group = crumb.get("group")
47
- if crumb_group == "v" and len(breadcrumbs) >= _MIN_CRUMB_WALKBACK_LEN:
48
- crumb = breadcrumbs[-2]
49
- crumb_group = crumb.get("group")
50
- if crumb_group == "s":
51
- self.series_pks = crumb.get("pks", ())
52
-
53
- return self.series_pks
54
-
55
- def _get_reader_arc_pks(
56
- self, arc, arc_pk_select_related, prefetch_related, arc_pk_rel
32
+ def _get_reader_arc_pks( # noqa: PLR0913
33
+ self, arc, arc_pk_select_related, prefetch_related, arc_pk_rel, arc_group
57
34
  ):
58
35
  """Get the nav filter."""
59
- arc_pks = arc.get("pks", ())
36
+ if arc_pks := arc.get("pks", ()):
37
+ return arc_pks
38
+
39
+ comic_pk = self.kwargs["pk"]
40
+ try:
41
+ arc_pk_qs = Comic.objects.filter(pk=comic_pk)
42
+ arc_pk_qs = arc_pk_qs.select_related(*arc_pk_select_related)
43
+ arc_pk_qs = arc_pk_qs.prefetch_related(*prefetch_related)
44
+ arc_pk_qs = arc_pk_qs.values_list(arc_pk_rel, flat=True)
45
+ arc_pk = arc_pk_qs.first()
46
+ except IndexError:
47
+ arc_pk = 0
48
+
49
+ _, multi_arc_pks = self.get_group_pks_from_breadcrumbs([arc_group])
50
+ if not arc_pk or arc_pk in multi_arc_pks:
51
+ arc_pks = multi_arc_pks
52
+
60
53
  if not arc_pks:
61
- comic_pk = self.kwargs["pk"]
62
- try:
63
- arc_pk_qs = Comic.objects.filter(pk=comic_pk)
64
- arc_pk_qs = arc_pk_qs.select_related(*arc_pk_select_related)
65
- arc_pk_qs = arc_pk_qs.prefetch_related(*prefetch_related)
66
- arc_pk = arc_pk_qs.values_list(arc_pk_rel, flat=True)[0]
67
- except IndexError:
68
- arc_pk = 0
69
-
70
- if arc_pk_rel == "series__pk":
71
- multi_arc_pks = self.get_series_pks_from_breadcrumbs()
72
- if not arc_pk or arc_pk in multi_arc_pks:
73
- arc_pks = multi_arc_pks
74
- if not arc_pks:
75
- arc_pks = (arc_pk,)
54
+ arc_pks = (arc_pk,)
76
55
 
77
56
  return arc_pks
78
57
 
79
58
  def _get_comics_list(self):
80
59
  """Get the reader naviation group filter."""
81
- select_related = ("series",)
60
+ select_related = ("series", "volume")
82
61
  prefetch_related = ()
83
62
 
84
63
  arc: Mapping = self.params.get("arc", {}) # type: ignore
@@ -91,31 +70,35 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
91
70
  arc_pk_rel = "story_arc_numbers__story_arc__pk"
92
71
  prefetch_related = (*prefetch_related, "story_arc_numbers__story_arc")
93
72
  arc_index = F("story_arc_numbers__number")
94
- ordering = ("arc_index", "date")
73
+ ordering = ("arc_index", "date", "pk")
95
74
  arc_pk_select_related = ()
96
75
  elif arc_group == FOLDER_GROUP:
97
76
  # folder mode
98
77
  rel = "parent_folder"
99
78
  fields = (*_COMIC_FIELDS, "parent_folder")
100
79
  arc_pk_rel = "parent_folder__pk"
101
- select_related = (*select_related, "parent_folder")
102
- arc_index = Value(None, IntegerField())
80
+ arc_index = NONE_INTEGERFIELD
103
81
  ordering = ("path", "pk")
104
82
  arc_pk_select_related = ("parent_folder",)
83
+ elif arc_group == "v":
84
+ # volume
85
+ rel = "volume"
86
+ fields = _COMIC_FIELDS
87
+ arc_pk_rel = "volume__pk"
88
+ arc_index = NONE_INTEGERFIELD
89
+ ordering = ()
90
+ arc_pk_select_related = ("volume",)
105
91
  else:
106
- # browser mode.
92
+ # series
107
93
  rel = "series"
108
94
  fields = _COMIC_FIELDS
109
95
  arc_pk_rel = "series__pk"
110
- arc_index = Value(None, IntegerField())
96
+ arc_index = NONE_INTEGERFIELD
111
97
  ordering = ()
112
98
  arc_pk_select_related = ("series",)
113
99
 
114
100
  arc_pks = self._get_reader_arc_pks(
115
- arc,
116
- arc_pk_select_related,
117
- prefetch_related,
118
- arc_pk_rel,
101
+ arc, arc_pk_select_related, prefetch_related, arc_pk_rel, arc_group
119
102
  )
120
103
  nav_filter = {f"{rel}__in": arc_pks}
121
104
  group_acl_filter = self.get_group_acl_filter(Comic)
@@ -136,9 +119,11 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
136
119
  .annotate(mtime=F("updated_at"))
137
120
  )
138
121
  qs = self.annotate_group_names(qs, Comic)
139
- if arc_group == "s":
122
+ if arc_group in ("v", "s"):
123
+ show = self.params["show"]
124
+ model_group = "i" if arc_group == "s" else "s"
140
125
  qs, comic_sort_names = self.alias_sort_names(
141
- qs, Comic, pks=arc_pks, model_group="i", show=_SHOW
126
+ qs, Comic, pks=arc_pks, model_group=model_group, show=show
142
127
  )
143
128
  ordering = (
144
129
  *comic_sort_names,
@@ -146,7 +131,8 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
146
131
  "issue_suffix",
147
132
  "sort_name",
148
133
  )
149
- return qs.order_by(*ordering), arc_group
134
+ qs = qs.order_by(*ordering)
135
+ return qs, arc_group
150
136
 
151
137
  def _append_with_settings(self, book, bookmark_filter):
152
138
  """Append bookmark to book list."""
@@ -183,9 +169,9 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
183
169
  prev_book, bookmark_filter
184
170
  )
185
171
  # create extra current book attrs:
186
- if book.arc_index is None: # type: ignore
187
- book.arc_index = index + 1 # type: ignore
188
- book.filename = book.get_filename() # type: ignore
172
+ if book.arc_index is None:
173
+ book.arc_index = index + 1
174
+ book.filename = book.get_filename()
189
175
  book.arc_group = arc_group
190
176
  book.arc_count = comics.count()
191
177
  books["current"] = self._append_with_settings(book, bookmark_filter)
@@ -193,5 +179,3 @@ class ReaderBooksView(BookmarkBaseView, SessionView, SharedAnnotationsMixin):
193
179
  # Haven't matched yet, so set the previous comic
194
180
  prev_book = book
195
181
  return books
196
-
197
- #####
@@ -0,0 +1,121 @@
1
+ """Views for reading comic books."""
2
+
3
+ from copy import deepcopy
4
+ from types import MappingProxyType
5
+
6
+ from codex.logger.logging import get_logger
7
+ from codex.models import Comic
8
+ from codex.serializers.reader import (
9
+ ReaderViewInputSerializer,
10
+ )
11
+ from codex.views.const import FOLDER_GROUP, GROUP_RELATION, STORY_ARC_GROUP
12
+ from codex.views.session import SessionView
13
+ from codex.views.util import reparse_json_query_params
14
+
15
+ LOG = get_logger(__name__)
16
+ VALID_ARC_GROUPS = frozenset({"s", "v", "f", "a"})
17
+ _JSON_KEYS = frozenset({"arc", "breadcrumbs", "show"})
18
+ _BROWSER_SESSION_DEFAULTS = SessionView.SESSION_DEFAULTS[
19
+ SessionView.BROWSER_SESSION_KEY
20
+ ]
21
+ _DEFAULT_PARAMS = {
22
+ "breadcrumbs": _BROWSER_SESSION_DEFAULTS["breadcrumbs"],
23
+ "show": _BROWSER_SESSION_DEFAULTS["show"],
24
+ "top_group": _BROWSER_SESSION_DEFAULTS["top_group"],
25
+ }
26
+
27
+
28
+ class ReaderInitView(SessionView):
29
+ """Reader initialization."""
30
+
31
+ input_serializer_class = ReaderViewInputSerializer
32
+
33
+ def __init__(self, *args, **kwargs):
34
+ """Initialize instance vars."""
35
+ super().__init__(*args, **kwargs)
36
+ self.group_pks: dict[str, tuple[int, ...]] = {}
37
+
38
+ def _set_group_pks_from_breadcrumbs(self, groups, breadcrumbs):
39
+ """Set Multi-Group pks from breadcrumbs to the cache."""
40
+ index = len(breadcrumbs) - 1
41
+ min_index = max(index - 1, 0) if FOLDER_GROUP in groups else 0
42
+ while index > min_index:
43
+ crumb = breadcrumbs[index]
44
+ crumb_group = crumb.get("group")
45
+ if crumb_group in groups:
46
+ pks = crumb.get("pks", ())
47
+ self.group_pks[crumb_group] = pks
48
+ break
49
+ index -= 1
50
+ else:
51
+ crumb_group = ""
52
+ pks = ()
53
+ for group in groups:
54
+ self.group_pks[group] = pks
55
+ return crumb_group, pks
56
+
57
+ def get_group_pks_from_breadcrumbs(self, groups):
58
+ """Get Multi-Group pks from the breadcrumbs."""
59
+ # Return cached values
60
+ for group in groups:
61
+ if pks := self.group_pks.get(group):
62
+ return group, pks
63
+
64
+ # Validate input
65
+ breadcrumbs: tuple = self.params.get("breadcrumbs") # type: ignore
66
+ if not breadcrumbs:
67
+ return "", ()
68
+
69
+ return self._set_group_pks_from_breadcrumbs(groups, breadcrumbs)
70
+
71
+ def _ensure_arc_contains_comic(self, params):
72
+ """Arc sanity check."""
73
+ arc = params.get("arc")
74
+ if not arc:
75
+ return
76
+ pk = self.kwargs["pk"]
77
+ arc_group = arc.get("group")
78
+ arc_pks = arc.get("pks")
79
+ valid = False
80
+ if rel := GROUP_RELATION.get(arc_group):
81
+ arc_filter = {rel: arc_pks}
82
+ valid = Comic.objects.filter(pk=pk, **arc_filter).exists()
83
+ if not valid:
84
+ LOG.warning(f"Invalid arc {arc} submitted to reader for comic {pk}.")
85
+ params.pop("arc", None)
86
+
87
+ def _ensure_arc(self, params):
88
+ """arc.group validation."""
89
+ # Can't be in the serializer
90
+ arc = params.get("arc", {})
91
+
92
+ if arc.get("group") not in VALID_ARC_GROUPS:
93
+ top_group = params["top_group"]
94
+ if top_group in (FOLDER_GROUP, STORY_ARC_GROUP):
95
+ search_groups = (top_group,)
96
+ else:
97
+ search_groups = ("v", "s")
98
+
99
+ group, pks = self.get_group_pks_from_breadcrumbs(search_groups)
100
+ if not group:
101
+ group = "s"
102
+
103
+ arc["group"] = group
104
+ if pks:
105
+ arc["pks"] = pks
106
+
107
+ params["arc"] = arc
108
+
109
+ def _parse_params(self):
110
+ data = self.request.GET
111
+ data = reparse_json_query_params(self.request.GET, _JSON_KEYS)
112
+ serializer = self.input_serializer_class(data=data)
113
+ serializer.is_valid(raise_exception=True)
114
+
115
+ params = deepcopy(_DEFAULT_PARAMS)
116
+ if serializer.validated_data:
117
+ params.update(serializer.validated_data) # type: ignore
118
+ self._ensure_arc_contains_comic(params)
119
+ self._ensure_arc(params)
120
+
121
+ self.params = MappingProxyType(params)
@@ -1,43 +1,30 @@
1
1
  """Views for reading comic books."""
2
2
 
3
- from types import MappingProxyType
4
-
5
3
  from comicbox.box import Comicbox
6
4
  from django.urls import reverse
7
- from drf_spectacular.types import OpenApiTypes
8
- from drf_spectacular.utils import OpenApiParameter, extend_schema
9
- from rest_framework.exceptions import NotFound, ValidationError
5
+ from drf_spectacular.utils import extend_schema
6
+ from rest_framework.exceptions import NotFound
10
7
  from rest_framework.response import Response
11
8
 
12
9
  from codex.librarian.importer.tasks import LazyImportComicsTask
13
10
  from codex.librarian.mp_queue import LIBRARIAN_QUEUE
14
11
  from codex.logger.logging import get_logger
15
- from codex.serializers.reader import ReaderArcSerializer, ReaderComicsSerializer
12
+ from codex.serializers.reader import ReaderComicsSerializer, ReaderViewInputSerializer
16
13
  from codex.serializers.redirect import ReaderRedirectSerializer
17
14
  from codex.views.reader.arcs import ReaderArcsView
18
- from codex.views.util import pop_name, reparse_json_query_params
15
+ from codex.views.util import pop_name
19
16
 
20
17
  LOG = get_logger(__name__)
21
- _VALID_ARC_GROUPS = frozenset({"f", "s", "a"})
22
- _JSON_KEYS = frozenset({"arc"})
23
18
 
24
19
 
25
- class ReaderView(
26
- ReaderArcsView,
27
- ):
20
+ class ReaderView(ReaderArcsView):
28
21
  """Get info for displaying comic pages."""
29
22
 
30
23
  serializer_class = ReaderComicsSerializer
31
- input_serializer_class = ReaderArcSerializer
32
24
 
33
25
  SESSION_KEY = "reader"
34
26
  TARGET = "reader"
35
27
 
36
- def __init__(self, *args, **kwargs):
37
- """Initialize instance vars."""
38
- super().__init__(*args, **kwargs)
39
- self.series_pks: tuple[int, ...] = ()
40
-
41
28
  def _raise_not_found(self):
42
29
  """Raise not found exception."""
43
30
  pk = self.kwargs.get("pk")
@@ -48,48 +35,6 @@ class ReaderView(
48
35
  }
49
36
  raise NotFound(detail=detail)
50
37
 
51
- def _validate_params(self, arc, top_group):
52
- """arc.group validation."""
53
- # Can't be in the serializer
54
- arc_group = arc.get("group", "")
55
- if not arc_group:
56
- series_pks = self.get_series_pks_from_breadcrumbs()
57
- if series_pks:
58
- arc_group = "s"
59
- arc["pks"] = series_pks
60
- if not arc_group:
61
- arc_group = top_group
62
- if arc_group not in _VALID_ARC_GROUPS:
63
- arc_group = None
64
-
65
- def _parse_params(self):
66
- data = self.request.GET
67
- arc = {}
68
- data = reparse_json_query_params(self.request.GET, _JSON_KEYS)
69
- arc_data = data.get("arc", [])
70
- if arc_data:
71
- serializer = self.input_serializer_class(data=arc_data)
72
- try:
73
- serializer.is_valid()
74
- arc: dict = serializer.validated_data # type: ignore
75
- except ValidationError:
76
- pass
77
-
78
- # From Browser Session
79
- top_group = self.get_from_session(
80
- "top_group", "s", session_key=self.BROWSER_SESSION_KEY
81
- )
82
-
83
- self._validate_params(arc, top_group)
84
-
85
- params = MappingProxyType(
86
- {
87
- "arc": arc,
88
- "top_group": top_group,
89
- }
90
- )
91
- self.params = params
92
-
93
38
  @staticmethod
94
39
  def _lazy_metadata(current, prev_book, next_book):
95
40
  """Get reader metadata from comicbox if it's not in the db."""
@@ -117,7 +62,6 @@ class ReaderView(
117
62
  def get_object(self):
118
63
  """Get the previous and next comics in a group or story arc."""
119
64
  # Books
120
- self.last_route = self.get_last_route()
121
65
  books = self.get_book_collection()
122
66
 
123
67
  current = books.get("current")
@@ -144,7 +88,9 @@ class ReaderView(
144
88
  arc["pks"] = (current.arc_pk,) # type: ignore
145
89
  arc["index"] = current.arc_index # type:ignore
146
90
  arc["count"] = current.arc_count # type: ignore
147
- close_route = pop_name(self.last_route)
91
+
92
+ close_route = self.get_last_route()
93
+ close_route = pop_name(close_route)
148
94
 
149
95
  return {
150
96
  "books": books,
@@ -154,14 +100,7 @@ class ReaderView(
154
100
  "mtime": mtime,
155
101
  }
156
102
 
157
- @extend_schema(
158
- parameters=[
159
- OpenApiParameter(
160
- "arc[group]", OpenApiTypes.STR, enum=sorted(_VALID_ARC_GROUPS)
161
- ),
162
- OpenApiParameter("arc[pks]", OpenApiTypes.STR),
163
- ]
164
- )
103
+ @extend_schema(parameters=[ReaderViewInputSerializer])
165
104
  def get(self, *args, **kwargs):
166
105
  """Get the book info."""
167
106
  self._parse_params()
codex/views/session.py CHANGED
@@ -102,17 +102,6 @@ class SessionView(AuthFilterGenericAPIView, ABC):
102
102
 
103
103
  return last_route
104
104
 
105
- def load_params_from_session(self, validated_data=None, session_key=None):
106
- """Load params from the session with defaults."""
107
- session_key = session_key if session_key else self.SESSION_KEY
108
- params = deepcopy(dict(self.SESSION_DEFAULTS[session_key]))
109
- session_params = self.request.session.get(session_key)
110
- if session_params:
111
- params.update(session_params)
112
- if validated_data:
113
- params.update(validated_data)
114
- self.params = MappingProxyType(params)
115
-
116
105
  @classmethod
117
106
  def _get_source_values_or_set_defaults(cls, defaults_dict, source_dict, data):
118
107
  """Recursively copy source_dict values into data or use defaults."""
@@ -128,6 +117,21 @@ class SessionView(AuthFilterGenericAPIView, ABC):
128
117
  )
129
118
  return result
130
119
 
120
+ def load_params_from_session(self, session_key=None, only=None):
121
+ """Get session settings with defaults."""
122
+ if not session_key:
123
+ session_key = self.SESSION_KEY
124
+ if only:
125
+ defaults = {}
126
+ for key in only:
127
+ if key:
128
+ defaults[key] = self.SESSION_DEFAULTS[session_key][key]
129
+ else:
130
+ defaults = self.SESSION_DEFAULTS[session_key]
131
+
132
+ session = self.request.session.get(session_key, defaults)
133
+ return self._get_source_values_or_set_defaults(defaults, session, {})
134
+
131
135
  def save_params_to_session(self, params): # reader session & browser final
132
136
  """Save the session from params with defaults for missing values."""
133
137
  try: