codex 1.6.0a6__py3-none-any.whl → 1.6.0a8__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 (612) hide show
  1. codex/exceptions.py +1 -0
  2. codex/integrity.py +4 -4
  3. codex/librarian/importer/{aggregate_metadata.py → aggregate.py} +56 -86
  4. codex/librarian/importer/cache.py +180 -0
  5. codex/librarian/importer/const.py +15 -6
  6. codex/librarian/importer/create_comics.py +102 -17
  7. codex/librarian/importer/create_covers.py +98 -0
  8. codex/librarian/importer/create_fks.py +73 -170
  9. codex/librarian/importer/deleted.py +59 -46
  10. codex/librarian/importer/{clean_metadata.py → extract.py} +33 -4
  11. codex/librarian/importer/failed_imports.py +41 -62
  12. codex/librarian/importer/importer.py +96 -0
  13. codex/librarian/importer/importerd.py +71 -395
  14. codex/librarian/importer/init.py +265 -0
  15. codex/librarian/importer/link_comics.py +26 -81
  16. codex/librarian/importer/link_covers.py +73 -0
  17. codex/librarian/importer/moved.py +72 -96
  18. codex/librarian/importer/query_covers.py +52 -0
  19. codex/librarian/importer/query_fks.py +128 -172
  20. codex/librarian/importer/status.py +2 -39
  21. codex/librarian/importer/tasks.py +22 -13
  22. codex/librarian/janitor/cleanup.py +2 -2
  23. codex/librarian/janitor/janitor.py +11 -1
  24. codex/librarian/librariand.py +8 -2
  25. codex/migrations/0027_import_order_and_covers.py +345 -0
  26. codex/models/comic.py +1 -2
  27. codex/models/groups.py +3 -2
  28. codex/models/paths.py +0 -1
  29. codex/permissions.py +1 -1
  30. codex/search/query.py +25 -2
  31. codex/search/writing.py +1 -1
  32. codex/serializers/admin.py +1 -2
  33. codex/serializers/browser/filters.py +8 -101
  34. codex/serializers/browser/mixins.py +2 -3
  35. codex/serializers/browser/page.py +2 -2
  36. codex/serializers/browser/settings.py +4 -3
  37. codex/serializers/choices.py +10 -1
  38. codex/serializers/fields.py +123 -0
  39. codex/serializers/mtime.py +25 -0
  40. codex/serializers/opds/v2.py +7 -11
  41. codex/serializers/opds/v2_unused.py +5 -3
  42. codex/serializers/reader.py +5 -2
  43. codex/serializers/route.py +7 -3
  44. codex/settings/settings.py +2 -2
  45. codex/static_root/assets/VCheckbox-DI-DgMb4.8eeb1bd3e18b.js +1 -0
  46. codex/static_root/assets/VCheckbox-DI-DgMb4.8eeb1bd3e18b.js.br +0 -0
  47. codex/static_root/assets/VCheckbox-DI-DgMb4.8eeb1bd3e18b.js.gz +0 -0
  48. codex/static_root/assets/VCheckbox-DI-DgMb4.js +1 -0
  49. codex/static_root/assets/VCheckbox-DI-DgMb4.js.br +0 -0
  50. codex/static_root/assets/VCheckbox-DI-DgMb4.js.gz +0 -0
  51. codex/static_root/assets/{VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js → VCheckboxBtn-B8Z4NA-t.3d8921f98ad9.js} +1 -1
  52. codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.3d8921f98ad9.js.br +0 -0
  53. codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.3d8921f98ad9.js.gz +0 -0
  54. codex/static_root/assets/{VCheckboxBtn-m4IbdjEe.js → VCheckboxBtn-B8Z4NA-t.js} +1 -1
  55. codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.js.br +0 -0
  56. codex/static_root/assets/VCheckboxBtn-B8Z4NA-t.js.gz +0 -0
  57. codex/static_root/assets/VCombobox-D1_0lCri.6ef85a29c2dc.js +1 -0
  58. codex/static_root/assets/VCombobox-D1_0lCri.6ef85a29c2dc.js.br +0 -0
  59. codex/static_root/assets/VCombobox-D1_0lCri.6ef85a29c2dc.js.gz +0 -0
  60. codex/static_root/assets/VCombobox-D1_0lCri.js +1 -0
  61. codex/static_root/assets/VCombobox-D1_0lCri.js.br +0 -0
  62. codex/static_root/assets/VCombobox-D1_0lCri.js.gz +0 -0
  63. codex/static_root/assets/VDialog-opmV0YZz.d77216e97599.js +1 -0
  64. codex/static_root/assets/VDialog-opmV0YZz.d77216e97599.js.br +0 -0
  65. codex/static_root/assets/VDialog-opmV0YZz.d77216e97599.js.gz +0 -0
  66. codex/static_root/assets/VDialog-opmV0YZz.js +1 -0
  67. codex/static_root/assets/VDialog-opmV0YZz.js.br +0 -0
  68. codex/static_root/assets/VDialog-opmV0YZz.js.gz +0 -0
  69. codex/static_root/assets/VExpansionPanels-DX2dhUf5.ecc2ebfa62ec.js +1 -0
  70. codex/static_root/assets/VExpansionPanels-DX2dhUf5.ecc2ebfa62ec.js.br +0 -0
  71. codex/static_root/assets/VExpansionPanels-DX2dhUf5.ecc2ebfa62ec.js.gz +0 -0
  72. codex/static_root/assets/VExpansionPanels-DX2dhUf5.js +1 -0
  73. codex/static_root/assets/VExpansionPanels-DX2dhUf5.js.br +0 -0
  74. codex/static_root/assets/VExpansionPanels-DX2dhUf5.js.gz +0 -0
  75. codex/static_root/assets/VRadioGroup-BGa6_q5a.d1a3ad7266bd.js +1 -0
  76. codex/static_root/assets/VRadioGroup-BGa6_q5a.d1a3ad7266bd.js.br +0 -0
  77. codex/static_root/assets/VRadioGroup-BGa6_q5a.d1a3ad7266bd.js.gz +0 -0
  78. codex/static_root/assets/VRadioGroup-BGa6_q5a.js +1 -0
  79. codex/static_root/assets/VRadioGroup-BGa6_q5a.js.br +0 -0
  80. codex/static_root/assets/VRadioGroup-BGa6_q5a.js.gz +0 -0
  81. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +1 -0
  82. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
  83. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
  84. codex/static_root/assets/VSelect-ARDhJiQK.css +1 -0
  85. codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
  86. codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
  87. codex/static_root/assets/VSelect-sIYW_-ud.7d40da0ec3ff.js +1 -0
  88. codex/static_root/assets/VSelect-sIYW_-ud.7d40da0ec3ff.js.br +0 -0
  89. codex/static_root/assets/VSelect-sIYW_-ud.7d40da0ec3ff.js.gz +0 -0
  90. codex/static_root/assets/VSelect-sIYW_-ud.js +1 -0
  91. codex/static_root/assets/VSelect-sIYW_-ud.js.br +0 -0
  92. codex/static_root/assets/VSelect-sIYW_-ud.js.gz +0 -0
  93. codex/static_root/assets/VSelectionControl-C8DP9rgp.ef80e8e3dfc1.js +1 -0
  94. codex/static_root/assets/VSelectionControl-C8DP9rgp.ef80e8e3dfc1.js.br +0 -0
  95. codex/static_root/assets/VSelectionControl-C8DP9rgp.ef80e8e3dfc1.js.gz +0 -0
  96. codex/static_root/assets/VSelectionControl-C8DP9rgp.js +1 -0
  97. codex/static_root/assets/VSelectionControl-C8DP9rgp.js.br +0 -0
  98. codex/static_root/assets/VSelectionControl-C8DP9rgp.js.gz +0 -0
  99. codex/static_root/assets/VSlideGroup-BRYhkLH8.3534ec94b2f1.js +1 -0
  100. codex/static_root/assets/VSlideGroup-BRYhkLH8.3534ec94b2f1.js.br +0 -0
  101. codex/static_root/assets/VSlideGroup-BRYhkLH8.3534ec94b2f1.js.gz +0 -0
  102. codex/static_root/assets/VSlideGroup-BRYhkLH8.js +1 -0
  103. codex/static_root/assets/VSlideGroup-BRYhkLH8.js.br +0 -0
  104. codex/static_root/assets/VSlideGroup-BRYhkLH8.js.gz +0 -0
  105. codex/static_root/assets/{VTable-C0gAriFq.fa6e4b60d4ef.js → VTable-D1esKtlw.52165930e3b9.js} +1 -1
  106. codex/static_root/assets/VTable-D1esKtlw.52165930e3b9.js.br +0 -0
  107. codex/static_root/assets/VTable-D1esKtlw.52165930e3b9.js.gz +0 -0
  108. codex/static_root/assets/{VTable-C0gAriFq.js → VTable-D1esKtlw.js} +1 -1
  109. codex/static_root/assets/VTable-D1esKtlw.js.br +0 -0
  110. codex/static_root/assets/VTable-D1esKtlw.js.gz +0 -0
  111. codex/static_root/assets/VTextField-BnGscKcY.54032adf3a4b.js +1 -0
  112. codex/static_root/assets/VTextField-BnGscKcY.54032adf3a4b.js.br +0 -0
  113. codex/static_root/assets/VTextField-BnGscKcY.54032adf3a4b.js.gz +0 -0
  114. codex/static_root/assets/VTextField-BnGscKcY.js +1 -0
  115. codex/static_root/assets/VTextField-BnGscKcY.js.br +0 -0
  116. codex/static_root/assets/VTextField-BnGscKcY.js.gz +0 -0
  117. codex/static_root/assets/{VWindowItem-CFhKCj49.b2458002a937.js → VWindowItem-B0KokcAk.ea94224aecb9.js} +1 -1
  118. codex/static_root/assets/VWindowItem-B0KokcAk.ea94224aecb9.js.br +0 -0
  119. codex/static_root/assets/VWindowItem-B0KokcAk.ea94224aecb9.js.gz +0 -0
  120. codex/static_root/assets/{VWindowItem-CFhKCj49.js → VWindowItem-B0KokcAk.js} +1 -1
  121. codex/static_root/assets/VWindowItem-B0KokcAk.js.br +0 -0
  122. codex/static_root/assets/VWindowItem-B0KokcAk.js.gz +0 -0
  123. codex/static_root/assets/admin-Y0bL21AB.42d4f0d68163.js +1 -0
  124. codex/static_root/assets/admin-Y0bL21AB.42d4f0d68163.js.br +0 -0
  125. codex/static_root/assets/admin-Y0bL21AB.42d4f0d68163.js.gz +0 -0
  126. codex/static_root/assets/admin-Y0bL21AB.js +1 -0
  127. codex/static_root/assets/admin-Y0bL21AB.js.br +0 -0
  128. codex/static_root/assets/admin-Y0bL21AB.js.gz +0 -0
  129. codex/static_root/assets/admin-drawer-panel-DNngWUEM.4e992e3fe86e.js +30 -0
  130. codex/static_root/assets/admin-drawer-panel-DNngWUEM.4e992e3fe86e.js.br +0 -0
  131. codex/static_root/assets/admin-drawer-panel-DNngWUEM.4e992e3fe86e.js.gz +0 -0
  132. codex/static_root/assets/admin-drawer-panel-DNngWUEM.js +30 -0
  133. codex/static_root/assets/admin-drawer-panel-DNngWUEM.js.br +0 -0
  134. codex/static_root/assets/admin-drawer-panel-DNngWUEM.js.gz +0 -0
  135. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css → admin-drawer-panel-o87BwD38.ce39427c8201.css} +1 -1
  136. codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.br +0 -0
  137. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.gz → admin-drawer-panel-o87BwD38.ce39427c8201.css.gz} +0 -0
  138. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.css → admin-drawer-panel-o87BwD38.css} +1 -1
  139. codex/static_root/assets/admin-drawer-panel-o87BwD38.css.br +0 -0
  140. codex/static_root/assets/{admin-drawer-panel-BHdNPwzF.css.gz → admin-drawer-panel-o87BwD38.css.gz} +0 -0
  141. codex/static_root/assets/browser-CMAtBCLx.17a277ce43a9.css +1 -0
  142. codex/static_root/assets/browser-CMAtBCLx.17a277ce43a9.css.br +0 -0
  143. codex/static_root/assets/browser-CMAtBCLx.17a277ce43a9.css.gz +0 -0
  144. codex/static_root/assets/browser-CMAtBCLx.css +1 -0
  145. codex/static_root/assets/browser-CMAtBCLx.css.br +0 -0
  146. codex/static_root/assets/browser-CMAtBCLx.css.gz +0 -0
  147. codex/static_root/assets/browser-DoJk8uvb.2c97a3858932.js +1 -0
  148. codex/static_root/assets/browser-DoJk8uvb.2c97a3858932.js.br +0 -0
  149. codex/static_root/assets/browser-DoJk8uvb.2c97a3858932.js.gz +0 -0
  150. codex/static_root/assets/browser-DoJk8uvb.js +1 -0
  151. codex/static_root/assets/browser-DoJk8uvb.js.br +0 -0
  152. codex/static_root/assets/browser-DoJk8uvb.js.gz +0 -0
  153. codex/static_root/assets/{change-password-dialog-JYM9aAR9.js → change-password-dialog-CH4xbjJa.b749aa6ea07e.js} +1 -1
  154. codex/static_root/assets/change-password-dialog-CH4xbjJa.b749aa6ea07e.js.br +0 -0
  155. codex/static_root/assets/change-password-dialog-CH4xbjJa.b749aa6ea07e.js.gz +0 -0
  156. codex/static_root/assets/{change-password-dialog-JYM9aAR9.f71d378f4fa6.js → change-password-dialog-CH4xbjJa.js} +1 -1
  157. codex/static_root/assets/change-password-dialog-CH4xbjJa.js.br +0 -0
  158. codex/static_root/assets/change-password-dialog-CH4xbjJa.js.gz +0 -0
  159. codex/static_root/assets/{confirm-dialog-ZAZ6P1Rc.515858dd21e8.js → confirm-dialog-DkU0twE6.a20006aa5ba6.js} +1 -1
  160. codex/static_root/assets/confirm-dialog-DkU0twE6.a20006aa5ba6.js.br +0 -0
  161. codex/static_root/assets/confirm-dialog-DkU0twE6.a20006aa5ba6.js.gz +0 -0
  162. codex/static_root/assets/{confirm-dialog-ZAZ6P1Rc.js → confirm-dialog-DkU0twE6.js} +1 -1
  163. codex/static_root/assets/confirm-dialog-DkU0twE6.js.br +0 -0
  164. codex/static_root/assets/confirm-dialog-DkU0twE6.js.gz +0 -0
  165. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +1 -0
  166. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
  167. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +1 -0
  168. codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
  169. codex/static_root/assets/datetime-column-BfW_j_3Q.6a614321a435.js +1 -0
  170. codex/static_root/assets/datetime-column-BfW_j_3Q.6a614321a435.js.br +0 -0
  171. codex/static_root/assets/datetime-column-BfW_j_3Q.6a614321a435.js.gz +0 -0
  172. codex/static_root/assets/datetime-column-BfW_j_3Q.js +1 -0
  173. codex/static_root/assets/datetime-column-BfW_j_3Q.js.br +0 -0
  174. codex/static_root/assets/datetime-column-BfW_j_3Q.js.gz +0 -0
  175. codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +1 -0
  176. codex/static_root/assets/datetime-column-DeCthByU.css +1 -0
  177. codex/static_root/assets/filter-DjxN78cK.35673f6e49b1.js +1 -0
  178. codex/static_root/assets/filter-DjxN78cK.35673f6e49b1.js.br +0 -0
  179. codex/static_root/assets/filter-DjxN78cK.35673f6e49b1.js.gz +0 -0
  180. codex/static_root/assets/filter-DjxN78cK.js +1 -0
  181. codex/static_root/assets/filter-DjxN78cK.js.br +0 -0
  182. codex/static_root/assets/filter-DjxN78cK.js.gz +0 -0
  183. codex/static_root/assets/{flag-tab-Ci_XcDT0.a5ea640a08d6.js → flag-tab-D4ZNSbMR.e763a9937701.js} +1 -1
  184. codex/static_root/assets/flag-tab-D4ZNSbMR.e763a9937701.js.br +0 -0
  185. codex/static_root/assets/flag-tab-D4ZNSbMR.e763a9937701.js.gz +0 -0
  186. codex/static_root/assets/{flag-tab-Ci_XcDT0.js → flag-tab-D4ZNSbMR.js} +1 -1
  187. codex/static_root/assets/flag-tab-D4ZNSbMR.js.br +0 -0
  188. codex/static_root/assets/flag-tab-D4ZNSbMR.js.gz +0 -0
  189. codex/static_root/assets/{group-tab-DFma2R-E.879c37b15bbd.js → group-tab-DucpNoBs.a6f37ae940c0.js} +1 -1
  190. codex/static_root/assets/group-tab-DucpNoBs.a6f37ae940c0.js.br +0 -0
  191. codex/static_root/assets/group-tab-DucpNoBs.a6f37ae940c0.js.gz +0 -0
  192. codex/static_root/assets/{group-tab-DFma2R-E.js → group-tab-DucpNoBs.js} +1 -1
  193. codex/static_root/assets/group-tab-DucpNoBs.js.br +0 -0
  194. codex/static_root/assets/group-tab-DucpNoBs.js.gz +0 -0
  195. codex/static_root/assets/http-error-Cj0hTq-c.062a88ddd4dc.js +1 -0
  196. codex/static_root/assets/http-error-Cj0hTq-c.062a88ddd4dc.js.br +0 -0
  197. codex/static_root/assets/http-error-Cj0hTq-c.062a88ddd4dc.js.gz +0 -0
  198. codex/static_root/assets/http-error-Cj0hTq-c.js +1 -0
  199. codex/static_root/assets/http-error-Cj0hTq-c.js.br +0 -0
  200. codex/static_root/assets/http-error-Cj0hTq-c.js.gz +0 -0
  201. codex/static_root/assets/{library-tab-V8DUGoHN.js → library-tab-CzksdS3Q.566acdb45dec.js} +1 -1
  202. codex/static_root/assets/library-tab-CzksdS3Q.566acdb45dec.js.br +0 -0
  203. codex/static_root/assets/library-tab-CzksdS3Q.566acdb45dec.js.gz +0 -0
  204. codex/static_root/assets/{library-tab-V8DUGoHN.809c548ff347.js → library-tab-CzksdS3Q.js} +1 -1
  205. codex/static_root/assets/library-tab-CzksdS3Q.js.br +0 -0
  206. codex/static_root/assets/library-tab-CzksdS3Q.js.gz +0 -0
  207. codex/static_root/assets/main-DX-k2724.f73d08e9ad64.js +32 -0
  208. codex/static_root/assets/main-DX-k2724.f73d08e9ad64.js.br +0 -0
  209. codex/static_root/assets/main-DX-k2724.f73d08e9ad64.js.gz +0 -0
  210. codex/static_root/assets/main-DX-k2724.js +32 -0
  211. codex/static_root/assets/main-DX-k2724.js.br +0 -0
  212. codex/static_root/assets/main-DX-k2724.js.gz +0 -0
  213. codex/static_root/assets/{main-sK7HC5h4.css → main-Ztp855PW.css} +1 -1
  214. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  215. codex/static_root/assets/{main-sK7HC5h4.css.gz → main-Ztp855PW.css.gz} +0 -0
  216. codex/static_root/assets/{main-sK7HC5h4.6f04ddfc5f1a.css → main-Ztp855PW.d46d718fac45.css} +1 -1
  217. codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
  218. codex/static_root/assets/{main-sK7HC5h4.6f04ddfc5f1a.css.gz → main-Ztp855PW.d46d718fac45.css.gz} +0 -0
  219. codex/static_root/assets/pagination-toolbar-CQcR0a3T.7a2d33ed8c39.js +1 -0
  220. codex/static_root/assets/pagination-toolbar-CQcR0a3T.7a2d33ed8c39.js.br +0 -0
  221. codex/static_root/assets/pagination-toolbar-CQcR0a3T.7a2d33ed8c39.js.gz +0 -0
  222. codex/static_root/assets/pagination-toolbar-CQcR0a3T.js +1 -0
  223. codex/static_root/assets/pagination-toolbar-CQcR0a3T.js.br +0 -0
  224. codex/static_root/assets/pagination-toolbar-CQcR0a3T.js.gz +0 -0
  225. codex/static_root/assets/pagination-toolbar-DWdp_kmz.css +1 -0
  226. codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.br +0 -0
  227. codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.gz +0 -0
  228. codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css +1 -0
  229. codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.br +0 -0
  230. codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.gz +0 -0
  231. codex/static_root/assets/pdf-doc-GkMk4o5L.ef24c859a7a4.js +305 -0
  232. codex/static_root/assets/pdf-doc-GkMk4o5L.ef24c859a7a4.js.br +0 -0
  233. codex/static_root/assets/pdf-doc-GkMk4o5L.ef24c859a7a4.js.gz +0 -0
  234. codex/static_root/assets/pdf-doc-GkMk4o5L.js +305 -0
  235. codex/static_root/assets/pdf-doc-GkMk4o5L.js.br +0 -0
  236. codex/static_root/assets/pdf-doc-GkMk4o5L.js.gz +0 -0
  237. codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css +1 -0
  238. codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.br +0 -0
  239. codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.gz +0 -0
  240. codex/static_root/assets/reader-CCCfpM3x.css +1 -0
  241. codex/static_root/assets/reader-CCCfpM3x.css.br +0 -0
  242. codex/static_root/assets/reader-CCCfpM3x.css.gz +0 -0
  243. codex/static_root/assets/reader-Cl6mmQB_.7b9e7591954f.js +2 -0
  244. codex/static_root/assets/reader-Cl6mmQB_.7b9e7591954f.js.br +0 -0
  245. codex/static_root/assets/reader-Cl6mmQB_.7b9e7591954f.js.gz +0 -0
  246. codex/static_root/assets/reader-Cl6mmQB_.js +2 -0
  247. codex/static_root/assets/reader-Cl6mmQB_.js.br +0 -0
  248. codex/static_root/assets/reader-Cl6mmQB_.js.gz +0 -0
  249. codex/static_root/assets/relation-chips-CZ8VvR4y.d4232f3c2518.js +1 -0
  250. codex/static_root/assets/relation-chips-CZ8VvR4y.d4232f3c2518.js.br +0 -0
  251. codex/static_root/assets/relation-chips-CZ8VvR4y.d4232f3c2518.js.gz +0 -0
  252. codex/static_root/assets/relation-chips-CZ8VvR4y.js +1 -0
  253. codex/static_root/assets/relation-chips-CZ8VvR4y.js.br +0 -0
  254. codex/static_root/assets/relation-chips-CZ8VvR4y.js.gz +0 -0
  255. codex/static_root/assets/settings-drawer-DyS0A2pN.17a98928589f.js +2 -0
  256. codex/static_root/assets/settings-drawer-DyS0A2pN.17a98928589f.js.br +0 -0
  257. codex/static_root/assets/settings-drawer-DyS0A2pN.17a98928589f.js.gz +0 -0
  258. codex/static_root/assets/settings-drawer-DyS0A2pN.js +2 -0
  259. codex/static_root/assets/settings-drawer-DyS0A2pN.js.br +0 -0
  260. codex/static_root/assets/settings-drawer-DyS0A2pN.js.gz +0 -0
  261. codex/static_root/assets/stats-tab-R3Ekobfm.70fa09a67518.js +1 -0
  262. codex/static_root/assets/stats-tab-R3Ekobfm.70fa09a67518.js.br +0 -0
  263. codex/static_root/assets/stats-tab-R3Ekobfm.70fa09a67518.js.gz +0 -0
  264. codex/static_root/assets/stats-tab-R3Ekobfm.js +1 -0
  265. codex/static_root/assets/stats-tab-R3Ekobfm.js.br +0 -0
  266. codex/static_root/assets/stats-tab-R3Ekobfm.js.gz +0 -0
  267. codex/static_root/assets/task-tab-BhLDK_7Q.28ff51b3ed01.js +1 -0
  268. codex/static_root/assets/task-tab-BhLDK_7Q.28ff51b3ed01.js.br +0 -0
  269. codex/static_root/assets/task-tab-BhLDK_7Q.28ff51b3ed01.js.gz +0 -0
  270. codex/static_root/assets/task-tab-BhLDK_7Q.js +1 -0
  271. codex/static_root/assets/task-tab-BhLDK_7Q.js.br +0 -0
  272. codex/static_root/assets/task-tab-BhLDK_7Q.js.gz +0 -0
  273. codex/static_root/assets/to-case-BR2frjFx.87de5d7a0503.js +1 -0
  274. codex/static_root/assets/to-case-BR2frjFx.js +1 -0
  275. codex/static_root/assets/unauthorized-BbXjAe5l.d48912d34a68.js +1 -0
  276. codex/static_root/assets/unauthorized-BbXjAe5l.d48912d34a68.js.br +0 -0
  277. codex/static_root/assets/unauthorized-BbXjAe5l.d48912d34a68.js.gz +0 -0
  278. codex/static_root/assets/unauthorized-BbXjAe5l.js +1 -0
  279. codex/static_root/assets/unauthorized-BbXjAe5l.js.br +0 -0
  280. codex/static_root/assets/unauthorized-BbXjAe5l.js.gz +0 -0
  281. codex/static_root/assets/user-tab-DxITIl2d.d79d7f062930.js +1 -0
  282. codex/static_root/assets/user-tab-DxITIl2d.d79d7f062930.js.br +0 -0
  283. codex/static_root/assets/user-tab-DxITIl2d.d79d7f062930.js.gz +0 -0
  284. codex/static_root/assets/user-tab-DxITIl2d.js +1 -0
  285. codex/static_root/assets/user-tab-DxITIl2d.js.br +0 -0
  286. codex/static_root/assets/user-tab-DxITIl2d.js.gz +0 -0
  287. codex/static_root/js/choices-admin.d790df01c20a.json +1 -0
  288. codex/static_root/js/choices-admin.d790df01c20a.json.br +0 -0
  289. codex/static_root/js/choices-admin.d790df01c20a.json.gz +0 -0
  290. codex/static_root/js/choices-admin.json +1 -1
  291. codex/static_root/js/choices-admin.json.br +0 -0
  292. codex/static_root/js/choices-admin.json.gz +0 -0
  293. codex/static_root/js/choices.a0aaaa6c7ef7.json +1 -0
  294. codex/static_root/js/choices.a0aaaa6c7ef7.json.br +0 -0
  295. codex/static_root/js/choices.a0aaaa6c7ef7.json.gz +0 -0
  296. codex/static_root/js/choices.json +1 -1
  297. codex/static_root/js/choices.json.br +0 -0
  298. codex/static_root/js/choices.json.gz +0 -0
  299. codex/static_root/manifest.16d52597ef50.json +629 -0
  300. codex/static_root/manifest.16d52597ef50.json.br +0 -0
  301. codex/static_root/manifest.16d52597ef50.json.gz +0 -0
  302. codex/static_root/manifest.json +275 -288
  303. codex/static_root/manifest.json.br +0 -0
  304. codex/static_root/manifest.json.gz +0 -0
  305. codex/static_root/staticfiles.json +1 -1
  306. codex/urls/api/auth.py +2 -1
  307. codex/urls/api/browser.py +13 -4
  308. codex/urls/api/reader.py +3 -9
  309. codex/urls/api/v3.py +2 -0
  310. codex/urls/opds/binary.py +1 -1
  311. codex/util.py +10 -0
  312. codex/views/admin/api_key.py +2 -6
  313. codex/views/admin/auth.py +28 -0
  314. codex/views/admin/flag.py +2 -7
  315. codex/views/admin/group.py +2 -6
  316. codex/views/admin/library.py +7 -20
  317. codex/views/admin/stats.py +7 -66
  318. codex/views/admin/tasks.py +24 -11
  319. codex/views/admin/user.py +3 -9
  320. codex/views/auth.py +30 -53
  321. codex/views/bookmark.py +7 -11
  322. codex/views/browser/{browser_annotations.py → annotations.py} +90 -181
  323. codex/views/browser/base.py +23 -88
  324. codex/views/browser/breadcrumbs.py +202 -0
  325. codex/views/browser/browser.py +64 -342
  326. codex/views/browser/choices.py +22 -54
  327. codex/views/browser/cover.py +177 -0
  328. codex/views/browser/filters/annotations.py +24 -23
  329. codex/views/browser/filters/bookmark.py +9 -13
  330. codex/views/browser/filters/field.py +5 -5
  331. codex/views/browser/filters/group.py +18 -10
  332. codex/views/browser/filters/search.py +30 -18
  333. codex/views/browser/metadata.py +22 -31
  334. codex/views/browser/{browser_order_by.py → order_by.py} +2 -6
  335. codex/views/browser/paginate.py +106 -0
  336. codex/views/browser/{session.py → settings.py} +4 -3
  337. codex/views/browser/title.py +48 -0
  338. codex/views/browser/validate.py +158 -0
  339. codex/views/const.py +109 -0
  340. codex/views/download.py +5 -8
  341. codex/views/frontend.py +4 -2
  342. codex/views/mtime.py +72 -0
  343. codex/views/opds/auth.py +17 -0
  344. codex/views/opds/authentication_v1.py +46 -45
  345. codex/views/opds/binary.py +5 -10
  346. codex/views/opds/const.py +13 -0
  347. codex/views/opds/urls.py +6 -4
  348. codex/views/opds/util.py +4 -12
  349. codex/views/opds/v1/entry/links.py +18 -33
  350. codex/views/opds/v1/facets.py +7 -7
  351. codex/views/opds/v1/feed.py +52 -40
  352. codex/views/opds/v1/links.py +7 -5
  353. codex/views/opds/v1/opensearch_v1.py +2 -6
  354. codex/views/opds/v2/feed.py +22 -23
  355. codex/views/opds/v2/links.py +4 -2
  356. codex/views/opds/v2/publications.py +34 -54
  357. codex/views/opds/v2/top_links.py +2 -2
  358. codex/views/public.py +36 -0
  359. codex/views/reader/arcs.py +101 -0
  360. codex/views/reader/books.py +199 -0
  361. codex/views/reader/page.py +3 -7
  362. codex/views/reader/reader.py +56 -301
  363. codex/views/reader/{session.py → settings.py} +4 -2
  364. codex/views/session.py +77 -103
  365. codex/views/settings.py +34 -0
  366. codex/views/template.py +3 -3
  367. codex/views/util.py +63 -0
  368. codex/views/version.py +2 -2
  369. {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/METADATA +19 -14
  370. {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/RECORD +373 -362
  371. codex/librarian/importer/covers.py +0 -47
  372. codex/librarian/importer/update_comics.py +0 -85
  373. codex/migrations/0027_sort_name.py +0 -139
  374. codex/migrations/0028_custom_covers.py +0 -166
  375. codex/migrations/0029_choices_adminflag_and_timestamp.py +0 -44
  376. codex/static_root/assets/VCheckbox-BS5fdM4M.a5d446e4aeb3.js +0 -1
  377. codex/static_root/assets/VCheckbox-BS5fdM4M.a5d446e4aeb3.js.br +0 -0
  378. codex/static_root/assets/VCheckbox-BS5fdM4M.a5d446e4aeb3.js.gz +0 -0
  379. codex/static_root/assets/VCheckbox-BS5fdM4M.js +0 -1
  380. codex/static_root/assets/VCheckbox-BS5fdM4M.js.br +0 -0
  381. codex/static_root/assets/VCheckbox-BS5fdM4M.js.gz +0 -0
  382. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js.br +0 -0
  383. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js.gz +0 -0
  384. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.js.br +0 -0
  385. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.js.gz +0 -0
  386. codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js +0 -1
  387. codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js.br +0 -0
  388. codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js.gz +0 -0
  389. codex/static_root/assets/VCombobox-jg7SI5mn.js +0 -1
  390. codex/static_root/assets/VCombobox-jg7SI5mn.js.br +0 -0
  391. codex/static_root/assets/VCombobox-jg7SI5mn.js.gz +0 -0
  392. codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js +0 -1
  393. codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js.br +0 -0
  394. codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js.gz +0 -0
  395. codex/static_root/assets/VDialog-DiixpkQL.js +0 -1
  396. codex/static_root/assets/VDialog-DiixpkQL.js.br +0 -0
  397. codex/static_root/assets/VDialog-DiixpkQL.js.gz +0 -0
  398. codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js +0 -1
  399. codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js.br +0 -0
  400. codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js.gz +0 -0
  401. codex/static_root/assets/VExpansionPanels-Db5mPFue.js +0 -1
  402. codex/static_root/assets/VExpansionPanels-Db5mPFue.js.br +0 -0
  403. codex/static_root/assets/VExpansionPanels-Db5mPFue.js.gz +0 -0
  404. codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js +0 -1
  405. codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js.br +0 -0
  406. codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js.gz +0 -0
  407. codex/static_root/assets/VRadioGroup-DULWiMSI.js +0 -1
  408. codex/static_root/assets/VRadioGroup-DULWiMSI.js.br +0 -0
  409. codex/static_root/assets/VRadioGroup-DULWiMSI.js.gz +0 -0
  410. codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js +0 -1
  411. codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js.br +0 -0
  412. codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js.gz +0 -0
  413. codex/static_root/assets/VSelect-BnoCfCLY.js +0 -1
  414. codex/static_root/assets/VSelect-BnoCfCLY.js.br +0 -0
  415. codex/static_root/assets/VSelect-BnoCfCLY.js.gz +0 -0
  416. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css +0 -1
  417. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.br +0 -0
  418. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.gz +0 -0
  419. codex/static_root/assets/VSelect-MGVSeLgr.css +0 -1
  420. codex/static_root/assets/VSelect-MGVSeLgr.css.br +0 -0
  421. codex/static_root/assets/VSelect-MGVSeLgr.css.gz +0 -0
  422. codex/static_root/assets/VSelectionControl-BSL0qeL9.5b5835d286df.js +0 -1
  423. codex/static_root/assets/VSelectionControl-BSL0qeL9.5b5835d286df.js.br +0 -0
  424. codex/static_root/assets/VSelectionControl-BSL0qeL9.5b5835d286df.js.gz +0 -0
  425. codex/static_root/assets/VSelectionControl-BSL0qeL9.js +0 -1
  426. codex/static_root/assets/VSelectionControl-BSL0qeL9.js.br +0 -0
  427. codex/static_root/assets/VSelectionControl-BSL0qeL9.js.gz +0 -0
  428. codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js +0 -1
  429. codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js.br +0 -0
  430. codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js.gz +0 -0
  431. codex/static_root/assets/VSlideGroup-BQtz8h9p.js +0 -1
  432. codex/static_root/assets/VSlideGroup-BQtz8h9p.js.br +0 -0
  433. codex/static_root/assets/VSlideGroup-BQtz8h9p.js.gz +0 -0
  434. codex/static_root/assets/VTable-C0gAriFq.fa6e4b60d4ef.js.br +0 -0
  435. codex/static_root/assets/VTable-C0gAriFq.fa6e4b60d4ef.js.gz +0 -0
  436. codex/static_root/assets/VTable-C0gAriFq.js.br +0 -0
  437. codex/static_root/assets/VTable-C0gAriFq.js.gz +0 -0
  438. codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js +0 -1
  439. codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js.br +0 -0
  440. codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js.gz +0 -0
  441. codex/static_root/assets/VTextField-Da_Hs7ls.js +0 -1
  442. codex/static_root/assets/VTextField-Da_Hs7ls.js.br +0 -0
  443. codex/static_root/assets/VTextField-Da_Hs7ls.js.gz +0 -0
  444. codex/static_root/assets/VWindowItem-CFhKCj49.b2458002a937.js.br +0 -0
  445. codex/static_root/assets/VWindowItem-CFhKCj49.b2458002a937.js.gz +0 -0
  446. codex/static_root/assets/VWindowItem-CFhKCj49.js.br +0 -0
  447. codex/static_root/assets/VWindowItem-CFhKCj49.js.gz +0 -0
  448. codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js +0 -1
  449. codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js.br +0 -0
  450. codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js.gz +0 -0
  451. codex/static_root/assets/admin-BpGFsvOH.js +0 -1
  452. codex/static_root/assets/admin-BpGFsvOH.js.br +0 -0
  453. codex/static_root/assets/admin-BpGFsvOH.js.gz +0 -0
  454. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.br +0 -0
  455. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css.br +0 -0
  456. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js +0 -30
  457. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js.br +0 -0
  458. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js.gz +0 -0
  459. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js +0 -30
  460. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js.br +0 -0
  461. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js.gz +0 -0
  462. codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js +0 -1
  463. codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js.br +0 -0
  464. codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js.gz +0 -0
  465. codex/static_root/assets/browser-CRPvEdqv.js +0 -1
  466. codex/static_root/assets/browser-CRPvEdqv.js.br +0 -0
  467. codex/static_root/assets/browser-CRPvEdqv.js.gz +0 -0
  468. codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css +0 -1
  469. codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css.br +0 -0
  470. codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css.gz +0 -0
  471. codex/static_root/assets/browser-Zqc9cR6T.css +0 -1
  472. codex/static_root/assets/browser-Zqc9cR6T.css.br +0 -0
  473. codex/static_root/assets/browser-Zqc9cR6T.css.gz +0 -0
  474. codex/static_root/assets/change-password-dialog-JYM9aAR9.f71d378f4fa6.js.br +0 -0
  475. codex/static_root/assets/change-password-dialog-JYM9aAR9.f71d378f4fa6.js.gz +0 -0
  476. codex/static_root/assets/change-password-dialog-JYM9aAR9.js.br +0 -0
  477. codex/static_root/assets/change-password-dialog-JYM9aAR9.js.gz +0 -0
  478. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.515858dd21e8.js.br +0 -0
  479. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.515858dd21e8.js.gz +0 -0
  480. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.js.br +0 -0
  481. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.js.gz +0 -0
  482. codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js +0 -1
  483. codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js.br +0 -0
  484. codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js.gz +0 -0
  485. codex/static_root/assets/datetime-column-BT1MSP82.js +0 -1
  486. codex/static_root/assets/datetime-column-BT1MSP82.js.br +0 -0
  487. codex/static_root/assets/datetime-column-BT1MSP82.js.gz +0 -0
  488. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +0 -1
  489. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
  490. codex/static_root/assets/datetime-column-Dg_RxTVM.css +0 -1
  491. codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
  492. codex/static_root/assets/filter-frSNsexx.eab1570bd32d.js +0 -1
  493. codex/static_root/assets/filter-frSNsexx.eab1570bd32d.js.br +0 -0
  494. codex/static_root/assets/filter-frSNsexx.eab1570bd32d.js.gz +0 -0
  495. codex/static_root/assets/filter-frSNsexx.js +0 -1
  496. codex/static_root/assets/filter-frSNsexx.js.br +0 -0
  497. codex/static_root/assets/filter-frSNsexx.js.gz +0 -0
  498. codex/static_root/assets/flag-tab-Ci_XcDT0.a5ea640a08d6.js.br +0 -0
  499. codex/static_root/assets/flag-tab-Ci_XcDT0.a5ea640a08d6.js.gz +0 -0
  500. codex/static_root/assets/flag-tab-Ci_XcDT0.js.br +0 -0
  501. codex/static_root/assets/flag-tab-Ci_XcDT0.js.gz +0 -0
  502. codex/static_root/assets/group-tab-DFma2R-E.879c37b15bbd.js.br +0 -0
  503. codex/static_root/assets/group-tab-DFma2R-E.879c37b15bbd.js.gz +0 -0
  504. codex/static_root/assets/group-tab-DFma2R-E.js.br +0 -0
  505. codex/static_root/assets/group-tab-DFma2R-E.js.gz +0 -0
  506. codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js +0 -1
  507. codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js.br +0 -0
  508. codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js.gz +0 -0
  509. codex/static_root/assets/http-error-B3fQGxpt.js +0 -1
  510. codex/static_root/assets/http-error-B3fQGxpt.js.br +0 -0
  511. codex/static_root/assets/http-error-B3fQGxpt.js.gz +0 -0
  512. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +0 -1
  513. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
  514. codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
  515. codex/static_root/assets/index-DUjeNg4x.js +0 -1
  516. codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
  517. codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
  518. codex/static_root/assets/library-tab-V8DUGoHN.809c548ff347.js.br +0 -0
  519. codex/static_root/assets/library-tab-V8DUGoHN.809c548ff347.js.gz +0 -0
  520. codex/static_root/assets/library-tab-V8DUGoHN.js.br +0 -0
  521. codex/static_root/assets/library-tab-V8DUGoHN.js.gz +0 -0
  522. codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js +0 -32
  523. codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js.br +0 -0
  524. codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js.gz +0 -0
  525. codex/static_root/assets/main-Cavvqkyp.js +0 -32
  526. codex/static_root/assets/main-Cavvqkyp.js.br +0 -0
  527. codex/static_root/assets/main-Cavvqkyp.js.gz +0 -0
  528. codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
  529. codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
  530. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css +0 -1
  531. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.br +0 -0
  532. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.gz +0 -0
  533. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css +0 -1
  534. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.br +0 -0
  535. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.gz +0 -0
  536. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js +0 -1
  537. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js.br +0 -0
  538. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js.gz +0 -0
  539. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js +0 -1
  540. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js.br +0 -0
  541. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js.gz +0 -0
  542. codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js +0 -35
  543. codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js.br +0 -0
  544. codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js.gz +0 -0
  545. codex/static_root/assets/pdf-doc-4PKLN-Z3.js +0 -35
  546. codex/static_root/assets/pdf-doc-4PKLN-Z3.js.br +0 -0
  547. codex/static_root/assets/pdf-doc-4PKLN-Z3.js.gz +0 -0
  548. codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js +0 -2
  549. codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js.br +0 -0
  550. codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js.gz +0 -0
  551. codex/static_root/assets/reader-C39uGGxn.js +0 -2
  552. codex/static_root/assets/reader-C39uGGxn.js.br +0 -0
  553. codex/static_root/assets/reader-C39uGGxn.js.gz +0 -0
  554. codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css +0 -1
  555. codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css.br +0 -0
  556. codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css.gz +0 -0
  557. codex/static_root/assets/reader-N2Ei_y2K.css +0 -1
  558. codex/static_root/assets/reader-N2Ei_y2K.css.br +0 -0
  559. codex/static_root/assets/reader-N2Ei_y2K.css.gz +0 -0
  560. codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js +0 -1
  561. codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js.br +0 -0
  562. codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js.gz +0 -0
  563. codex/static_root/assets/relation-chips-CAC5ytqH.js +0 -1
  564. codex/static_root/assets/relation-chips-CAC5ytqH.js.br +0 -0
  565. codex/static_root/assets/relation-chips-CAC5ytqH.js.gz +0 -0
  566. codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js +0 -2
  567. codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js.br +0 -0
  568. codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js.gz +0 -0
  569. codex/static_root/assets/settings-drawer-CT73ieiV.js +0 -2
  570. codex/static_root/assets/settings-drawer-CT73ieiV.js.br +0 -0
  571. codex/static_root/assets/settings-drawer-CT73ieiV.js.gz +0 -0
  572. codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js +0 -1
  573. codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js.br +0 -0
  574. codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js.gz +0 -0
  575. codex/static_root/assets/stats-tab-C6qAnV6-.js +0 -1
  576. codex/static_root/assets/stats-tab-C6qAnV6-.js.br +0 -0
  577. codex/static_root/assets/stats-tab-C6qAnV6-.js.gz +0 -0
  578. codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js +0 -1
  579. codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js.br +0 -0
  580. codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js.gz +0 -0
  581. codex/static_root/assets/task-tab-JcG2vvgt.js +0 -1
  582. codex/static_root/assets/task-tab-JcG2vvgt.js.br +0 -0
  583. codex/static_root/assets/task-tab-JcG2vvgt.js.gz +0 -0
  584. codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +0 -1
  585. codex/static_root/assets/to-case-kUm8NPpW.js +0 -1
  586. codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js +0 -1
  587. codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js.br +0 -0
  588. codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js.gz +0 -0
  589. codex/static_root/assets/unauthorized-28bmiwAU.js +0 -1
  590. codex/static_root/assets/unauthorized-28bmiwAU.js.br +0 -0
  591. codex/static_root/assets/unauthorized-28bmiwAU.js.gz +0 -0
  592. codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js +0 -1
  593. codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js.br +0 -0
  594. codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js.gz +0 -0
  595. codex/static_root/assets/user-tab-BOq4BMMc.js +0 -1
  596. codex/static_root/assets/user-tab-BOq4BMMc.js.br +0 -0
  597. codex/static_root/assets/user-tab-BOq4BMMc.js.gz +0 -0
  598. codex/static_root/js/choices-admin.ef1ff3a8b9da.json +0 -1
  599. codex/static_root/js/choices-admin.ef1ff3a8b9da.json.br +0 -0
  600. codex/static_root/js/choices-admin.ef1ff3a8b9da.json.gz +0 -0
  601. codex/static_root/js/choices.23fedac2b9ba.json +0 -1
  602. codex/static_root/js/choices.23fedac2b9ba.json.br +0 -0
  603. codex/static_root/js/choices.23fedac2b9ba.json.gz +0 -0
  604. codex/static_root/manifest.ad9da9714e46.json +0 -642
  605. codex/static_root/manifest.ad9da9714e46.json.br +0 -0
  606. codex/static_root/manifest.ad9da9714e46.json.gz +0 -0
  607. codex/views/browser/browser_breadcrumbs.py +0 -175
  608. codex/views/browser/const.py +0 -3
  609. codex/views/cover.py +0 -78
  610. {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/LICENSE +0 -0
  611. {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/WHEEL +0 -0
  612. {codex-1.6.0a6.dist-info → codex-1.6.0a8.dist-info}/entry_points.txt +0 -0
@@ -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
- from codex.views.cover import CoverView
3
+ from codex.views.browser.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,17 @@
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
+ from codex.views.util import pop_name
9
10
 
10
- OPDS_VERSIONS = (1, 2)
11
+ _OPDS_VERSIONS = (1, 2)
11
12
 
12
13
 
13
- class OPDSURLsView(GenericAPIView):
14
+ class OPDSURLsView(AuthGenericAPIView):
14
15
  """OPDS URLs."""
15
16
 
16
17
  serializer_class = OPDSURLsSerializer
@@ -19,7 +20,8 @@ class OPDSURLsView(GenericAPIView):
19
20
  """Resolve the urls."""
20
21
  obj = {}
21
22
  route = DEFAULTS["breadcrumbs"][0]
22
- for version in OPDS_VERSIONS:
23
+ route = pop_name(route)
24
+ for version in _OPDS_VERSIONS:
23
25
  key = f"v{version}"
24
26
  name = f"opds:v{version}:feed"
25
27
  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
- StoryArc,
16
- Tag,
17
- Team,
18
11
  )
19
12
  from codex.serializers.choices import DEFAULTS
20
-
21
- OPDS_M2M_MODELS = (Character, Genre, Location, SeriesGroup, StoryArc, Tag, Team)
13
+ from codex.views.auth import GroupACLMixin
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):
@@ -65,9 +58,7 @@ def get_m2m_objects(pks) -> dict:
65
58
  cats = {}
66
59
  for model in OPDS_M2M_MODELS:
67
60
  table = model.__name__.lower()
68
- rel = "comic"
69
- if model == StoryArc:
70
- rel = "storyarcnumber__" + rel
61
+ rel = GroupACLMixin.get_rel_prefix(model)
71
62
  comic_filter = {rel + "__in": pks}
72
63
  qs = model.objects.filter(**comic_filter).order_by("name").only("name")
73
64
  cats[table] = qs
@@ -81,6 +72,7 @@ def full_redirect_view(url_name):
81
72
  def func(request):
82
73
  """Redirect to view, forwarding query strings and auth."""
83
74
  kwargs = DEFAULTS["breadcrumbs"][0]
75
+ kwargs.pop("name", None)
84
76
  url = reverse(url_name, kwargs=kwargs)
85
77
 
86
78
  # Forward the query string.
@@ -1,12 +1,12 @@
1
1
  """OPDS v1 Entry Links Methods."""
2
2
 
3
+ from datetime import datetime
4
+ from math import floor
3
5
  from urllib.parse import quote_plus
4
6
 
5
- from django.contrib.staticfiles.storage import staticfiles_storage
6
7
  from django.urls import reverse
7
8
 
8
9
  from codex.logger.logging import get_logger
9
- from codex.views.const import MISSING_COVER_FN, MISSING_COVER_NAME_MAP
10
10
  from codex.views.opds.const import MimeType, Rel
11
11
  from codex.views.opds.util import update_href_query_params
12
12
  from codex.views.opds.v1.data import OPDS1Link
@@ -31,39 +31,24 @@ class OPDS1EntryLinksMixin:
31
31
  self.mime_type_map = data.mime_type_map
32
32
  self.title_filename_fallback = title_filename_fallback
33
33
 
34
- def _thumb_link(self):
34
+ def _cover_link(self, rel):
35
35
  if self.fake:
36
36
  return None
37
- cover_pk = getattr(self.obj, "cover_pk", None)
38
- if cover_pk:
39
- kwargs = {"pk": cover_pk}
37
+ # cover_pk = getattr(self.obj, "cover_pk", None)
38
+ try:
39
+ kwargs = {"group": self.obj.group, "pks": self.obj.ids}
40
+ ts = floor(datetime.timestamp(self.obj.updated_at))
41
+ query_params = {
42
+ "customCovers": True,
43
+ "dynamicCovers": False,
44
+ "ts": ts,
45
+ }
40
46
  href = reverse("opds:bin:cover", kwargs=kwargs)
47
+ href = update_href_query_params(href, query_params)
41
48
  mime_type = "image/webp"
42
- else:
43
- cover_name = MISSING_COVER_NAME_MAP.get(self.obj.group)
44
- cover_path = "img/"
45
- if cover_name:
46
- cover_path += cover_name + ".svg"
47
- mime_type = "image/svg+xml"
48
- else:
49
- cover_path += MISSING_COVER_FN
50
- mime_type = "image/webp"
51
- href = staticfiles_storage.url(cover_path)
52
- return OPDS1Link(Rel.THUMBNAIL, href, mime_type)
53
-
54
- def _image_link(self):
55
- if self.fake:
56
- return None
57
- cover_pk = getattr(self.obj, "cover_pk", None)
58
- if cover_pk:
59
- kwargs = {"pk": cover_pk, "page": 0}
60
- href = reverse("opds:bin:page", kwargs=kwargs)
61
- mime_type = "image/jpeg"
62
- else:
63
- fn = f"img/{MISSING_COVER_FN}"
64
- href = staticfiles_storage.url(fn)
65
- mime_type = "image/webp"
66
- return OPDS1Link(Rel.IMAGE, href, mime_type)
49
+ return OPDS1Link(rel, href, mime_type)
50
+ except Exception:
51
+ LOG.exception("create thumb")
67
52
 
68
53
  def _nav_href(self, metadata=False):
69
54
  try:
@@ -149,9 +134,9 @@ class OPDS1EntryLinksMixin:
149
134
  """Create all entry links."""
150
135
  result = []
151
136
  try:
152
- if thumb := self._thumb_link():
137
+ if thumb := self._cover_link(Rel.THUMBNAIL):
153
138
  result += [thumb]
154
- if image := self._image_link():
139
+ if image := self._cover_link(Rel.IMAGE):
155
140
  result += [image]
156
141
 
157
142
  if self.obj.group == "c" and not self.fake:
@@ -12,6 +12,7 @@ from codex.views.opds.util import update_href_query_params
12
12
  from codex.views.opds.v1.data import OPDS1Link
13
13
  from codex.views.opds.v1.entry.data import OPDS1EntryData, OPDS1EntryObject
14
14
  from codex.views.opds.v1.entry.entry import OPDS1Entry
15
+ from codex.views.util import pop_name
15
16
 
16
17
 
17
18
  @dataclass
@@ -77,7 +78,7 @@ DEFAULT_FACETS = {
77
78
  }
78
79
 
79
80
 
80
- class FacetsMixin(BrowserView):
81
+ class OPDS1FacetsView(BrowserView):
81
82
  """OPDS 1 Facets methods."""
82
83
 
83
84
  OPDS = 1
@@ -89,15 +90,14 @@ class FacetsMixin(BrowserView):
89
90
  obj = MappingProxyType({})
90
91
 
91
92
  def _facet(self, kwargs, facet_group, facet_title, new_query_params):
93
+ kwargs = pop_name(kwargs)
92
94
  href = reverse("opds:v1:feed", kwargs=kwargs)
93
95
  facet_active = False
94
96
  for key, val in new_query_params.items():
95
- if self.request.query_params.get(key) == val:
97
+ if self.request.GET.get(key) == val:
96
98
  facet_active = True
97
99
  break
98
- href = update_href_query_params(
99
- href, self.request.query_params, new_query_params
100
- )
100
+ href = update_href_query_params(href, self.request.GET, new_query_params)
101
101
 
102
102
  title = " ".join(filter(None, (facet_group.title_prefix, facet_title))).strip()
103
103
  return OPDS1Link(
@@ -118,7 +118,7 @@ class FacetsMixin(BrowserView):
118
118
  ids=item.get("pks"),
119
119
  name=name,
120
120
  )
121
- qps = {**self.request.query_params}
121
+ qps = {**self.request.GET}
122
122
  qps.update(query_params)
123
123
  zero_pad = self.obj["zero_pad"]
124
124
  data = OPDS1EntryData(
@@ -131,7 +131,7 @@ class FacetsMixin(BrowserView):
131
131
  default_val = DEFAULT_FACETS.get(facet_group.query_param)
132
132
  if facet.value == default_val:
133
133
  compare += [None]
134
- return self.request.query_params.get(facet_group.query_param) in compare
134
+ return self.request.GET.get(facet_group.query_param) in compare
135
135
 
136
136
  @staticmethod
137
137
  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
@@ -44,20 +41,19 @@ class OpdsNs:
44
41
  ACQUISITION = "http://opds-spec.org/2010/acquisition"
45
42
 
46
43
 
47
- class UserAgentPrefixes:
44
+ class UserAgentNames:
48
45
  """Control whether to hack in facets with nav links."""
49
46
 
50
- CLIENT_REORDERS = ("Chunky",)
51
- FACET_SUPPORT = ("yar",) # kybooks
52
- SIMPLE_DOWNLOAD_MIME_TYPES = ("PocketBook",)
53
- # Other known valid prefixes:
47
+ CLIENT_REORDERS = frozenset({"Chunky"})
48
+ FACET_SUPPORT = frozenset({"yar"}) # kybooks
49
+ SIMPLE_DOWNLOAD_MIME_TYPES = frozenset({"PocketBook Reader"})
50
+ # Other known valid names:
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")
@@ -175,14 +168,17 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
175
168
 
176
169
  def _ensure_page_counts(self):
177
170
  """Ensure page counts on books with just in time comicbox."""
171
+ if self.admin_flags.get("import_metadata"):
172
+ return
178
173
  books_qs: QuerySet = self.obj["books"] # type: ignore
179
174
  import_pks = set()
180
175
  new_books = []
181
176
  for book in books_qs:
182
- if not book.page_count:
183
- with Comicbox(book.path) as cb:
184
- book.file_type = cb.get_file_type()
185
- book.page_count = cb.get_page_count()
177
+ if book.page_count is not None:
178
+ continue
179
+ with Comicbox(book.path) as cb:
180
+ book.file_type = cb.get_file_type()
181
+ book.page_count = cb.get_page_count()
186
182
  new_books.append(book)
187
183
  import_pks.add(book.pk)
188
184
  if new_books:
@@ -194,7 +190,24 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
194
190
 
195
191
  def get_object(self): # type: ignore
196
192
  """Get the browser page and serialize it for this subclass."""
197
- self.obj = super().get_object()
193
+ group_qs, book_qs, num_pages, total_count, zero_pad, mtime = (
194
+ super()._get_group_and_books()
195
+ )
196
+ book_qs = book_qs.select_related("series", "volume", "language")
197
+
198
+ title = self.get_browser_page_title()
199
+ self.obj = MappingProxyType(
200
+ {
201
+ "title": title,
202
+ "groups": group_qs,
203
+ "books": book_qs,
204
+ "zero_pad": zero_pad,
205
+ "num_pages": num_pages,
206
+ "total_count": total_count,
207
+ "mtime": mtime,
208
+ }
209
+ )
210
+
198
211
  self.is_aq_feed = self.model_group in ("c", "f")
199
212
 
200
213
  self._ensure_page_counts()
@@ -204,22 +217,21 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
204
217
 
205
218
  def _set_user_agent_variables(self):
206
219
  """Set User Agent variables."""
207
- # defaults in FacetsMixin
208
220
  user_agent = self.request.headers.get("User-Agent")
209
221
  if not user_agent:
210
222
  return
211
- for prefix in UserAgentPrefixes.FACET_SUPPORT:
212
- if user_agent.startswith(prefix):
213
- self.use_facets = True
214
- break
215
- for prefix in UserAgentPrefixes.CLIENT_REORDERS:
216
- if user_agent.startswith(prefix):
217
- self.skip_order_facets = True
218
- break
219
- for prefix in UserAgentPrefixes.SIMPLE_DOWNLOAD_MIME_TYPES:
220
- if user_agent.startswith(prefix):
221
- self.mime_type_map = MimeType.SIMPLE_FILE_TYPE_MAP
222
- break
223
+ user_agent_parts = user_agent.split("/", 1)
224
+ if user_agent_parts:
225
+ user_agent_name = user_agent_parts[0]
226
+ else:
227
+ return
228
+
229
+ if user_agent_name in UserAgentNames.FACET_SUPPORT:
230
+ self.use_facets = True
231
+ if user_agent_name in UserAgentNames.CLIENT_REORDERS:
232
+ self.skip_order_facets = True
233
+ if user_agent_name in UserAgentNames.SIMPLE_DOWNLOAD_MIME_TYPES:
234
+ self.mime_type_map = MimeType.SIMPLE_FILE_TYPE_MAP
223
235
 
224
236
  def set_opds_request_type(self):
225
237
  """Set the opds request type variables."""
@@ -232,7 +244,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
232
244
  self.acquisition_groups = frozenset({*self.valid_nav_groups[-2:]} | {"c"})
233
245
  self.is_opds_1_acquisition = group in self.acquisition_groups
234
246
  self.is_opds_metadata = (
235
- self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
247
+ self.request.GET.get("opdsMetadata", "").lower() not in FALSY
236
248
  )
237
249
 
238
250
  def init_request(self):
@@ -14,7 +14,8 @@ 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
+ from codex.views.util import pop_name
18
19
 
19
20
  LOG = get_logger(__name__)
20
21
 
@@ -101,13 +102,13 @@ class RootTopLinks:
101
102
  ALL = (NEW, FEATURED, LAST_READ)
102
103
 
103
104
 
104
- class LinksMixin(FacetsMixin):
105
+ class OPDS1LinksView(OPDS1FacetsView):
105
106
  """OPDS 1 Links methods."""
106
107
 
107
108
  # overwritten in get_object()
108
109
  DEFAULT_ROUTE = MappingProxyType(
109
110
  {
110
- **FacetsMixin.DEFAULT_ROUTE,
111
+ **OPDS1FacetsView.DEFAULT_ROUTE,
111
112
  "name": "opds:v1:feed",
112
113
  }
113
114
  )
@@ -120,7 +121,7 @@ class LinksMixin(FacetsMixin):
120
121
  return False
121
122
 
122
123
  for key, value in top_link.query_params.items():
123
- if str(self.request.query_params.get(key)) != str(value):
124
+ if str(self.request.GET.get(key)) != str(value):
124
125
  return False
125
126
 
126
127
  return True
@@ -128,7 +129,8 @@ class LinksMixin(FacetsMixin):
128
129
  def _link(self, kwargs, rel, query_params=None, mime_type=MimeType.NAV):
129
130
  """Create a link."""
130
131
  if query_params is None:
131
- query_params = self.request.query_params
132
+ query_params = self.request.GET
133
+ kwargs = pop_name(kwargs)
132
134
  href = reverse("opds:v1:feed", kwargs=kwargs)
133
135
  href = update_href_query_params(href, query_params)
134
136
  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,)