codex 1.6.0a5__py3-none-any.whl → 1.6.0a7__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 (564) hide show
  1. codex/exceptions.py +1 -0
  2. codex/librarian/covers/purge.py +1 -2
  3. codex/librarian/importer/aggregate_metadata.py +2 -10
  4. codex/librarian/importer/cache.py +47 -0
  5. codex/librarian/importer/const.py +8 -0
  6. codex/librarian/importer/deleted.py +45 -7
  7. codex/librarian/importer/importerd.py +11 -1
  8. codex/librarian/importer/update_comics.py +0 -2
  9. codex/librarian/janitor/janitor.py +1 -1
  10. codex/librarian/librariand.py +1 -1
  11. codex/migrations/0029_choices_adminflag_and_timestamp.py +44 -0
  12. codex/models/admin.py +0 -1
  13. codex/permissions.py +1 -1
  14. codex/serializers/admin.py +1 -2
  15. codex/serializers/browser/filters.py +8 -101
  16. codex/serializers/browser/mixins.py +20 -3
  17. codex/serializers/browser/page.py +2 -3
  18. codex/serializers/browser/settings.py +2 -0
  19. codex/serializers/fields.py +123 -0
  20. codex/serializers/mtime.py +25 -0
  21. codex/serializers/opds/v2.py +7 -11
  22. codex/serializers/opds/v2_unused.py +5 -3
  23. codex/serializers/reader.py +5 -2
  24. codex/serializers/route.py +7 -3
  25. codex/static_root/assets/VCheckbox-BOUtyxuo.c690f0cdbe48.js +1 -0
  26. codex/static_root/assets/VCheckbox-BOUtyxuo.c690f0cdbe48.js.br +0 -0
  27. codex/static_root/assets/VCheckbox-BOUtyxuo.c690f0cdbe48.js.gz +0 -0
  28. codex/static_root/assets/VCheckbox-BOUtyxuo.js +1 -0
  29. codex/static_root/assets/VCheckbox-BOUtyxuo.js.br +0 -0
  30. codex/static_root/assets/VCheckbox-BOUtyxuo.js.gz +0 -0
  31. codex/static_root/assets/VCheckboxBtn-B1-m5pEh.e2b8cbdb92c9.js +1 -0
  32. codex/static_root/assets/VCheckboxBtn-B1-m5pEh.e2b8cbdb92c9.js.br +0 -0
  33. codex/static_root/assets/VCheckboxBtn-B1-m5pEh.e2b8cbdb92c9.js.gz +0 -0
  34. codex/static_root/assets/VCheckboxBtn-B1-m5pEh.js +1 -0
  35. codex/static_root/assets/VCheckboxBtn-B1-m5pEh.js.br +0 -0
  36. codex/static_root/assets/VCheckboxBtn-B1-m5pEh.js.gz +0 -0
  37. codex/static_root/assets/VCombobox-DjkDXe33.22617ea193b1.js +1 -0
  38. codex/static_root/assets/VCombobox-DjkDXe33.22617ea193b1.js.br +0 -0
  39. codex/static_root/assets/VCombobox-DjkDXe33.22617ea193b1.js.gz +0 -0
  40. codex/static_root/assets/VCombobox-DjkDXe33.js +1 -0
  41. codex/static_root/assets/VCombobox-DjkDXe33.js.br +0 -0
  42. codex/static_root/assets/VCombobox-DjkDXe33.js.gz +0 -0
  43. codex/static_root/assets/VDialog-X0zn9AGX.0d89c749b9a6.js +1 -0
  44. codex/static_root/assets/VDialog-X0zn9AGX.0d89c749b9a6.js.br +0 -0
  45. codex/static_root/assets/VDialog-X0zn9AGX.0d89c749b9a6.js.gz +0 -0
  46. codex/static_root/assets/VDialog-X0zn9AGX.js +1 -0
  47. codex/static_root/assets/VDialog-X0zn9AGX.js.br +0 -0
  48. codex/static_root/assets/VDialog-X0zn9AGX.js.gz +0 -0
  49. codex/static_root/assets/VExpansionPanels-CRevojaF.43d65c777c58.js +1 -0
  50. codex/static_root/assets/VExpansionPanels-CRevojaF.43d65c777c58.js.br +0 -0
  51. codex/static_root/assets/VExpansionPanels-CRevojaF.43d65c777c58.js.gz +0 -0
  52. codex/static_root/assets/VExpansionPanels-CRevojaF.js +1 -0
  53. codex/static_root/assets/VExpansionPanels-CRevojaF.js.br +0 -0
  54. codex/static_root/assets/VExpansionPanels-CRevojaF.js.gz +0 -0
  55. codex/static_root/assets/VRadioGroup-8nrrxjDv.2b4dfb984a8c.js +1 -0
  56. codex/static_root/assets/VRadioGroup-8nrrxjDv.2b4dfb984a8c.js.br +0 -0
  57. codex/static_root/assets/VRadioGroup-8nrrxjDv.2b4dfb984a8c.js.gz +0 -0
  58. codex/static_root/assets/VRadioGroup-8nrrxjDv.js +1 -0
  59. codex/static_root/assets/VRadioGroup-8nrrxjDv.js.br +0 -0
  60. codex/static_root/assets/VRadioGroup-8nrrxjDv.js.gz +0 -0
  61. codex/static_root/assets/VSelect-DgfIKRnC.fa7a73a9c415.js +1 -0
  62. codex/static_root/assets/VSelect-DgfIKRnC.fa7a73a9c415.js.br +0 -0
  63. codex/static_root/assets/VSelect-DgfIKRnC.fa7a73a9c415.js.gz +0 -0
  64. codex/static_root/assets/VSelect-DgfIKRnC.js +1 -0
  65. codex/static_root/assets/VSelect-DgfIKRnC.js.br +0 -0
  66. codex/static_root/assets/VSelect-DgfIKRnC.js.gz +0 -0
  67. codex/static_root/assets/VSelectionControl-CGu0oz0K.3030b7a270d6.js +1 -0
  68. codex/static_root/assets/VSelectionControl-CGu0oz0K.3030b7a270d6.js.br +0 -0
  69. codex/static_root/assets/VSelectionControl-CGu0oz0K.3030b7a270d6.js.gz +0 -0
  70. codex/static_root/assets/VSelectionControl-CGu0oz0K.js +1 -0
  71. codex/static_root/assets/VSelectionControl-CGu0oz0K.js.br +0 -0
  72. codex/static_root/assets/VSelectionControl-CGu0oz0K.js.gz +0 -0
  73. codex/static_root/assets/VSlideGroup-D_oNvCOd.71a7101cdddd.js +1 -0
  74. codex/static_root/assets/VSlideGroup-D_oNvCOd.71a7101cdddd.js.br +0 -0
  75. codex/static_root/assets/VSlideGroup-D_oNvCOd.71a7101cdddd.js.gz +0 -0
  76. codex/static_root/assets/VSlideGroup-D_oNvCOd.js +1 -0
  77. codex/static_root/assets/VSlideGroup-D_oNvCOd.js.br +0 -0
  78. codex/static_root/assets/VSlideGroup-D_oNvCOd.js.gz +0 -0
  79. codex/static_root/assets/{VTable-FKMTcdkF.6c1eed84d25e.js → VTable-C7pKI7gU.087912f706f5.js} +1 -1
  80. codex/static_root/assets/VTable-C7pKI7gU.087912f706f5.js.br +0 -0
  81. codex/static_root/assets/VTable-C7pKI7gU.087912f706f5.js.gz +0 -0
  82. codex/static_root/assets/{VTable-FKMTcdkF.js → VTable-C7pKI7gU.js} +1 -1
  83. codex/static_root/assets/VTable-C7pKI7gU.js.br +0 -0
  84. codex/static_root/assets/VTable-C7pKI7gU.js.gz +0 -0
  85. codex/static_root/assets/{VTextField-Dftcik85.js → VTextField-Bs8oq9mk.7999fef12a03.js} +1 -1
  86. codex/static_root/assets/VTextField-Bs8oq9mk.7999fef12a03.js.br +0 -0
  87. codex/static_root/assets/VTextField-Bs8oq9mk.7999fef12a03.js.gz +0 -0
  88. codex/static_root/assets/{VTextField-Dftcik85.cb2f1dda87e1.js → VTextField-Bs8oq9mk.js} +1 -1
  89. codex/static_root/assets/VTextField-Bs8oq9mk.js.br +0 -0
  90. codex/static_root/assets/VTextField-Bs8oq9mk.js.gz +0 -0
  91. codex/static_root/assets/{VWindowItem-CYCwnt3s.28025dc36c6a.js → VWindowItem-iyZ1XOkP.409f7d292253.js} +1 -1
  92. codex/static_root/assets/VWindowItem-iyZ1XOkP.409f7d292253.js.br +0 -0
  93. codex/static_root/assets/VWindowItem-iyZ1XOkP.409f7d292253.js.gz +0 -0
  94. codex/static_root/assets/{VWindowItem-CYCwnt3s.js → VWindowItem-iyZ1XOkP.js} +1 -1
  95. codex/static_root/assets/VWindowItem-iyZ1XOkP.js.br +0 -0
  96. codex/static_root/assets/VWindowItem-iyZ1XOkP.js.gz +0 -0
  97. codex/static_root/assets/admin-B0pCBjma.2407da79bd20.js +1 -0
  98. codex/static_root/assets/admin-B0pCBjma.2407da79bd20.js.br +0 -0
  99. codex/static_root/assets/admin-B0pCBjma.2407da79bd20.js.gz +0 -0
  100. codex/static_root/assets/admin-B0pCBjma.js +1 -0
  101. codex/static_root/assets/admin-B0pCBjma.js.br +0 -0
  102. codex/static_root/assets/admin-B0pCBjma.js.gz +0 -0
  103. codex/static_root/assets/admin-drawer-panel-BxoNUHQr.5e2e1a4a255d.js +30 -0
  104. codex/static_root/assets/admin-drawer-panel-BxoNUHQr.5e2e1a4a255d.js.br +0 -0
  105. codex/static_root/assets/admin-drawer-panel-BxoNUHQr.5e2e1a4a255d.js.gz +0 -0
  106. codex/static_root/assets/admin-drawer-panel-BxoNUHQr.js +30 -0
  107. codex/static_root/assets/admin-drawer-panel-BxoNUHQr.js.br +0 -0
  108. codex/static_root/assets/admin-drawer-panel-BxoNUHQr.js.gz +0 -0
  109. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css → admin-drawer-panel-o87BwD38.ce39427c8201.css} +1 -1
  110. codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.br +0 -0
  111. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.gz → admin-drawer-panel-o87BwD38.ce39427c8201.css.gz} +0 -0
  112. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.css → admin-drawer-panel-o87BwD38.css} +1 -1
  113. codex/static_root/assets/admin-drawer-panel-o87BwD38.css.br +0 -0
  114. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.css.gz → admin-drawer-panel-o87BwD38.css.gz} +0 -0
  115. codex/static_root/assets/browser-DbyAjCNh.577a228a966b.js +1 -0
  116. codex/static_root/assets/browser-DbyAjCNh.577a228a966b.js.br +0 -0
  117. codex/static_root/assets/browser-DbyAjCNh.577a228a966b.js.gz +0 -0
  118. codex/static_root/assets/browser-DbyAjCNh.js +1 -0
  119. codex/static_root/assets/browser-DbyAjCNh.js.br +0 -0
  120. codex/static_root/assets/browser-DbyAjCNh.js.gz +0 -0
  121. codex/static_root/assets/browser-VnzNj1t3.cff861ad2ca3.css +1 -0
  122. codex/static_root/assets/browser-VnzNj1t3.cff861ad2ca3.css.br +0 -0
  123. codex/static_root/assets/browser-VnzNj1t3.cff861ad2ca3.css.gz +0 -0
  124. codex/static_root/assets/browser-VnzNj1t3.css +1 -0
  125. codex/static_root/assets/browser-VnzNj1t3.css.br +0 -0
  126. codex/static_root/assets/browser-VnzNj1t3.css.gz +0 -0
  127. codex/static_root/assets/{change-password-dialog-BIBG6Bk8.3583021c124a.js → change-password-dialog-DZ9dP8_F.930f4cae3cb0.js} +1 -1
  128. codex/static_root/assets/change-password-dialog-DZ9dP8_F.930f4cae3cb0.js.br +0 -0
  129. codex/static_root/assets/change-password-dialog-DZ9dP8_F.930f4cae3cb0.js.gz +0 -0
  130. codex/static_root/assets/{change-password-dialog-BIBG6Bk8.js → change-password-dialog-DZ9dP8_F.js} +1 -1
  131. codex/static_root/assets/change-password-dialog-DZ9dP8_F.js.br +0 -0
  132. codex/static_root/assets/change-password-dialog-DZ9dP8_F.js.gz +0 -0
  133. codex/static_root/assets/{confirm-dialog-DARLmXRf.53fc6938fb8c.js → confirm-dialog-RP7JNStF.a752a7c1e697.js} +1 -1
  134. codex/static_root/assets/confirm-dialog-RP7JNStF.a752a7c1e697.js.br +0 -0
  135. codex/static_root/assets/confirm-dialog-RP7JNStF.a752a7c1e697.js.gz +0 -0
  136. codex/static_root/assets/{confirm-dialog-DARLmXRf.js → confirm-dialog-RP7JNStF.js} +1 -1
  137. codex/static_root/assets/confirm-dialog-RP7JNStF.js.br +0 -0
  138. codex/static_root/assets/confirm-dialog-RP7JNStF.js.gz +0 -0
  139. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +1 -0
  140. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
  141. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +1 -0
  142. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
  143. codex/static_root/assets/{datetime-column-LpLgWpkf.16ce3324970f.js → datetime-column-Cu3WYyPj.54b4c3817b74.js} +1 -1
  144. codex/static_root/assets/datetime-column-Cu3WYyPj.54b4c3817b74.js.br +0 -0
  145. codex/static_root/assets/datetime-column-Cu3WYyPj.54b4c3817b74.js.gz +0 -0
  146. codex/static_root/assets/{datetime-column-LpLgWpkf.js → datetime-column-Cu3WYyPj.js} +1 -1
  147. codex/static_root/assets/datetime-column-Cu3WYyPj.js.br +0 -0
  148. codex/static_root/assets/datetime-column-Cu3WYyPj.js.gz +0 -0
  149. codex/static_root/assets/filter-DAdGUt-1.3261cbcd50b5.js +1 -0
  150. codex/static_root/assets/filter-DAdGUt-1.3261cbcd50b5.js.br +0 -0
  151. codex/static_root/assets/filter-DAdGUt-1.3261cbcd50b5.js.gz +0 -0
  152. codex/static_root/assets/filter-DAdGUt-1.js +1 -0
  153. codex/static_root/assets/filter-DAdGUt-1.js.br +0 -0
  154. codex/static_root/assets/filter-DAdGUt-1.js.gz +0 -0
  155. codex/static_root/assets/flag-tab-BP7Zs7iQ.cb16ef6363d4.js +1 -0
  156. codex/static_root/assets/flag-tab-BP7Zs7iQ.cb16ef6363d4.js.br +0 -0
  157. codex/static_root/assets/flag-tab-BP7Zs7iQ.cb16ef6363d4.js.gz +0 -0
  158. codex/static_root/assets/flag-tab-BP7Zs7iQ.js +1 -0
  159. codex/static_root/assets/flag-tab-BP7Zs7iQ.js.br +0 -0
  160. codex/static_root/assets/flag-tab-BP7Zs7iQ.js.gz +0 -0
  161. codex/static_root/assets/{group-tab-MK1lcjUU.98406d5babdc.js → group-tab-DEwh4jfB.a95097b5e320.js} +1 -1
  162. codex/static_root/assets/group-tab-DEwh4jfB.a95097b5e320.js.br +0 -0
  163. codex/static_root/assets/group-tab-DEwh4jfB.a95097b5e320.js.gz +0 -0
  164. codex/static_root/assets/{group-tab-MK1lcjUU.js → group-tab-DEwh4jfB.js} +1 -1
  165. codex/static_root/assets/group-tab-DEwh4jfB.js.br +0 -0
  166. codex/static_root/assets/group-tab-DEwh4jfB.js.gz +0 -0
  167. codex/static_root/assets/http-error-xs3mIL8U.e143c472a96c.js +1 -0
  168. codex/static_root/assets/http-error-xs3mIL8U.e143c472a96c.js.br +0 -0
  169. codex/static_root/assets/http-error-xs3mIL8U.e143c472a96c.js.gz +0 -0
  170. codex/static_root/assets/http-error-xs3mIL8U.js +1 -0
  171. codex/static_root/assets/http-error-xs3mIL8U.js.br +0 -0
  172. codex/static_root/assets/http-error-xs3mIL8U.js.gz +0 -0
  173. codex/static_root/assets/{library-tab-DYeEBYlT.a2a2ef088d1c.js → library-tab-DrXvD9B2.f765f9d3bae4.js} +1 -1
  174. codex/static_root/assets/library-tab-DrXvD9B2.f765f9d3bae4.js.br +0 -0
  175. codex/static_root/assets/library-tab-DrXvD9B2.f765f9d3bae4.js.gz +0 -0
  176. codex/static_root/assets/{library-tab-DYeEBYlT.js → library-tab-DrXvD9B2.js} +1 -1
  177. codex/static_root/assets/library-tab-DrXvD9B2.js.br +0 -0
  178. codex/static_root/assets/library-tab-DrXvD9B2.js.gz +0 -0
  179. codex/static_root/assets/main-Dy9Uqpmh.2f99b1914b3e.js +32 -0
  180. codex/static_root/assets/main-Dy9Uqpmh.2f99b1914b3e.js.br +0 -0
  181. codex/static_root/assets/main-Dy9Uqpmh.2f99b1914b3e.js.gz +0 -0
  182. codex/static_root/assets/main-Dy9Uqpmh.js +32 -0
  183. codex/static_root/assets/main-Dy9Uqpmh.js.br +0 -0
  184. codex/static_root/assets/main-Dy9Uqpmh.js.gz +0 -0
  185. codex/static_root/assets/{main-sK7HC5h4.css → main-Ztp855PW.css} +1 -1
  186. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  187. codex/static_root/assets/{main-sK7HC5h4.css.gz → main-Ztp855PW.css.gz} +0 -0
  188. codex/static_root/assets/{main-sK7HC5h4.6f04ddfc5f1a.css → main-Ztp855PW.d46d718fac45.css} +1 -1
  189. codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
  190. codex/static_root/assets/{main-sK7HC5h4.6f04ddfc5f1a.css.gz → main-Ztp855PW.d46d718fac45.css.gz} +0 -0
  191. codex/static_root/assets/pagination-toolbar-DbS_HOT1.81222ab51ef4.js +1 -0
  192. codex/static_root/assets/pagination-toolbar-DbS_HOT1.81222ab51ef4.js.br +0 -0
  193. codex/static_root/assets/pagination-toolbar-DbS_HOT1.81222ab51ef4.js.gz +0 -0
  194. codex/static_root/assets/pagination-toolbar-DbS_HOT1.js +1 -0
  195. codex/static_root/assets/pagination-toolbar-DbS_HOT1.js.br +0 -0
  196. codex/static_root/assets/pagination-toolbar-DbS_HOT1.js.gz +0 -0
  197. codex/static_root/assets/{pagination-toolbar-ClZui9Zf.697d7ae11c21.css → pagination-toolbar-r6nbtWxg.4088ab042c0c.css} +1 -1
  198. codex/static_root/assets/pagination-toolbar-r6nbtWxg.4088ab042c0c.css.br +0 -0
  199. codex/static_root/assets/pagination-toolbar-r6nbtWxg.4088ab042c0c.css.gz +0 -0
  200. codex/static_root/assets/{pagination-toolbar-ClZui9Zf.css → pagination-toolbar-r6nbtWxg.css} +1 -1
  201. codex/static_root/assets/pagination-toolbar-r6nbtWxg.css.br +0 -0
  202. codex/static_root/assets/pagination-toolbar-r6nbtWxg.css.gz +0 -0
  203. codex/static_root/assets/pdf-doc-obrBwxa6.ec9bd13a5786.js +305 -0
  204. codex/static_root/assets/pdf-doc-obrBwxa6.ec9bd13a5786.js.br +0 -0
  205. codex/static_root/assets/pdf-doc-obrBwxa6.ec9bd13a5786.js.gz +0 -0
  206. codex/static_root/assets/pdf-doc-obrBwxa6.js +305 -0
  207. codex/static_root/assets/pdf-doc-obrBwxa6.js.br +0 -0
  208. codex/static_root/assets/pdf-doc-obrBwxa6.js.gz +0 -0
  209. codex/static_root/assets/reader-BXKUaUeC.953d30b969ef.js +2 -0
  210. codex/static_root/assets/reader-BXKUaUeC.953d30b969ef.js.br +0 -0
  211. codex/static_root/assets/reader-BXKUaUeC.953d30b969ef.js.gz +0 -0
  212. codex/static_root/assets/reader-BXKUaUeC.js +2 -0
  213. codex/static_root/assets/reader-BXKUaUeC.js.br +0 -0
  214. codex/static_root/assets/reader-BXKUaUeC.js.gz +0 -0
  215. codex/static_root/assets/{reader-CF3xt6JG.19fad7f62450.css → reader-OXJ1KVaW.a463b19d0508.css} +1 -1
  216. codex/static_root/assets/reader-OXJ1KVaW.a463b19d0508.css.br +0 -0
  217. codex/static_root/assets/reader-OXJ1KVaW.a463b19d0508.css.gz +0 -0
  218. codex/static_root/assets/{reader-CF3xt6JG.css → reader-OXJ1KVaW.css} +1 -1
  219. codex/static_root/assets/reader-OXJ1KVaW.css.br +0 -0
  220. codex/static_root/assets/reader-OXJ1KVaW.css.gz +0 -0
  221. codex/static_root/assets/relation-chips-CZJcLjc8.1d7426dab654.js +1 -0
  222. codex/static_root/assets/relation-chips-CZJcLjc8.1d7426dab654.js.br +0 -0
  223. codex/static_root/assets/relation-chips-CZJcLjc8.1d7426dab654.js.gz +0 -0
  224. codex/static_root/assets/relation-chips-CZJcLjc8.js +1 -0
  225. codex/static_root/assets/relation-chips-CZJcLjc8.js.br +0 -0
  226. codex/static_root/assets/relation-chips-CZJcLjc8.js.gz +0 -0
  227. codex/static_root/assets/settings-drawer-7iTnAdjf.337eecf2fdbb.js +2 -0
  228. codex/static_root/assets/settings-drawer-7iTnAdjf.337eecf2fdbb.js.br +0 -0
  229. codex/static_root/assets/settings-drawer-7iTnAdjf.337eecf2fdbb.js.gz +0 -0
  230. codex/static_root/assets/settings-drawer-7iTnAdjf.js +2 -0
  231. codex/static_root/assets/settings-drawer-7iTnAdjf.js.br +0 -0
  232. codex/static_root/assets/settings-drawer-7iTnAdjf.js.gz +0 -0
  233. codex/static_root/assets/{settings-drawer-BJOfMtqA.6471a50fc05b.css → settings-drawer-nZ9HvKSZ.01744e131806.css} +1 -1
  234. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
  235. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
  236. codex/static_root/assets/{settings-drawer-BJOfMtqA.css → settings-drawer-nZ9HvKSZ.css} +1 -1
  237. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
  238. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
  239. codex/static_root/assets/stats-tab-CQsK1n8O.544bf50ffb22.js +1 -0
  240. codex/static_root/assets/stats-tab-CQsK1n8O.544bf50ffb22.js.br +0 -0
  241. codex/static_root/assets/stats-tab-CQsK1n8O.544bf50ffb22.js.gz +0 -0
  242. codex/static_root/assets/stats-tab-CQsK1n8O.js +1 -0
  243. codex/static_root/assets/stats-tab-CQsK1n8O.js.br +0 -0
  244. codex/static_root/assets/stats-tab-CQsK1n8O.js.gz +0 -0
  245. codex/static_root/assets/task-tab-C60TLP6e.63aa929a4ed6.js +1 -0
  246. codex/static_root/assets/task-tab-C60TLP6e.63aa929a4ed6.js.br +0 -0
  247. codex/static_root/assets/task-tab-C60TLP6e.63aa929a4ed6.js.gz +0 -0
  248. codex/static_root/assets/task-tab-C60TLP6e.js +1 -0
  249. codex/static_root/assets/task-tab-C60TLP6e.js.br +0 -0
  250. codex/static_root/assets/task-tab-C60TLP6e.js.gz +0 -0
  251. codex/static_root/assets/to-case-CR9beRR0.21bb805fdab4.js +1 -0
  252. codex/static_root/assets/to-case-CR9beRR0.js +1 -0
  253. codex/static_root/assets/unauthorized-Bs7NSqea.c55650fac055.js +1 -0
  254. codex/static_root/assets/unauthorized-Bs7NSqea.c55650fac055.js.br +0 -0
  255. codex/static_root/assets/unauthorized-Bs7NSqea.c55650fac055.js.gz +0 -0
  256. codex/static_root/assets/unauthorized-Bs7NSqea.js +1 -0
  257. codex/static_root/assets/unauthorized-Bs7NSqea.js.br +0 -0
  258. codex/static_root/assets/unauthorized-Bs7NSqea.js.gz +0 -0
  259. codex/static_root/assets/user-tab-ChuH7Atm.7e80cf1fb78e.js +1 -0
  260. codex/static_root/assets/user-tab-ChuH7Atm.7e80cf1fb78e.js.br +0 -0
  261. codex/static_root/assets/user-tab-ChuH7Atm.7e80cf1fb78e.js.gz +0 -0
  262. codex/static_root/assets/user-tab-ChuH7Atm.js +1 -0
  263. codex/static_root/assets/user-tab-ChuH7Atm.js.br +0 -0
  264. codex/static_root/assets/user-tab-ChuH7Atm.js.gz +0 -0
  265. codex/static_root/js/choices.079a01e0be1a.json +1 -0
  266. codex/static_root/js/choices.079a01e0be1a.json.br +0 -0
  267. codex/static_root/js/choices.079a01e0be1a.json.gz +0 -0
  268. codex/static_root/js/choices.json +1 -1
  269. codex/static_root/js/choices.json.br +0 -0
  270. codex/static_root/js/choices.json.gz +0 -0
  271. codex/static_root/manifest.dad79c8475ae.json +629 -0
  272. codex/static_root/manifest.dad79c8475ae.json.br +0 -0
  273. codex/static_root/manifest.dad79c8475ae.json.gz +0 -0
  274. codex/static_root/manifest.json +265 -278
  275. codex/static_root/manifest.json.br +0 -0
  276. codex/static_root/manifest.json.gz +0 -0
  277. codex/static_root/staticfiles.json +1 -1
  278. codex/urls/api/auth.py +2 -1
  279. codex/urls/api/browser.py +2 -2
  280. codex/urls/api/reader.py +2 -2
  281. codex/urls/api/v3.py +2 -0
  282. codex/util.py +10 -0
  283. codex/views/admin/api_key.py +2 -6
  284. codex/views/admin/auth.py +30 -0
  285. codex/views/admin/flag.py +2 -7
  286. codex/views/admin/group.py +2 -6
  287. codex/views/admin/library.py +7 -20
  288. codex/views/admin/stats.py +7 -66
  289. codex/views/admin/tasks.py +12 -11
  290. codex/views/admin/user.py +3 -9
  291. codex/views/auth.py +31 -52
  292. codex/views/bookmark.py +7 -11
  293. codex/views/browser/{browser_annotations.py → annotations.py} +119 -78
  294. codex/views/browser/base.py +24 -72
  295. codex/views/browser/breadcrumbs.py +200 -0
  296. codex/views/browser/browser.py +64 -344
  297. codex/views/browser/choices.py +25 -33
  298. codex/views/browser/filters/annotations.py +2 -2
  299. codex/views/browser/filters/bookmark.py +9 -13
  300. codex/views/browser/filters/field.py +9 -6
  301. codex/views/browser/filters/group.py +8 -5
  302. codex/views/browser/filters/search.py +11 -8
  303. codex/views/browser/metadata.py +3 -7
  304. codex/views/browser/{browser_order_by.py → order_by.py} +2 -6
  305. codex/views/browser/paginate.py +106 -0
  306. codex/views/browser/{session.py → settings.py} +4 -3
  307. codex/views/browser/title.py +48 -0
  308. codex/views/browser/validate.py +158 -0
  309. codex/views/const.py +97 -0
  310. codex/views/cover.py +4 -6
  311. codex/views/download.py +5 -8
  312. codex/views/frontend.py +4 -2
  313. codex/views/mtime.py +70 -0
  314. codex/views/opds/auth.py +17 -0
  315. codex/views/opds/authentication_v1.py +46 -45
  316. codex/views/opds/binary.py +4 -9
  317. codex/views/opds/const.py +13 -0
  318. codex/views/opds/urls.py +5 -4
  319. codex/views/opds/util.py +2 -8
  320. codex/views/opds/v1/facets.py +6 -7
  321. codex/views/opds/v1/feed.py +10 -17
  322. codex/views/opds/v1/links.py +6 -5
  323. codex/views/opds/v1/opensearch_v1.py +2 -6
  324. codex/views/opds/v2/feed.py +12 -15
  325. codex/views/opds/v2/links.py +3 -2
  326. codex/views/opds/v2/publications.py +21 -20
  327. codex/views/opds/v2/top_links.py +2 -2
  328. codex/views/public.py +38 -0
  329. codex/views/reader/arcs.py +101 -0
  330. codex/views/reader/books.py +199 -0
  331. codex/views/reader/page.py +3 -7
  332. codex/views/reader/reader.py +53 -298
  333. codex/views/reader/{session.py → settings.py} +4 -2
  334. codex/views/session.py +74 -102
  335. codex/views/settings.py +34 -0
  336. codex/views/template.py +3 -3
  337. codex/views/util.py +31 -0
  338. codex/views/utils.py +26 -0
  339. codex/views/version.py +2 -2
  340. {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/METADATA +12 -8
  341. {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/RECORD +344 -330
  342. codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js +0 -1
  343. codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js.br +0 -0
  344. codex/static_root/assets/VCheckbox-BF_99tgB.7fe59e56d9d4.js.gz +0 -0
  345. codex/static_root/assets/VCheckbox-BF_99tgB.js +0 -1
  346. codex/static_root/assets/VCheckbox-BF_99tgB.js.br +0 -0
  347. codex/static_root/assets/VCheckbox-BF_99tgB.js.gz +0 -0
  348. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js +0 -1
  349. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js.br +0 -0
  350. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.31e1e6aa44c9.js.gz +0 -0
  351. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js +0 -1
  352. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js.br +0 -0
  353. codex/static_root/assets/VCheckboxBtn-Coc7g8m7.js.gz +0 -0
  354. codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js +0 -1
  355. codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js.br +0 -0
  356. codex/static_root/assets/VCombobox-CTRWnOuM.0f00903c2ebc.js.gz +0 -0
  357. codex/static_root/assets/VCombobox-CTRWnOuM.js +0 -1
  358. codex/static_root/assets/VCombobox-CTRWnOuM.js.br +0 -0
  359. codex/static_root/assets/VCombobox-CTRWnOuM.js.gz +0 -0
  360. codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js +0 -1
  361. codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js.br +0 -0
  362. codex/static_root/assets/VDialog-pzwzInEh.51acf566a069.js.gz +0 -0
  363. codex/static_root/assets/VDialog-pzwzInEh.js +0 -1
  364. codex/static_root/assets/VDialog-pzwzInEh.js.br +0 -0
  365. codex/static_root/assets/VDialog-pzwzInEh.js.gz +0 -0
  366. codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js +0 -1
  367. codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js.br +0 -0
  368. codex/static_root/assets/VExpansionPanels-CKSq1GpG.0ecf702c5b67.js.gz +0 -0
  369. codex/static_root/assets/VExpansionPanels-CKSq1GpG.js +0 -1
  370. codex/static_root/assets/VExpansionPanels-CKSq1GpG.js.br +0 -0
  371. codex/static_root/assets/VExpansionPanels-CKSq1GpG.js.gz +0 -0
  372. codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js +0 -1
  373. codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js.br +0 -0
  374. codex/static_root/assets/VRadioGroup-CW5GobGU.b72925bca3de.js.gz +0 -0
  375. codex/static_root/assets/VRadioGroup-CW5GobGU.js +0 -1
  376. codex/static_root/assets/VRadioGroup-CW5GobGU.js.br +0 -0
  377. codex/static_root/assets/VRadioGroup-CW5GobGU.js.gz +0 -0
  378. codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js +0 -1
  379. codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js.br +0 -0
  380. codex/static_root/assets/VSelect-ChOlLRyH.00b11751b25e.js.gz +0 -0
  381. codex/static_root/assets/VSelect-ChOlLRyH.js +0 -1
  382. codex/static_root/assets/VSelect-ChOlLRyH.js.br +0 -0
  383. codex/static_root/assets/VSelect-ChOlLRyH.js.gz +0 -0
  384. codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js +0 -1
  385. codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js.br +0 -0
  386. codex/static_root/assets/VSelectionControl-DKOhc2zB.061dca094303.js.gz +0 -0
  387. codex/static_root/assets/VSelectionControl-DKOhc2zB.js +0 -1
  388. codex/static_root/assets/VSelectionControl-DKOhc2zB.js.br +0 -0
  389. codex/static_root/assets/VSelectionControl-DKOhc2zB.js.gz +0 -0
  390. codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js +0 -1
  391. codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js.br +0 -0
  392. codex/static_root/assets/VSlideGroup-zFz5_byd.e0e246b7acbb.js.gz +0 -0
  393. codex/static_root/assets/VSlideGroup-zFz5_byd.js +0 -1
  394. codex/static_root/assets/VSlideGroup-zFz5_byd.js.br +0 -0
  395. codex/static_root/assets/VSlideGroup-zFz5_byd.js.gz +0 -0
  396. codex/static_root/assets/VTable-FKMTcdkF.6c1eed84d25e.js.br +0 -0
  397. codex/static_root/assets/VTable-FKMTcdkF.6c1eed84d25e.js.gz +0 -0
  398. codex/static_root/assets/VTable-FKMTcdkF.js.br +0 -0
  399. codex/static_root/assets/VTable-FKMTcdkF.js.gz +0 -0
  400. codex/static_root/assets/VTextField-Dftcik85.cb2f1dda87e1.js.br +0 -0
  401. codex/static_root/assets/VTextField-Dftcik85.cb2f1dda87e1.js.gz +0 -0
  402. codex/static_root/assets/VTextField-Dftcik85.js.br +0 -0
  403. codex/static_root/assets/VTextField-Dftcik85.js.gz +0 -0
  404. codex/static_root/assets/VWindowItem-CYCwnt3s.28025dc36c6a.js.br +0 -0
  405. codex/static_root/assets/VWindowItem-CYCwnt3s.28025dc36c6a.js.gz +0 -0
  406. codex/static_root/assets/VWindowItem-CYCwnt3s.js.br +0 -0
  407. codex/static_root/assets/VWindowItem-CYCwnt3s.js.gz +0 -0
  408. codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js +0 -1
  409. codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js.br +0 -0
  410. codex/static_root/assets/admin-DH_DrB0F.dc45fb726e07.js.gz +0 -0
  411. codex/static_root/assets/admin-DH_DrB0F.js +0 -1
  412. codex/static_root/assets/admin-DH_DrB0F.js.br +0 -0
  413. codex/static_root/assets/admin-DH_DrB0F.js.gz +0 -0
  414. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js +0 -30
  415. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js.br +0 -0
  416. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.a9e72fbfdadc.js.gz +0 -0
  417. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js +0 -30
  418. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js.br +0 -0
  419. codex/static_root/assets/admin-drawer-panel-B2rmpAVA.js.gz +0 -0
  420. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.br +0 -0
  421. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css.br +0 -0
  422. codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css +0 -1
  423. codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css.br +0 -0
  424. codex/static_root/assets/browser-CpppBvim.a1b5d4fe97b1.css.gz +0 -0
  425. codex/static_root/assets/browser-CpppBvim.css +0 -1
  426. codex/static_root/assets/browser-CpppBvim.css.br +0 -0
  427. codex/static_root/assets/browser-CpppBvim.css.gz +0 -0
  428. codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js +0 -1
  429. codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js.br +0 -0
  430. codex/static_root/assets/browser-lKU7rqLd.cdb86e625ed9.js.gz +0 -0
  431. codex/static_root/assets/browser-lKU7rqLd.js +0 -1
  432. codex/static_root/assets/browser-lKU7rqLd.js.br +0 -0
  433. codex/static_root/assets/browser-lKU7rqLd.js.gz +0 -0
  434. codex/static_root/assets/change-password-dialog-BIBG6Bk8.3583021c124a.js.br +0 -0
  435. codex/static_root/assets/change-password-dialog-BIBG6Bk8.3583021c124a.js.gz +0 -0
  436. codex/static_root/assets/change-password-dialog-BIBG6Bk8.js.br +0 -0
  437. codex/static_root/assets/change-password-dialog-BIBG6Bk8.js.gz +0 -0
  438. codex/static_root/assets/confirm-dialog-DARLmXRf.53fc6938fb8c.js.br +0 -0
  439. codex/static_root/assets/confirm-dialog-DARLmXRf.53fc6938fb8c.js.gz +0 -0
  440. codex/static_root/assets/confirm-dialog-DARLmXRf.js.br +0 -0
  441. codex/static_root/assets/confirm-dialog-DARLmXRf.js.gz +0 -0
  442. codex/static_root/assets/datetime-column-LpLgWpkf.16ce3324970f.js.br +0 -0
  443. codex/static_root/assets/datetime-column-LpLgWpkf.16ce3324970f.js.gz +0 -0
  444. codex/static_root/assets/datetime-column-LpLgWpkf.js.br +0 -0
  445. codex/static_root/assets/datetime-column-LpLgWpkf.js.gz +0 -0
  446. codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js +0 -1
  447. codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js.br +0 -0
  448. codex/static_root/assets/filter-jRB1Ob6r.6a559a53e805.js.gz +0 -0
  449. codex/static_root/assets/filter-jRB1Ob6r.js +0 -1
  450. codex/static_root/assets/filter-jRB1Ob6r.js.br +0 -0
  451. codex/static_root/assets/filter-jRB1Ob6r.js.gz +0 -0
  452. codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js +0 -1
  453. codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js.br +0 -0
  454. codex/static_root/assets/flag-tab-mYS2wg5e.17829c46cae9.js.gz +0 -0
  455. codex/static_root/assets/flag-tab-mYS2wg5e.js +0 -1
  456. codex/static_root/assets/flag-tab-mYS2wg5e.js.br +0 -0
  457. codex/static_root/assets/flag-tab-mYS2wg5e.js.gz +0 -0
  458. codex/static_root/assets/group-tab-MK1lcjUU.98406d5babdc.js.br +0 -0
  459. codex/static_root/assets/group-tab-MK1lcjUU.98406d5babdc.js.gz +0 -0
  460. codex/static_root/assets/group-tab-MK1lcjUU.js.br +0 -0
  461. codex/static_root/assets/group-tab-MK1lcjUU.js.gz +0 -0
  462. codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js +0 -1
  463. codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js.br +0 -0
  464. codex/static_root/assets/http-error-HFeYnF0n.a3c578a0265e.js.gz +0 -0
  465. codex/static_root/assets/http-error-HFeYnF0n.js +0 -1
  466. codex/static_root/assets/http-error-HFeYnF0n.js.br +0 -0
  467. codex/static_root/assets/http-error-HFeYnF0n.js.gz +0 -0
  468. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +0 -1
  469. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
  470. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
  471. codex/static_root/assets/index-DUjeNg4x.js +0 -1
  472. codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
  473. codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
  474. codex/static_root/assets/library-tab-DYeEBYlT.a2a2ef088d1c.js.br +0 -0
  475. codex/static_root/assets/library-tab-DYeEBYlT.a2a2ef088d1c.js.gz +0 -0
  476. codex/static_root/assets/library-tab-DYeEBYlT.js.br +0 -0
  477. codex/static_root/assets/library-tab-DYeEBYlT.js.gz +0 -0
  478. codex/static_root/assets/main-gSW7TFDY.329223b148d4.js +0 -32
  479. codex/static_root/assets/main-gSW7TFDY.329223b148d4.js.br +0 -0
  480. codex/static_root/assets/main-gSW7TFDY.329223b148d4.js.gz +0 -0
  481. codex/static_root/assets/main-gSW7TFDY.js +0 -32
  482. codex/static_root/assets/main-gSW7TFDY.js.br +0 -0
  483. codex/static_root/assets/main-gSW7TFDY.js.gz +0 -0
  484. codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
  485. codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
  486. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.br +0 -0
  487. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.gz +0 -0
  488. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.br +0 -0
  489. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.gz +0 -0
  490. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js +0 -1
  491. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js.br +0 -0
  492. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.8f23f8bd9a05.js.gz +0 -0
  493. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js +0 -1
  494. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js.br +0 -0
  495. codex/static_root/assets/pagination-toolbar-Dlm70Bhs.js.gz +0 -0
  496. codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js +0 -35
  497. codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js.br +0 -0
  498. codex/static_root/assets/pdf-doc-BTdS9hUz.7c966524280a.js.gz +0 -0
  499. codex/static_root/assets/pdf-doc-BTdS9hUz.js +0 -35
  500. codex/static_root/assets/pdf-doc-BTdS9hUz.js.br +0 -0
  501. codex/static_root/assets/pdf-doc-BTdS9hUz.js.gz +0 -0
  502. codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js +0 -2
  503. codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js.br +0 -0
  504. codex/static_root/assets/reader-BtSTIETq.f940f66c0e58.js.gz +0 -0
  505. codex/static_root/assets/reader-BtSTIETq.js +0 -2
  506. codex/static_root/assets/reader-BtSTIETq.js.br +0 -0
  507. codex/static_root/assets/reader-BtSTIETq.js.gz +0 -0
  508. codex/static_root/assets/reader-CF3xt6JG.19fad7f62450.css.br +0 -0
  509. codex/static_root/assets/reader-CF3xt6JG.19fad7f62450.css.gz +0 -0
  510. codex/static_root/assets/reader-CF3xt6JG.css.br +0 -0
  511. codex/static_root/assets/reader-CF3xt6JG.css.gz +0 -0
  512. codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js +0 -1
  513. codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js.br +0 -0
  514. codex/static_root/assets/relation-chips-C4Ob7ySi.95855a010f4a.js.gz +0 -0
  515. codex/static_root/assets/relation-chips-C4Ob7ySi.js +0 -1
  516. codex/static_root/assets/relation-chips-C4Ob7ySi.js.br +0 -0
  517. codex/static_root/assets/relation-chips-C4Ob7ySi.js.gz +0 -0
  518. codex/static_root/assets/settings-drawer-BJOfMtqA.6471a50fc05b.css.br +0 -0
  519. codex/static_root/assets/settings-drawer-BJOfMtqA.6471a50fc05b.css.gz +0 -0
  520. codex/static_root/assets/settings-drawer-BJOfMtqA.css.br +0 -0
  521. codex/static_root/assets/settings-drawer-BJOfMtqA.css.gz +0 -0
  522. codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js +0 -2
  523. codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js.br +0 -0
  524. codex/static_root/assets/settings-drawer-xSctoxZn.4ce8afcb879a.js.gz +0 -0
  525. codex/static_root/assets/settings-drawer-xSctoxZn.js +0 -2
  526. codex/static_root/assets/settings-drawer-xSctoxZn.js.br +0 -0
  527. codex/static_root/assets/settings-drawer-xSctoxZn.js.gz +0 -0
  528. codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js +0 -1
  529. codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js.br +0 -0
  530. codex/static_root/assets/stats-tab-C_uC_U7o.e6b014b6e8c6.js.gz +0 -0
  531. codex/static_root/assets/stats-tab-C_uC_U7o.js +0 -1
  532. codex/static_root/assets/stats-tab-C_uC_U7o.js.br +0 -0
  533. codex/static_root/assets/stats-tab-C_uC_U7o.js.gz +0 -0
  534. codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js +0 -1
  535. codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js.br +0 -0
  536. codex/static_root/assets/task-tab-VIpy1Ula.c07877250c88.js.gz +0 -0
  537. codex/static_root/assets/task-tab-VIpy1Ula.js +0 -1
  538. codex/static_root/assets/task-tab-VIpy1Ula.js.br +0 -0
  539. codex/static_root/assets/task-tab-VIpy1Ula.js.gz +0 -0
  540. codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +0 -1
  541. codex/static_root/assets/to-case-kUm8NPpW.js +0 -1
  542. codex/static_root/assets/unauthorized-kEuFL5br.475729603aeb.js +0 -1
  543. codex/static_root/assets/unauthorized-kEuFL5br.475729603aeb.js.br +0 -0
  544. codex/static_root/assets/unauthorized-kEuFL5br.475729603aeb.js.gz +0 -0
  545. codex/static_root/assets/unauthorized-kEuFL5br.js +0 -1
  546. codex/static_root/assets/unauthorized-kEuFL5br.js.br +0 -0
  547. codex/static_root/assets/unauthorized-kEuFL5br.js.gz +0 -0
  548. codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js +0 -1
  549. codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js.br +0 -0
  550. codex/static_root/assets/user-tab-CZNFTTEI.f3940a75daae.js.gz +0 -0
  551. codex/static_root/assets/user-tab-CZNFTTEI.js +0 -1
  552. codex/static_root/assets/user-tab-CZNFTTEI.js.br +0 -0
  553. codex/static_root/assets/user-tab-CZNFTTEI.js.gz +0 -0
  554. codex/static_root/js/choices.23fedac2b9ba.json +0 -1
  555. codex/static_root/js/choices.23fedac2b9ba.json.br +0 -0
  556. codex/static_root/js/choices.23fedac2b9ba.json.gz +0 -0
  557. codex/static_root/manifest.6ef112683dae.json +0 -642
  558. codex/static_root/manifest.6ef112683dae.json.br +0 -0
  559. codex/static_root/manifest.6ef112683dae.json.gz +0 -0
  560. codex/views/browser/browser_breadcrumbs.py +0 -175
  561. codex/views/browser/const.py +0 -3
  562. {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/LICENSE +0 -0
  563. {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/WHEEL +0 -0
  564. {codex-1.6.0a5.dist-info → codex-1.6.0a7.dist-info}/entry_points.txt +0 -0
codex/views/download.py CHANGED
@@ -1,24 +1,21 @@
1
1
  """Download a comic archive."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import ClassVar
5
4
 
6
5
  from django.http import FileResponse, Http404
7
6
  from drf_spectacular.types import OpenApiTypes
8
7
  from drf_spectacular.utils import extend_schema
9
- from rest_framework.views import APIView
10
8
 
11
9
  from codex.models import Comic
12
- from codex.views.auth import GroupACLMixin, IsAuthenticatedOrEnabledNonUsers
10
+ from codex.views.auth import AuthFilterAPIView
13
11
 
14
12
 
15
- class DownloadView(APIView, GroupACLMixin):
13
+ class DownloadView(AuthFilterAPIView):
16
14
  """Return the comic archive file as an attachment."""
17
15
 
18
- permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
19
16
  content_type = "application/vnd.comicbook+zip"
20
17
 
21
- AS_ATTACHMENT = True
18
+ _AS_ATTACHMENT = True
22
19
 
23
20
  @extend_schema(responses={(200, content_type): OpenApiTypes.BINARY})
24
21
  def get(self, *_args, **kwargs):
@@ -48,7 +45,7 @@ class DownloadView(APIView, GroupACLMixin):
48
45
  filename = comic.get_filename()
49
46
  return FileResponse(
50
47
  comic_file,
51
- as_attachment=self.AS_ATTACHMENT,
48
+ as_attachment=self._AS_ATTACHMENT,
52
49
  content_type=content_type,
53
50
  filename=filename,
54
51
  )
@@ -57,4 +54,4 @@ class DownloadView(APIView, GroupACLMixin):
57
54
  class FileView(DownloadView):
58
55
  """View a comic in the browser."""
59
56
 
60
- AS_ATTACHMENT = False
57
+ _AS_ATTACHMENT = False
codex/views/frontend.py CHANGED
@@ -6,12 +6,14 @@ from rest_framework.renderers import TemplateHTMLRenderer
6
6
  from rest_framework.response import Response
7
7
 
8
8
  from codex.serializers.route import RouteSerializer
9
- from codex.views.session import BrowserSessionViewBase
9
+ from codex.views.session import SessionView
10
10
 
11
11
 
12
- class IndexView(BrowserSessionViewBase):
12
+ class IndexView(SessionView):
13
13
  """The main app."""
14
14
 
15
+ SESSION_KEY = SessionView.BROWSER_SESSION_KEY
16
+
15
17
  renderer_classes: ClassVar[list] = [TemplateHTMLRenderer] # type: ignore
16
18
  template_name = "index.html"
17
19
 
codex/views/mtime.py ADDED
@@ -0,0 +1,70 @@
1
+ """Get the mtimes for the submitted groups."""
2
+
3
+ from django.db.models import Max
4
+ from rest_framework.response import Response
5
+
6
+ from codex.models.groups import Publisher
7
+ from codex.serializers.mtime import GroupsMtimeSerializer, MtimeSerializer
8
+ from codex.util import max_none
9
+ from codex.views.auth import AuthGenericAPIView
10
+ from codex.views.browser.filters.bookmark import BookmarkFilterMixin
11
+ from codex.views.const import GROUP_MODEL_MAP
12
+ from codex.views.utils import reparse_json_query_params
13
+
14
+ _REPARSE_JSON_FIELDS = frozenset({"groups"})
15
+
16
+
17
+ class MtimeView(AuthGenericAPIView, BookmarkFilterMixin):
18
+ """Get the mtimes for the submitted groups."""
19
+
20
+ serializer_class = GroupsMtimeSerializer
21
+ response_serializer_class = MtimeSerializer
22
+
23
+ def _get_group_mtime(self, group, pks, use_bookmark_filter):
24
+ """Get one group's mtimes."""
25
+ model = GROUP_MODEL_MAP[group]
26
+ if not model:
27
+ model = Publisher
28
+
29
+ qs = model.objects
30
+
31
+ if pks and 0 not in pks:
32
+ qs = qs.filter(pk__in=pks)
33
+ updated_at_max = qs.aggregate(max=Max("updated_at"))["max"]
34
+
35
+ if use_bookmark_filter:
36
+ bm_rel = self.get_bm_rel(model)
37
+ bm_filter = self.get_my_bookmark_filter(bm_rel)
38
+ qs = qs.filter(bm_filter)
39
+ # TODO can these be combined into one max?
40
+ bookmark_updated_at_max = qs.aggregate(max=(Max(f"{bm_rel}__updated_at")))[
41
+ "max"
42
+ ]
43
+ updated_at_max = max_none(updated_at_max, bookmark_updated_at_max)
44
+
45
+ return updated_at_max
46
+
47
+ def get_max_groups_mtime(self, groups, use_bookmark_filter):
48
+ """Get max mtime for all groups."""
49
+ max_mtime = None
50
+
51
+ for item in groups:
52
+ group = item["group"]
53
+ pks = tuple(int(pk) for pk in item["pks"].split(","))
54
+ mtime = self._get_group_mtime(group, pks, use_bookmark_filter)
55
+ max_mtime = max_none(max_mtime, mtime)
56
+ return max_mtime
57
+
58
+ def get(self, *args, **kwargs):
59
+ """Get the mtimes for the submitted groups."""
60
+ # Parse Request
61
+ params = reparse_json_query_params(self.request.GET, _REPARSE_JSON_FIELDS)
62
+ groups = params.get("groups", "")
63
+ use_bookmark_filter = self.request.GET.get("use_bookmark_filter", False)
64
+
65
+ max_mtime = self.get_max_groups_mtime(groups, use_bookmark_filter)
66
+
67
+ # Serialize Response
68
+ result = {"max_mtime": max_mtime}
69
+ serializer = self.response_serializer_class(result)
70
+ return Response(serializer.data)
@@ -0,0 +1,17 @@
1
+ """OPDS Authentican mixin."""
2
+
3
+ from rest_framework.authentication import BasicAuthentication, SessionAuthentication
4
+
5
+ from codex.views.auth import IsAuthenticatedOrEnabledNonUsers
6
+ from codex.views.template import CodexXMLTemplateView
7
+
8
+
9
+ class OPDSAuthMixin:
10
+ """Add Basic Auth."""
11
+
12
+ authentication_classes = (SessionAuthentication, BasicAuthentication)
13
+ permission_classes = (IsAuthenticatedOrEnabledNonUsers,)
14
+
15
+
16
+ class OPDSTemplateView(OPDSAuthMixin, CodexXMLTemplateView):
17
+ """XML Template view with OPDSAuth."""
@@ -9,57 +9,58 @@ from rest_framework.response import Response
9
9
 
10
10
  from codex.serializers.opds.authentication import OPDSAuthentication1Serializer
11
11
 
12
+ _LOGO_SIZE = 512
13
+ _DOC = MappingProxyType(
14
+ {
15
+ "id": reverse_lazy("opds:authentication:v1"),
16
+ "title": "Codex",
17
+ "description": "Codex OPDS Syndication",
18
+ "links": [
19
+ {
20
+ "rel": "logo",
21
+ "href": staticfiles_storage.url("img/logo.svg"),
22
+ "type": "image/svg+xml",
23
+ "width": _LOGO_SIZE,
24
+ "height": _LOGO_SIZE,
25
+ },
26
+ {
27
+ "rel": "help",
28
+ "href": "https://github.com/ajslater/codex",
29
+ "type": "text/html",
30
+ },
31
+ {
32
+ "rel": "register",
33
+ "href": reverse_lazy("app:start"),
34
+ "type": "text/html",
35
+ },
36
+ ],
37
+ "authentication": [
38
+ {
39
+ "type": "http://opds-spec.org/auth/basic",
40
+ "labels": {"login": "Username", "password": "Password"},
41
+ },
42
+ {
43
+ # XXX Out of spec type
44
+ "type": "cookie",
45
+ "links": [
46
+ {
47
+ "rel": "authenticate",
48
+ "href": reverse_lazy("app:start"),
49
+ "type": "text/html",
50
+ }
51
+ ],
52
+ },
53
+ ],
54
+ }
55
+ )
56
+
12
57
 
13
58
  class OPDSAuthentication1View(GenericAPIView):
14
59
  """Authentication document."""
15
60
 
16
61
  serializer_class = OPDSAuthentication1Serializer
17
62
 
18
- DOC = MappingProxyType(
19
- {
20
- "id": reverse_lazy("opds:authentication:v1"),
21
- "title": "Codex",
22
- "description": "Codex OPDS Syndication",
23
- "links": [
24
- {
25
- "rel": "logo",
26
- "href": staticfiles_storage.url("img/logo.svg"),
27
- "type": "image/svg+xml",
28
- "width": 512,
29
- "height": 512,
30
- },
31
- {
32
- "rel": "help",
33
- "href": "https://github.com/ajslater/codex",
34
- "type": "text/html",
35
- },
36
- {
37
- "rel": "register",
38
- "href": reverse_lazy("app:start"),
39
- "type": "text/html",
40
- },
41
- ],
42
- "authentication": [
43
- {
44
- "type": "http://opds-spec.org/auth/basic",
45
- "labels": {"login": "Username", "password": "Password"},
46
- },
47
- {
48
- # XXX Out of spec type
49
- "type": "cookie",
50
- "links": [
51
- {
52
- "rel": "authenticate",
53
- "href": reverse_lazy("app:start"),
54
- "type": "text/html",
55
- }
56
- ],
57
- },
58
- ],
59
- }
60
- )
61
-
62
63
  def get(self, *args, **kwargs):
63
64
  """Fill in the authentication dict."""
64
- serializer = self.get_serializer(self.DOC)
65
+ serializer = self.get_serializer(_DOC)
65
66
  return Response(serializer.data)
@@ -1,21 +1,16 @@
1
1
  """Binary views with Basic Authentication added."""
2
2
 
3
- from rest_framework.authentication import BasicAuthentication, SessionAuthentication
4
-
5
3
  from codex.views.cover import CoverView
6
4
  from codex.views.download import DownloadView
5
+ from codex.views.opds.auth import OPDSAuthMixin
7
6
  from codex.views.reader.page import ReaderPageView
8
7
 
9
8
 
10
- class OPDSAuthMixin:
11
- """Add Basic Auth."""
12
-
13
- authentication_classes = (BasicAuthentication, SessionAuthentication)
14
-
15
-
16
- class OPDSCoverView(OPDSAuthMixin, CoverView):
9
+ class OPDSCoverView(CoverView):
17
10
  """Cover View with Basic Auth."""
18
11
 
12
+ # Panels sometimes doesn't send auth with cover requests.
13
+
19
14
 
20
15
  class OPDSDownloadView(OPDSAuthMixin, DownloadView):
21
16
  """Download View with Basic Auth."""
codex/views/opds/const.py CHANGED
@@ -3,6 +3,16 @@
3
3
  from collections.abc import Mapping
4
4
  from types import MappingProxyType
5
5
 
6
+ from codex.models import (
7
+ Character,
8
+ Genre,
9
+ Location,
10
+ SeriesGroup,
11
+ StoryArc,
12
+ Tag,
13
+ Team,
14
+ )
15
+
6
16
  BLANK_TITLE = "Unknown"
7
17
  AUTHOR_ROLES = {"Writer"}
8
18
 
@@ -71,3 +81,6 @@ class MimeType:
71
81
  }
72
82
  )
73
83
  OCTET = "application/octet-stream"
84
+
85
+
86
+ OPDS_M2M_MODELS = (Character, Genre, Location, SeriesGroup, StoryArc, Tag, Team)
codex/views/opds/urls.py CHANGED
@@ -1,16 +1,16 @@
1
1
  """OPDS URLs."""
2
2
 
3
3
  from django.urls import reverse
4
- from rest_framework.generics import GenericAPIView
5
4
  from rest_framework.response import Response
6
5
 
7
6
  from codex.serializers.choices import DEFAULTS
8
7
  from codex.serializers.opds.urls import OPDSURLsSerializer
8
+ from codex.views.auth import AuthGenericAPIView
9
9
 
10
- OPDS_VERSIONS = (1, 2)
10
+ _OPDS_VERSIONS = (1, 2)
11
11
 
12
12
 
13
- class OPDSURLsView(GenericAPIView):
13
+ class OPDSURLsView(AuthGenericAPIView):
14
14
  """OPDS URLs."""
15
15
 
16
16
  serializer_class = OPDSURLsSerializer
@@ -19,7 +19,8 @@ class OPDSURLsView(GenericAPIView):
19
19
  """Resolve the urls."""
20
20
  obj = {}
21
21
  route = DEFAULTS["breadcrumbs"][0]
22
- for version in OPDS_VERSIONS:
22
+ route.pop("name", None)
23
+ for version in _OPDS_VERSIONS:
23
24
  key = f"v{version}"
24
25
  name = f"opds:v{version}:feed"
25
26
  value = reverse(name, kwargs=route)
codex/views/opds/util.py CHANGED
@@ -6,19 +6,12 @@ from django.urls import reverse
6
6
  from django.utils.http import urlencode
7
7
 
8
8
  from codex.models import (
9
- Character,
10
9
  Contributor,
11
10
  ContributorPerson,
12
- Genre,
13
- Location,
14
- SeriesGroup,
15
11
  StoryArc,
16
- Tag,
17
- Team,
18
12
  )
19
13
  from codex.serializers.choices import DEFAULTS
20
-
21
- OPDS_M2M_MODELS = (Character, Genre, Location, SeriesGroup, StoryArc, Tag, Team)
14
+ from codex.views.opds.const import OPDS_M2M_MODELS
22
15
 
23
16
 
24
17
  def update_href_query_params(href, old_query_params, new_query_params=None):
@@ -81,6 +74,7 @@ def full_redirect_view(url_name):
81
74
  def func(request):
82
75
  """Redirect to view, forwarding query strings and auth."""
83
76
  kwargs = DEFAULTS["breadcrumbs"][0]
77
+ kwargs.pop("name", None)
84
78
  url = reverse(url_name, kwargs=kwargs)
85
79
 
86
80
  # Forward the query string.
@@ -77,7 +77,7 @@ DEFAULT_FACETS = {
77
77
  }
78
78
 
79
79
 
80
- class FacetsMixin(BrowserView):
80
+ class OPDS1FacetsView(BrowserView):
81
81
  """OPDS 1 Facets methods."""
82
82
 
83
83
  OPDS = 1
@@ -89,15 +89,14 @@ class FacetsMixin(BrowserView):
89
89
  obj = MappingProxyType({})
90
90
 
91
91
  def _facet(self, kwargs, facet_group, facet_title, new_query_params):
92
+ kwargs.pop("name", None)
92
93
  href = reverse("opds:v1:feed", kwargs=kwargs)
93
94
  facet_active = False
94
95
  for key, val in new_query_params.items():
95
- if self.request.query_params.get(key) == val:
96
+ if self.request.GET.get(key) == val:
96
97
  facet_active = True
97
98
  break
98
- href = update_href_query_params(
99
- href, self.request.query_params, new_query_params
100
- )
99
+ href = update_href_query_params(href, self.request.GET, new_query_params)
101
100
 
102
101
  title = " ".join(filter(None, (facet_group.title_prefix, facet_title))).strip()
103
102
  return OPDS1Link(
@@ -118,7 +117,7 @@ class FacetsMixin(BrowserView):
118
117
  ids=item.get("pks"),
119
118
  name=name,
120
119
  )
121
- qps = {**self.request.query_params}
120
+ qps = {**self.request.GET}
122
121
  qps.update(query_params)
123
122
  zero_pad = self.obj["zero_pad"]
124
123
  data = OPDS1EntryData(
@@ -131,7 +130,7 @@ class FacetsMixin(BrowserView):
131
130
  default_val = DEFAULT_FACETS.get(facet_group.query_param)
132
131
  if facet.value == default_val:
133
132
  compare += [None]
134
- return self.request.query_params.get(facet_group.query_param) in compare
133
+ return self.request.GET.get(facet_group.query_param) in compare
135
134
 
136
135
  @staticmethod
137
136
  def _did_special_group_change(group, facet_group):
@@ -1,12 +1,10 @@
1
1
  """OPDS v1 feed."""
2
2
 
3
- from datetime import datetime, timezone
4
3
  from types import MappingProxyType
5
4
  from typing import TYPE_CHECKING, Any
6
5
 
7
6
  from comicbox.box import Comicbox
8
7
  from drf_spectacular.utils import extend_schema
9
- from rest_framework.authentication import BasicAuthentication, SessionAuthentication
10
8
  from rest_framework.response import Response
11
9
  from rest_framework.throttling import ScopedRateThrottle
12
10
 
@@ -17,17 +15,16 @@ from codex.serializers.opds.v1 import (
17
15
  OPDS1TemplateSerializer,
18
16
  )
19
17
  from codex.views.browser.browser import BrowserView
20
- from codex.views.browser.const import MAX_OBJ_PER_PAGE
21
- from codex.views.const import FALSY
18
+ from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
19
+ from codex.views.opds.auth import OPDSTemplateView
22
20
  from codex.views.opds.const import BLANK_TITLE, MimeType
23
21
  from codex.views.opds.v1.entry.data import OPDS1EntryData
24
22
  from codex.views.opds.v1.entry.entry import OPDS1Entry
25
23
  from codex.views.opds.v1.links import (
26
- LinksMixin,
24
+ OPDS1LinksView,
27
25
  RootTopLinks,
28
26
  TopLinks,
29
27
  )
30
- from codex.views.template import CodexXMLTemplateView
31
28
 
32
29
  if TYPE_CHECKING:
33
30
  from collections.abc import Mapping
@@ -54,10 +51,9 @@ class UserAgentPrefixes:
54
51
  # "Panels", "Chunky"
55
52
 
56
53
 
57
- class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
54
+ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
58
55
  """OPDS 1 Feed."""
59
56
 
60
- authentication_classes = (SessionAuthentication, BasicAuthentication)
61
57
  template_name = "opds_v1/index.xml"
62
58
  serializer_class = OPDS1TemplateSerializer
63
59
  throttle_classes = (ScopedRateThrottle,)
@@ -90,7 +86,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
90
86
  """Create the feed title."""
91
87
  result = ""
92
88
  try:
93
- browser_title: Mapping[str, Any] = self.obj.get("browser_title") # type: ignore
89
+ browser_title: Mapping[str, Any] = self.obj.get("title") # type: ignore
94
90
  if browser_title:
95
91
  parent_name = browser_title.get("parent_name", "All")
96
92
  pks = self.kwargs["pks"]
@@ -107,10 +103,9 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
107
103
 
108
104
  @property
109
105
  def updated(self):
110
- """Hack in feed update time from cover timestamp."""
106
+ """Use mtime for updated."""
111
107
  try:
112
- if ts := self.obj.get("covers_timestamp"):
113
- return datetime.fromtimestamp(ts, tz=timezone.utc) # type: ignore
108
+ return self.obj.get("mtime", "")
114
109
  except Exception:
115
110
  LOG.exception("Getting OPDS v1 updated")
116
111
 
@@ -145,7 +140,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
145
140
  entries += [
146
141
  OPDS1Entry(
147
142
  obj,
148
- self.request.query_params,
143
+ self.request.GET,
149
144
  data,
150
145
  title_filename_fallback=fallback,
151
146
  )
@@ -165,9 +160,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
165
160
  entries += self.facets(entries=True, root=at_root)
166
161
 
167
162
  entries += self._get_entries_section("groups", False)
168
- metadata = (
169
- self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
170
- )
163
+ metadata = self.request.GET.get("opdsMetadata", "").lower() not in FALSY
171
164
  entries += self._get_entries_section("books", metadata)
172
165
  except Exception:
173
166
  LOG.exception("Getting OPDS v1 entries")
@@ -232,7 +225,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
232
225
  self.acquisition_groups = frozenset({*self.valid_nav_groups[-2:]} | {"c"})
233
226
  self.is_opds_1_acquisition = group in self.acquisition_groups
234
227
  self.is_opds_metadata = (
235
- self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
228
+ self.request.GET.get("opdsMetadata", "").lower() not in FALSY
236
229
  )
237
230
 
238
231
  def init_request(self):
@@ -14,7 +14,7 @@ from codex.views.opds.util import update_href_query_params
14
14
  from codex.views.opds.v1.data import OPDS1Link
15
15
  from codex.views.opds.v1.entry.data import OPDS1EntryData, OPDS1EntryObject
16
16
  from codex.views.opds.v1.entry.entry import OPDS1Entry
17
- from codex.views.opds.v1.facets import FacetsMixin
17
+ from codex.views.opds.v1.facets import OPDS1FacetsView
18
18
 
19
19
  LOG = get_logger(__name__)
20
20
 
@@ -101,13 +101,13 @@ class RootTopLinks:
101
101
  ALL = (NEW, FEATURED, LAST_READ)
102
102
 
103
103
 
104
- class LinksMixin(FacetsMixin):
104
+ class OPDS1LinksView(OPDS1FacetsView):
105
105
  """OPDS 1 Links methods."""
106
106
 
107
107
  # overwritten in get_object()
108
108
  DEFAULT_ROUTE = MappingProxyType(
109
109
  {
110
- **FacetsMixin.DEFAULT_ROUTE,
110
+ **OPDS1FacetsView.DEFAULT_ROUTE,
111
111
  "name": "opds:v1:feed",
112
112
  }
113
113
  )
@@ -120,7 +120,7 @@ class LinksMixin(FacetsMixin):
120
120
  return False
121
121
 
122
122
  for key, value in top_link.query_params.items():
123
- if str(self.request.query_params.get(key)) != str(value):
123
+ if str(self.request.GET.get(key)) != str(value):
124
124
  return False
125
125
 
126
126
  return True
@@ -128,7 +128,8 @@ class LinksMixin(FacetsMixin):
128
128
  def _link(self, kwargs, rel, query_params=None, mime_type=MimeType.NAV):
129
129
  """Create a link."""
130
130
  if query_params is None:
131
- query_params = self.request.query_params
131
+ query_params = self.request.GET
132
+ kwargs.pop("name", None)
132
133
  href = reverse("opds:v1:feed", kwargs=kwargs)
133
134
  href = update_href_query_params(href, query_params)
134
135
  return OPDS1Link(rel, href, mime_type)
@@ -2,19 +2,15 @@
2
2
 
3
3
  from drf_spectacular.types import OpenApiTypes
4
4
  from drf_spectacular.utils import extend_schema
5
- from rest_framework.authentication import BasicAuthentication, SessionAuthentication
6
5
  from rest_framework.throttling import ScopedRateThrottle
7
6
 
8
- from codex.views.auth import IsAuthenticatedOrEnabledNonUsers
9
- from codex.views.template import CodexXMLTemplateView
7
+ from codex.views.opds.auth import OPDSTemplateView
10
8
 
11
9
 
12
10
  @extend_schema(responses={("200", "application/xml"): OpenApiTypes.BYTE})
13
- class OpenSearch1View(CodexXMLTemplateView):
11
+ class OpenSearch1View(OPDSTemplateView):
14
12
  """OpenSearchView."""
15
13
 
16
- authentication_classes = (SessionAuthentication, BasicAuthentication)
17
- permission_classes = (IsAuthenticatedOrEnabledNonUsers,)
18
14
  template_name = "opds_v1/opensearch_v1.xml"
19
15
  content_type = "application/xml"
20
16
  throttle_classes = (ScopedRateThrottle,)
@@ -3,15 +3,14 @@
3
3
  from types import MappingProxyType
4
4
 
5
5
  from drf_spectacular.utils import extend_schema
6
- from rest_framework.authentication import BasicAuthentication, SessionAuthentication
7
6
  from rest_framework.response import Response
8
7
 
9
8
  from codex.logger.logging import get_logger
10
9
  from codex.models import AdminFlag
11
10
  from codex.serializers.opds.v2 import OPDS2FeedSerializer
12
11
  from codex.views.browser.browser import BrowserView
13
- from codex.views.browser.const import MAX_OBJ_PER_PAGE
14
- from codex.views.const import FALSY
12
+ from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
13
+ from codex.views.opds.auth import OPDSAuthMixin
15
14
  from codex.views.opds.const import BLANK_TITLE
16
15
  from codex.views.opds.v2.const import (
17
16
  FACETS,
@@ -22,22 +21,20 @@ from codex.views.opds.v2.const import (
22
21
  NavigationGroup,
23
22
  )
24
23
  from codex.views.opds.v2.links import HrefData, LinkData
25
- from codex.views.opds.v2.publications import PublicationMixin
26
- from codex.views.opds.v2.top_links import TopLinksMixin
24
+ from codex.views.opds.v2.publications import OPDS2PublicationView
27
25
 
28
26
  LOG = get_logger(__name__)
29
27
 
30
28
 
31
- class OPDS2FeedView(PublicationMixin, TopLinksMixin):
29
+ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
32
30
  """OPDS 2.0 Feed."""
33
31
 
34
32
  DEFAULT_ROUTE = MappingProxyType(
35
- {**TopLinksMixin.DEFAULT_ROUTE, "name": "opds:v2:feed"}
33
+ {**OPDS2PublicationView.DEFAULT_ROUTE, "name": "opds:v2:feed"}
36
34
  )
37
35
  TARGET = "opds2"
38
36
  throttle_scope = "opds"
39
37
 
40
- authentication_classes = (SessionAuthentication, BasicAuthentication)
41
38
  serializer_class = OPDS2FeedSerializer
42
39
 
43
40
  def _title(self, browser_title):
@@ -55,11 +52,11 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
55
52
  result = BLANK_TITLE
56
53
  return result
57
54
 
58
- def _detect_user_agent(self):
59
- """Hacks for individual clients."""
60
- user_agent = self.request.headers.get("User-Agent")
61
- if not user_agent:
62
- return
55
+ # def _detect_user_agent(self):
56
+ # """Hacks for individual clients."""
57
+ # user_agent = self.request.headers.get("User-Agent")
58
+ # if not user_agent:
59
+ # return
63
60
 
64
61
  @staticmethod
65
62
  def _is_allowed(link_spec):
@@ -187,7 +184,7 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
187
184
  self.num_pages = browser_page["num_pages"]
188
185
 
189
186
  # opds page
190
- title = self._title(browser_page.get("browser_title"))
187
+ title = self._title(browser_page.get("title"))
191
188
  number_of_items = browser_page["total_count"]
192
189
  current_page = self.kwargs.get("page")
193
190
  up_route = self.get_last_route()
@@ -224,7 +221,7 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
224
221
  acquisition_groups = frozenset(self.valid_nav_groups[-2:])
225
222
  self.is_opds_2_acquisition = group in acquisition_groups
226
223
  self.is_opds_metadata = (
227
- self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
224
+ self.request.GET.get("opdsMetadata", "").lower() not in FALSY
228
225
  )
229
226
 
230
227
  def init_request(self):
@@ -39,7 +39,7 @@ class LinkData:
39
39
  num_items: int | None = None
40
40
 
41
41
 
42
- class LinksMixin(BrowserView):
42
+ class OPDS2LinksView(BrowserView):
43
43
  """Links methods for OPDS 2.0 Feed."""
44
44
 
45
45
  num_pages = 0 # For pyright. Overwritten every run.
@@ -58,7 +58,7 @@ class LinksMixin(BrowserView):
58
58
  elif hasattr(self, "request"):
59
59
  # if request link and not init static links
60
60
  href = update_href_query_params(
61
- href, self.request.query_params, new_query_params=data.query_params
61
+ href, self.request.GET, new_query_params=data.query_params
62
62
  )
63
63
  return href
64
64
 
@@ -69,6 +69,7 @@ class LinksMixin(BrowserView):
69
69
  if "page" in kwargs and not self._href_page_validate(kwargs, data):
70
70
  return None
71
71
 
72
+ kwargs.pop("name", None)
72
73
  href = reverse(url_name, kwargs=kwargs)
73
74
  return self._href_update_query_params(href, data)
74
75