codex 1.7.2__py3-none-any.whl → 1.7.3a0__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 (697) hide show
  1. codex/choices.py +13 -5
  2. codex/librarian/bookmark/bookmarkd.py +4 -5
  3. codex/librarian/bookmark/tasks.py +1 -1
  4. codex/librarian/bookmark/update.py +15 -29
  5. codex/librarian/cron/crond.py +1 -1
  6. codex/librarian/importer/query_fks.py +2 -2
  7. codex/librarian/janitor/janitor.py +11 -1
  8. codex/librarian/janitor/log_compress.py +77 -0
  9. codex/librarian/janitor/status.py +1 -0
  10. codex/librarian/janitor/tasks.py +5 -0
  11. codex/librarian/librariand.py +18 -13
  12. codex/librarian/search/update.py +8 -4
  13. codex/migrations/0031_adminflag_banner.py +50 -0
  14. codex/models/admin.py +4 -3
  15. codex/serializers/admin/flags.py +1 -1
  16. codex/serializers/auth.py +1 -0
  17. codex/serializers/opds/v2/__init__.py +1 -0
  18. codex/serializers/opds/v2/facet.py +13 -0
  19. codex/serializers/opds/v2/feed.py +35 -0
  20. codex/serializers/opds/v2/links.py +73 -0
  21. codex/serializers/opds/v2/metadata.py +22 -0
  22. codex/serializers/opds/v2/progression.py +63 -0
  23. codex/serializers/opds/v2/publication.py +78 -0
  24. codex/serializers/opds/{v2_unused.py → v2/unused.py} +1 -1
  25. codex/settings/settings.py +1 -0
  26. codex/static_root/assets/VCheckbox-pHjSEgdV.176f3c1f4191.js +1 -0
  27. codex/static_root/assets/VCheckbox-pHjSEgdV.176f3c1f4191.js.br +0 -0
  28. codex/static_root/assets/VCheckbox-pHjSEgdV.176f3c1f4191.js.gz +0 -0
  29. codex/static_root/assets/VCheckbox-pHjSEgdV.js +1 -0
  30. codex/static_root/assets/VCheckbox-pHjSEgdV.js.br +0 -0
  31. codex/static_root/assets/VCheckbox-pHjSEgdV.js.gz +0 -0
  32. codex/static_root/assets/VCheckboxBtn-DBKqrZGq.467e75859b62.js +1 -0
  33. codex/static_root/assets/VCheckboxBtn-DBKqrZGq.467e75859b62.js.br +0 -0
  34. codex/static_root/assets/VCheckboxBtn-DBKqrZGq.467e75859b62.js.gz +0 -0
  35. codex/static_root/assets/VCheckboxBtn-DBKqrZGq.js +1 -0
  36. codex/static_root/assets/VCheckboxBtn-DBKqrZGq.js.br +0 -0
  37. codex/static_root/assets/VCheckboxBtn-DBKqrZGq.js.gz +0 -0
  38. codex/static_root/assets/VCombobox-Bg7uOTbF.2fc6073b789f.js +1 -0
  39. codex/static_root/assets/VCombobox-Bg7uOTbF.2fc6073b789f.js.br +0 -0
  40. codex/static_root/assets/VCombobox-Bg7uOTbF.2fc6073b789f.js.gz +0 -0
  41. codex/static_root/assets/VCombobox-Bg7uOTbF.js +1 -0
  42. codex/static_root/assets/VCombobox-Bg7uOTbF.js.br +0 -0
  43. codex/static_root/assets/VCombobox-Bg7uOTbF.js.gz +0 -0
  44. codex/static_root/assets/VDialog-Dyd05WHA.b07aca713774.css +1 -0
  45. codex/static_root/assets/VDialog-Dyd05WHA.b07aca713774.css.br +0 -0
  46. codex/static_root/assets/VDialog-Dyd05WHA.b07aca713774.css.gz +0 -0
  47. codex/static_root/assets/VDialog-Dyd05WHA.css +1 -0
  48. codex/static_root/assets/VDialog-Dyd05WHA.css.br +0 -0
  49. codex/static_root/assets/VDialog-Dyd05WHA.css.gz +0 -0
  50. codex/static_root/assets/VDialog-IrbBYFLM.91a058d572c4.js +1 -0
  51. codex/static_root/assets/VDialog-IrbBYFLM.91a058d572c4.js.br +0 -0
  52. codex/static_root/assets/VDialog-IrbBYFLM.91a058d572c4.js.gz +0 -0
  53. codex/static_root/assets/VDialog-IrbBYFLM.js +1 -0
  54. codex/static_root/assets/VDialog-IrbBYFLM.js.br +0 -0
  55. codex/static_root/assets/VDialog-IrbBYFLM.js.gz +0 -0
  56. codex/static_root/assets/VDivider-CuRohbVQ.3c12b040aed7.js +1 -0
  57. codex/static_root/assets/VDivider-CuRohbVQ.3c12b040aed7.js.br +0 -0
  58. codex/static_root/assets/VDivider-CuRohbVQ.3c12b040aed7.js.gz +0 -0
  59. codex/static_root/assets/VDivider-CuRohbVQ.js +1 -0
  60. codex/static_root/assets/VDivider-CuRohbVQ.js.br +0 -0
  61. codex/static_root/assets/VDivider-CuRohbVQ.js.gz +0 -0
  62. codex/static_root/assets/VDivider-DE8MJPGz.3d2e17799e5c.css +1 -0
  63. codex/static_root/assets/VDivider-DE8MJPGz.3d2e17799e5c.css.br +0 -0
  64. codex/static_root/assets/VDivider-DE8MJPGz.3d2e17799e5c.css.gz +0 -0
  65. codex/static_root/assets/VDivider-DE8MJPGz.css +1 -0
  66. codex/static_root/assets/VDivider-DE8MJPGz.css.br +0 -0
  67. codex/static_root/assets/VDivider-DE8MJPGz.css.gz +0 -0
  68. codex/static_root/assets/VExpansionPanels-b0xWLH75.bff6ea8b4055.js +1 -0
  69. codex/static_root/assets/VExpansionPanels-b0xWLH75.bff6ea8b4055.js.br +0 -0
  70. codex/static_root/assets/VExpansionPanels-b0xWLH75.bff6ea8b4055.js.gz +0 -0
  71. codex/static_root/assets/VExpansionPanels-b0xWLH75.js +1 -0
  72. codex/static_root/assets/VExpansionPanels-b0xWLH75.js.br +0 -0
  73. codex/static_root/assets/VExpansionPanels-b0xWLH75.js.gz +0 -0
  74. codex/static_root/assets/VForm-B4hkqnS8.afbd5cf0908a.js +1 -0
  75. codex/static_root/assets/VForm-B4hkqnS8.afbd5cf0908a.js.br +0 -0
  76. codex/static_root/assets/VForm-B4hkqnS8.afbd5cf0908a.js.gz +0 -0
  77. codex/static_root/assets/VForm-B4hkqnS8.js +1 -0
  78. codex/static_root/assets/VForm-B4hkqnS8.js.br +0 -0
  79. codex/static_root/assets/VForm-B4hkqnS8.js.gz +0 -0
  80. codex/static_root/assets/VForm-v1Gmu6E1.138c0b89b6fc.css +1 -0
  81. codex/static_root/assets/VForm-v1Gmu6E1.138c0b89b6fc.css.br +0 -0
  82. codex/static_root/assets/VForm-v1Gmu6E1.138c0b89b6fc.css.gz +0 -0
  83. codex/static_root/assets/VForm-v1Gmu6E1.css +1 -0
  84. codex/static_root/assets/VForm-v1Gmu6E1.css.br +0 -0
  85. codex/static_root/assets/VForm-v1Gmu6E1.css.gz +0 -0
  86. codex/static_root/assets/VRadioGroup-Cbayik6d.034f0f275265.js +1 -0
  87. codex/static_root/assets/VRadioGroup-Cbayik6d.034f0f275265.js.br +0 -0
  88. codex/static_root/assets/VRadioGroup-Cbayik6d.034f0f275265.js.gz +0 -0
  89. codex/static_root/assets/VRadioGroup-Cbayik6d.js +1 -0
  90. codex/static_root/assets/VRadioGroup-Cbayik6d.js.br +0 -0
  91. codex/static_root/assets/VRadioGroup-Cbayik6d.js.gz +0 -0
  92. codex/static_root/assets/VSelect-8MFoaOUz.6309f7bf17bb.css +1 -0
  93. codex/static_root/assets/VSelect-8MFoaOUz.6309f7bf17bb.css.br +0 -0
  94. codex/static_root/assets/VSelect-8MFoaOUz.6309f7bf17bb.css.gz +0 -0
  95. codex/static_root/assets/VSelect-8MFoaOUz.css +1 -0
  96. codex/static_root/assets/VSelect-8MFoaOUz.css.br +0 -0
  97. codex/static_root/assets/VSelect-8MFoaOUz.css.gz +0 -0
  98. codex/static_root/assets/VSelect-BsuB-2P1.abe187c8b53f.js +1 -0
  99. codex/static_root/assets/VSelect-BsuB-2P1.abe187c8b53f.js.br +0 -0
  100. codex/static_root/assets/VSelect-BsuB-2P1.abe187c8b53f.js.gz +0 -0
  101. codex/static_root/assets/VSelect-BsuB-2P1.js +1 -0
  102. codex/static_root/assets/VSelect-BsuB-2P1.js.br +0 -0
  103. codex/static_root/assets/VSelect-BsuB-2P1.js.gz +0 -0
  104. codex/static_root/assets/VSelectionControl-DLxcLoTI.cc18de4e83f6.css +1 -0
  105. codex/static_root/assets/VSelectionControl-DLxcLoTI.cc18de4e83f6.css.br +0 -0
  106. codex/static_root/assets/VSelectionControl-DLxcLoTI.cc18de4e83f6.css.gz +0 -0
  107. codex/static_root/assets/VSelectionControl-DLxcLoTI.css +1 -0
  108. codex/static_root/assets/VSelectionControl-DLxcLoTI.css.br +0 -0
  109. codex/static_root/assets/VSelectionControl-DLxcLoTI.css.gz +0 -0
  110. codex/static_root/assets/VSelectionControl-L41n5bnX.f4674bb62425.js +1 -0
  111. codex/static_root/assets/VSelectionControl-L41n5bnX.f4674bb62425.js.br +0 -0
  112. codex/static_root/assets/VSelectionControl-L41n5bnX.f4674bb62425.js.gz +0 -0
  113. codex/static_root/assets/VSelectionControl-L41n5bnX.js +1 -0
  114. codex/static_root/assets/VSelectionControl-L41n5bnX.js.br +0 -0
  115. codex/static_root/assets/VSelectionControl-L41n5bnX.js.gz +0 -0
  116. codex/static_root/assets/VTable-CYv4gUUt.76e4658abeb3.js +1 -0
  117. codex/static_root/assets/VTable-CYv4gUUt.76e4658abeb3.js.br +0 -0
  118. codex/static_root/assets/VTable-CYv4gUUt.76e4658abeb3.js.gz +0 -0
  119. codex/static_root/assets/VTable-CYv4gUUt.js +1 -0
  120. codex/static_root/assets/VTable-CYv4gUUt.js.br +0 -0
  121. codex/static_root/assets/VTable-CYv4gUUt.js.gz +0 -0
  122. codex/static_root/assets/VWindowItem-f8UkxXa_.1d3e9e78fc9b.js +1 -0
  123. codex/static_root/assets/VWindowItem-f8UkxXa_.1d3e9e78fc9b.js.br +0 -0
  124. codex/static_root/assets/VWindowItem-f8UkxXa_.1d3e9e78fc9b.js.gz +0 -0
  125. codex/static_root/assets/VWindowItem-f8UkxXa_.js +1 -0
  126. codex/static_root/assets/VWindowItem-f8UkxXa_.js.br +0 -0
  127. codex/static_root/assets/VWindowItem-f8UkxXa_.js.gz +0 -0
  128. codex/static_root/assets/admin-BmuWJcAB.b0b253aefd88.js +1 -0
  129. codex/static_root/assets/admin-BmuWJcAB.b0b253aefd88.js.br +0 -0
  130. codex/static_root/assets/admin-BmuWJcAB.b0b253aefd88.js.gz +0 -0
  131. codex/static_root/assets/admin-BmuWJcAB.js +1 -0
  132. codex/static_root/assets/admin-BmuWJcAB.js.br +0 -0
  133. codex/static_root/assets/admin-BmuWJcAB.js.gz +0 -0
  134. codex/static_root/assets/admin-Izb24SXM.504ea9d9515c.js +1 -0
  135. codex/static_root/assets/admin-Izb24SXM.504ea9d9515c.js.br +0 -0
  136. codex/static_root/assets/admin-Izb24SXM.504ea9d9515c.js.gz +0 -0
  137. codex/static_root/assets/admin-Izb24SXM.js +1 -0
  138. codex/static_root/assets/admin-Izb24SXM.js.br +0 -0
  139. codex/static_root/assets/admin-Izb24SXM.js.gz +0 -0
  140. codex/static_root/assets/admin-dm3bSP7O.ae1234cc0beb.css +1 -0
  141. codex/static_root/assets/admin-dm3bSP7O.ae1234cc0beb.css.br +0 -0
  142. codex/static_root/assets/admin-dm3bSP7O.ae1234cc0beb.css.gz +0 -0
  143. codex/static_root/assets/admin-dm3bSP7O.css +1 -0
  144. codex/static_root/assets/admin-dm3bSP7O.css.br +0 -0
  145. codex/static_root/assets/admin-dm3bSP7O.css.gz +0 -0
  146. codex/static_root/assets/admin-menu-C712OV-9.ecd1f2af4cf4.js +1 -0
  147. codex/static_root/assets/admin-menu-C712OV-9.ecd1f2af4cf4.js.br +0 -0
  148. codex/static_root/assets/admin-menu-C712OV-9.ecd1f2af4cf4.js.gz +0 -0
  149. codex/static_root/assets/admin-menu-C712OV-9.js +1 -0
  150. codex/static_root/assets/admin-menu-C712OV-9.js.br +0 -0
  151. codex/static_root/assets/admin-menu-C712OV-9.js.gz +0 -0
  152. codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.2536580dece1.js +1 -0
  153. codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.2536580dece1.js.br +0 -0
  154. codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.2536580dece1.js.gz +0 -0
  155. codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.js +1 -0
  156. codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.js.br +0 -0
  157. codex/static_root/assets/admin-settings-button-progress-Clv9yQ7G.js.gz +0 -0
  158. codex/static_root/assets/browser-CReWGhn_.521193ab0710.css +1 -0
  159. codex/static_root/assets/browser-CReWGhn_.521193ab0710.css.br +0 -0
  160. codex/static_root/assets/browser-CReWGhn_.521193ab0710.css.gz +0 -0
  161. codex/static_root/assets/browser-CReWGhn_.css +1 -0
  162. codex/static_root/assets/browser-CReWGhn_.css.br +0 -0
  163. codex/static_root/assets/browser-CReWGhn_.css.gz +0 -0
  164. codex/static_root/assets/browser-DiAN9Ipo.a5b62a3cbe1d.js +1 -0
  165. codex/static_root/assets/browser-DiAN9Ipo.a5b62a3cbe1d.js.br +0 -0
  166. codex/static_root/assets/browser-DiAN9Ipo.a5b62a3cbe1d.js.gz +0 -0
  167. codex/static_root/assets/browser-DiAN9Ipo.js +1 -0
  168. codex/static_root/assets/browser-DiAN9Ipo.js.br +0 -0
  169. codex/static_root/assets/browser-DiAN9Ipo.js.gz +0 -0
  170. codex/static_root/assets/change-password-dialog-BZPmAOML.73fdc23e13a4.js +1 -0
  171. codex/static_root/assets/change-password-dialog-BZPmAOML.73fdc23e13a4.js.br +0 -0
  172. codex/static_root/assets/change-password-dialog-BZPmAOML.73fdc23e13a4.js.gz +0 -0
  173. codex/static_root/assets/change-password-dialog-BZPmAOML.js +1 -0
  174. codex/static_root/assets/change-password-dialog-BZPmAOML.js.br +0 -0
  175. codex/static_root/assets/change-password-dialog-BZPmAOML.js.gz +0 -0
  176. codex/static_root/assets/confirm-dialog-CBLQOik_.f53acd0c622a.js +1 -0
  177. codex/static_root/assets/confirm-dialog-CBLQOik_.f53acd0c622a.js.br +0 -0
  178. codex/static_root/assets/confirm-dialog-CBLQOik_.f53acd0c622a.js.gz +0 -0
  179. codex/static_root/assets/confirm-dialog-CBLQOik_.js +1 -0
  180. codex/static_root/assets/confirm-dialog-CBLQOik_.js.br +0 -0
  181. codex/static_root/assets/confirm-dialog-CBLQOik_.js.gz +0 -0
  182. codex/static_root/assets/datetime-column-CU19rCbi.9edc0d2da736.js +1 -0
  183. codex/static_root/assets/datetime-column-CU19rCbi.9edc0d2da736.js.br +1 -0
  184. codex/static_root/assets/datetime-column-CU19rCbi.9edc0d2da736.js.gz +0 -0
  185. codex/static_root/assets/datetime-column-CU19rCbi.js +1 -0
  186. codex/static_root/assets/datetime-column-CU19rCbi.js.br +1 -0
  187. codex/static_root/assets/datetime-column-CU19rCbi.js.gz +0 -0
  188. codex/static_root/assets/{filter-ud5mPZpn.0f0a537959aa.js → filter-CZ7kkBLq.171b4fcba146.js} +1 -1
  189. codex/static_root/assets/filter-CZ7kkBLq.171b4fcba146.js.br +0 -0
  190. codex/static_root/assets/filter-CZ7kkBLq.171b4fcba146.js.gz +0 -0
  191. codex/static_root/assets/{filter-ud5mPZpn.js → filter-CZ7kkBLq.js} +1 -1
  192. codex/static_root/assets/filter-CZ7kkBLq.js.br +0 -0
  193. codex/static_root/assets/filter-CZ7kkBLq.js.gz +0 -0
  194. codex/static_root/assets/flag-tab-DHGfv0Q3.2b7ef394b850.css +1 -0
  195. codex/static_root/assets/flag-tab-DHGfv0Q3.2b7ef394b850.css.br +0 -0
  196. codex/static_root/assets/flag-tab-DHGfv0Q3.2b7ef394b850.css.gz +0 -0
  197. codex/static_root/assets/flag-tab-DHGfv0Q3.css +1 -0
  198. codex/static_root/assets/flag-tab-DHGfv0Q3.css.br +0 -0
  199. codex/static_root/assets/flag-tab-DHGfv0Q3.css.gz +0 -0
  200. codex/static_root/assets/flag-tab-kcBN36xe.631c48ab75fd.js +1 -0
  201. codex/static_root/assets/flag-tab-kcBN36xe.631c48ab75fd.js.br +0 -0
  202. codex/static_root/assets/flag-tab-kcBN36xe.631c48ab75fd.js.gz +0 -0
  203. codex/static_root/assets/flag-tab-kcBN36xe.js +1 -0
  204. codex/static_root/assets/flag-tab-kcBN36xe.js.br +0 -0
  205. codex/static_root/assets/flag-tab-kcBN36xe.js.gz +0 -0
  206. codex/static_root/assets/forwardRefs-DNAemYUk.06a315272ed9.js +1 -0
  207. codex/static_root/assets/forwardRefs-DNAemYUk.06a315272ed9.js.br +0 -0
  208. codex/static_root/assets/forwardRefs-DNAemYUk.06a315272ed9.js.gz +0 -0
  209. codex/static_root/assets/forwardRefs-DNAemYUk.js +1 -0
  210. codex/static_root/assets/forwardRefs-DNAemYUk.js.br +0 -0
  211. codex/static_root/assets/forwardRefs-DNAemYUk.js.gz +0 -0
  212. codex/static_root/assets/forwardRefs-zKOZsbbr.9b56b4459240.css +1 -0
  213. codex/static_root/assets/forwardRefs-zKOZsbbr.9b56b4459240.css.br +0 -0
  214. codex/static_root/assets/forwardRefs-zKOZsbbr.9b56b4459240.css.gz +0 -0
  215. codex/static_root/assets/forwardRefs-zKOZsbbr.css +1 -0
  216. codex/static_root/assets/forwardRefs-zKOZsbbr.css.br +0 -0
  217. codex/static_root/assets/forwardRefs-zKOZsbbr.css.gz +0 -0
  218. codex/static_root/assets/group-tab-D9gwAdJc.60fb0ffa734c.js +1 -0
  219. codex/static_root/assets/group-tab-D9gwAdJc.60fb0ffa734c.js.br +0 -0
  220. codex/static_root/assets/group-tab-D9gwAdJc.60fb0ffa734c.js.gz +0 -0
  221. codex/static_root/assets/group-tab-D9gwAdJc.js +1 -0
  222. codex/static_root/assets/group-tab-D9gwAdJc.js.br +0 -0
  223. codex/static_root/assets/group-tab-D9gwAdJc.js.gz +0 -0
  224. codex/static_root/assets/http-error-DEJmFobt.8c13689ae574.css +1 -0
  225. codex/static_root/assets/http-error-DEJmFobt.8c13689ae574.css.br +0 -0
  226. codex/static_root/assets/http-error-DEJmFobt.8c13689ae574.css.gz +0 -0
  227. codex/static_root/assets/http-error-DEJmFobt.css +1 -0
  228. codex/static_root/assets/http-error-DEJmFobt.css.br +0 -0
  229. codex/static_root/assets/http-error-DEJmFobt.css.gz +0 -0
  230. codex/static_root/assets/http-error-DmFqD2Xz.593d626b5f1b.js +1 -0
  231. codex/static_root/assets/http-error-DmFqD2Xz.593d626b5f1b.js.br +0 -0
  232. codex/static_root/assets/http-error-DmFqD2Xz.593d626b5f1b.js.gz +0 -0
  233. codex/static_root/assets/http-error-DmFqD2Xz.js +1 -0
  234. codex/static_root/assets/http-error-DmFqD2Xz.js.br +0 -0
  235. codex/static_root/assets/http-error-DmFqD2Xz.js.gz +0 -0
  236. codex/static_root/assets/index-hvGLIBbY.30ce8cca0c6d.js +1 -0
  237. codex/static_root/assets/index-hvGLIBbY.30ce8cca0c6d.js.br +0 -0
  238. codex/static_root/assets/index-hvGLIBbY.30ce8cca0c6d.js.gz +0 -0
  239. codex/static_root/assets/index-hvGLIBbY.js +1 -0
  240. codex/static_root/assets/index-hvGLIBbY.js.br +0 -0
  241. codex/static_root/assets/index-hvGLIBbY.js.gz +0 -0
  242. codex/static_root/assets/library-tab-CF-RAEgP.a0ad816fb994.js +1 -0
  243. codex/static_root/assets/library-tab-CF-RAEgP.a0ad816fb994.js.br +0 -0
  244. codex/static_root/assets/library-tab-CF-RAEgP.a0ad816fb994.js.gz +0 -0
  245. codex/static_root/assets/library-tab-CF-RAEgP.js +1 -0
  246. codex/static_root/assets/library-tab-CF-RAEgP.js.br +0 -0
  247. codex/static_root/assets/library-tab-CF-RAEgP.js.gz +0 -0
  248. codex/static_root/assets/{library-tab-Cat0MAK5.5911f022188e.css → library-tab-Ce6qaeCV.75baedb7e976.css} +1 -1
  249. codex/static_root/assets/library-tab-Ce6qaeCV.75baedb7e976.css.br +0 -0
  250. codex/static_root/assets/library-tab-Ce6qaeCV.75baedb7e976.css.gz +0 -0
  251. codex/static_root/assets/{library-tab-Cat0MAK5.css → library-tab-Ce6qaeCV.css} +1 -1
  252. codex/static_root/assets/library-tab-Ce6qaeCV.css.br +0 -0
  253. codex/static_root/assets/library-tab-Ce6qaeCV.css.gz +0 -0
  254. codex/static_root/assets/main-Dv8e0bMI.bd74f1ba1627.js +35 -0
  255. codex/static_root/assets/main-Dv8e0bMI.bd74f1ba1627.js.br +0 -0
  256. codex/static_root/assets/main-Dv8e0bMI.bd74f1ba1627.js.gz +0 -0
  257. codex/static_root/assets/main-Dv8e0bMI.js +35 -0
  258. codex/static_root/assets/main-Dv8e0bMI.js.br +0 -0
  259. codex/static_root/assets/main-Dv8e0bMI.js.gz +0 -0
  260. codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.04a4f5c451b4.js +1 -0
  261. codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.04a4f5c451b4.js.br +0 -0
  262. codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.04a4f5c451b4.js.gz +0 -0
  263. codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.js +1 -0
  264. codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.js.br +0 -0
  265. codex/static_root/assets/pager-full-pdf-BXd9Y5oQ.js.gz +0 -0
  266. codex/static_root/assets/pagination-toolbar-DSe0TkMm.css +1 -0
  267. codex/static_root/assets/pagination-toolbar-DSe0TkMm.css.br +0 -0
  268. codex/static_root/assets/pagination-toolbar-DSe0TkMm.css.gz +0 -0
  269. codex/static_root/assets/pagination-toolbar-DSe0TkMm.dcb0eba65827.css +1 -0
  270. codex/static_root/assets/pagination-toolbar-DSe0TkMm.dcb0eba65827.css.br +0 -0
  271. codex/static_root/assets/pagination-toolbar-DSe0TkMm.dcb0eba65827.css.gz +0 -0
  272. codex/static_root/assets/pagination-toolbar-D_whea1o.9bbd927058f1.js +1 -0
  273. codex/static_root/assets/pagination-toolbar-D_whea1o.9bbd927058f1.js.br +0 -0
  274. codex/static_root/assets/pagination-toolbar-D_whea1o.9bbd927058f1.js.gz +0 -0
  275. codex/static_root/assets/pagination-toolbar-D_whea1o.js +1 -0
  276. codex/static_root/assets/pagination-toolbar-D_whea1o.js.br +0 -0
  277. codex/static_root/assets/pagination-toolbar-D_whea1o.js.gz +0 -0
  278. codex/static_root/assets/pdf-doc-dnr5fHbU.fa1d7ed9255b.js +348 -0
  279. codex/static_root/assets/pdf-doc-dnr5fHbU.fa1d7ed9255b.js.br +0 -0
  280. codex/static_root/assets/pdf-doc-dnr5fHbU.fa1d7ed9255b.js.gz +0 -0
  281. codex/static_root/assets/pdf-doc-dnr5fHbU.js +348 -0
  282. codex/static_root/assets/pdf-doc-dnr5fHbU.js.br +0 -0
  283. codex/static_root/assets/pdf-doc-dnr5fHbU.js.gz +0 -0
  284. codex/static_root/assets/{reader-Dd2KmdCN.115d4bfe9280.css → reader-B3hgta6g.2ed32f08e906.css} +1 -1
  285. codex/static_root/assets/reader-B3hgta6g.2ed32f08e906.css.br +0 -0
  286. codex/static_root/assets/reader-B3hgta6g.2ed32f08e906.css.gz +0 -0
  287. codex/static_root/assets/{reader-Dd2KmdCN.css → reader-B3hgta6g.css} +1 -1
  288. codex/static_root/assets/reader-B3hgta6g.css.br +0 -0
  289. codex/static_root/assets/reader-B3hgta6g.css.gz +0 -0
  290. codex/static_root/assets/reader-Cn-ra_Dv.f38c0b419dec.js +2 -0
  291. codex/static_root/assets/reader-Cn-ra_Dv.f38c0b419dec.js.br +0 -0
  292. codex/static_root/assets/reader-Cn-ra_Dv.f38c0b419dec.js.gz +0 -0
  293. codex/static_root/assets/reader-Cn-ra_Dv.js +2 -0
  294. codex/static_root/assets/reader-Cn-ra_Dv.js.br +0 -0
  295. codex/static_root/assets/reader-Cn-ra_Dv.js.gz +0 -0
  296. codex/static_root/assets/relation-chips-fOPxWAMU.78e5fe70c014.js +1 -0
  297. codex/static_root/assets/relation-chips-fOPxWAMU.78e5fe70c014.js.br +0 -0
  298. codex/static_root/assets/relation-chips-fOPxWAMU.78e5fe70c014.js.gz +0 -0
  299. codex/static_root/assets/relation-chips-fOPxWAMU.js +1 -0
  300. codex/static_root/assets/relation-chips-fOPxWAMU.js.br +0 -0
  301. codex/static_root/assets/relation-chips-fOPxWAMU.js.gz +0 -0
  302. codex/static_root/assets/settings-drawer-1UEq8JY1.d95cee9aefb5.js +2 -0
  303. codex/static_root/assets/settings-drawer-1UEq8JY1.d95cee9aefb5.js.br +0 -0
  304. codex/static_root/assets/settings-drawer-1UEq8JY1.d95cee9aefb5.js.gz +0 -0
  305. codex/static_root/assets/settings-drawer-1UEq8JY1.js +2 -0
  306. codex/static_root/assets/settings-drawer-1UEq8JY1.js.br +0 -0
  307. codex/static_root/assets/settings-drawer-1UEq8JY1.js.gz +0 -0
  308. codex/static_root/assets/settings-drawer-CqJ4gXLq.75a6ea969e71.css +1 -0
  309. codex/static_root/assets/settings-drawer-CqJ4gXLq.75a6ea969e71.css.br +0 -0
  310. codex/static_root/assets/settings-drawer-CqJ4gXLq.75a6ea969e71.css.gz +0 -0
  311. codex/static_root/assets/settings-drawer-CqJ4gXLq.css +1 -0
  312. codex/static_root/assets/settings-drawer-CqJ4gXLq.css.br +0 -0
  313. codex/static_root/assets/settings-drawer-CqJ4gXLq.css.gz +0 -0
  314. codex/static_root/assets/stats-tab-0ptRCeKw.5514d3508e37.js +1 -0
  315. codex/static_root/assets/stats-tab-0ptRCeKw.5514d3508e37.js.br +0 -0
  316. codex/static_root/assets/stats-tab-0ptRCeKw.5514d3508e37.js.gz +0 -0
  317. codex/static_root/assets/stats-tab-0ptRCeKw.js +1 -0
  318. codex/static_root/assets/stats-tab-0ptRCeKw.js.br +0 -0
  319. codex/static_root/assets/stats-tab-0ptRCeKw.js.gz +0 -0
  320. codex/static_root/assets/task-tab-CG9_bi6Z.ad9f647f8e5f.js +1 -0
  321. codex/static_root/assets/task-tab-CG9_bi6Z.ad9f647f8e5f.js.br +0 -0
  322. codex/static_root/assets/task-tab-CG9_bi6Z.ad9f647f8e5f.js.gz +0 -0
  323. codex/static_root/assets/task-tab-CG9_bi6Z.js +1 -0
  324. codex/static_root/assets/task-tab-CG9_bi6Z.js.br +0 -0
  325. codex/static_root/assets/task-tab-CG9_bi6Z.js.gz +0 -0
  326. codex/static_root/assets/unauthorized-VnIXUO1w.c5536d9dae75.css +1 -0
  327. codex/static_root/assets/unauthorized-VnIXUO1w.c5536d9dae75.css.br +0 -0
  328. codex/static_root/assets/unauthorized-VnIXUO1w.c5536d9dae75.css.gz +0 -0
  329. codex/static_root/assets/unauthorized-VnIXUO1w.css +1 -0
  330. codex/static_root/assets/unauthorized-VnIXUO1w.css.br +0 -0
  331. codex/static_root/assets/unauthorized-VnIXUO1w.css.gz +0 -0
  332. codex/static_root/assets/unauthorized-vG1Lh0m_.0c4bae81ee09.js +1 -0
  333. codex/static_root/assets/unauthorized-vG1Lh0m_.0c4bae81ee09.js.br +0 -0
  334. codex/static_root/assets/unauthorized-vG1Lh0m_.0c4bae81ee09.js.gz +0 -0
  335. codex/static_root/assets/unauthorized-vG1Lh0m_.js +1 -0
  336. codex/static_root/assets/unauthorized-vG1Lh0m_.js.br +0 -0
  337. codex/static_root/assets/unauthorized-vG1Lh0m_.js.gz +0 -0
  338. codex/static_root/assets/user-tab-BOxWlpY7.1a94280a799b.js +1 -0
  339. codex/static_root/assets/user-tab-BOxWlpY7.1a94280a799b.js.br +0 -0
  340. codex/static_root/assets/user-tab-BOxWlpY7.1a94280a799b.js.gz +0 -0
  341. codex/static_root/assets/user-tab-BOxWlpY7.js +1 -0
  342. codex/static_root/assets/user-tab-BOxWlpY7.js.br +0 -0
  343. codex/static_root/assets/user-tab-BOxWlpY7.js.gz +0 -0
  344. codex/static_root/manifest.9b3479a6a4a6.json +735 -0
  345. codex/static_root/manifest.9b3479a6a4a6.json.br +0 -0
  346. codex/static_root/manifest.9b3479a6a4a6.json.gz +0 -0
  347. codex/static_root/manifest.json +365 -342
  348. codex/static_root/manifest.json.br +0 -0
  349. codex/static_root/manifest.json.gz +0 -0
  350. codex/static_root/staticfiles.json +1 -1
  351. codex/templates/index.html +1 -1
  352. codex/threads.py +8 -3
  353. codex/urls/api/admin.py +1 -0
  354. codex/urls/api/browser.py +1 -1
  355. codex/urls/opds/v2.py +6 -0
  356. codex/views/admin/flag.py +5 -1
  357. codex/views/admin/tasks.py +2 -0
  358. codex/views/bookmark.py +53 -41
  359. codex/views/browser/annotate/bookmark.py +6 -4
  360. codex/views/browser/annotate/card.py +1 -16
  361. codex/views/browser/annotate/order.py +45 -18
  362. codex/views/browser/bookmark.py +53 -0
  363. codex/views/browser/download.py +2 -0
  364. codex/views/browser/filters/bookmark.py +4 -32
  365. codex/views/browser/group_mtime.py +2 -2
  366. codex/views/browser/metadata/annotate.py +1 -1
  367. codex/views/browser/metadata/metadata.py +1 -1
  368. codex/views/browser/order_by.py +6 -1
  369. codex/views/frontend.py +19 -4
  370. codex/views/mixins.py +16 -28
  371. codex/views/opds/const.py +2 -0
  372. codex/views/opds/util.py +2 -2
  373. codex/views/opds/v1/entry/links.py +1 -2
  374. codex/views/opds/v2/feed.py +3 -2
  375. codex/views/opds/v2/href.py +60 -0
  376. codex/views/opds/v2/links.py +13 -47
  377. codex/views/opds/v2/progression.py +137 -0
  378. codex/views/opds/v2/publications.py +21 -1
  379. codex/views/opds/v2/top_links.py +2 -1
  380. codex/views/public.py +11 -4
  381. codex/views/reader/books.py +19 -8
  382. codex/views/reader/page.py +5 -5
  383. {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/METADATA +21 -26
  384. {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/RECORD +387 -370
  385. codex/serializers/opds/v2.py +0 -178
  386. codex/static_root/assets/VCheckbox-DLMzIII1.1ff8fc2d9a21.js +0 -1
  387. codex/static_root/assets/VCheckbox-DLMzIII1.1ff8fc2d9a21.js.br +0 -0
  388. codex/static_root/assets/VCheckbox-DLMzIII1.1ff8fc2d9a21.js.gz +0 -0
  389. codex/static_root/assets/VCheckbox-DLMzIII1.js +0 -1
  390. codex/static_root/assets/VCheckbox-DLMzIII1.js.br +0 -0
  391. codex/static_root/assets/VCheckbox-DLMzIII1.js.gz +0 -0
  392. codex/static_root/assets/VCheckboxBtn-BzoQl8XC.094679a3624b.js +0 -1
  393. codex/static_root/assets/VCheckboxBtn-BzoQl8XC.094679a3624b.js.br +0 -0
  394. codex/static_root/assets/VCheckboxBtn-BzoQl8XC.094679a3624b.js.gz +0 -0
  395. codex/static_root/assets/VCheckboxBtn-BzoQl8XC.js +0 -1
  396. codex/static_root/assets/VCheckboxBtn-BzoQl8XC.js.br +0 -0
  397. codex/static_root/assets/VCheckboxBtn-BzoQl8XC.js.gz +0 -0
  398. codex/static_root/assets/VCombobox-CBY8K5CP.fcf2221a9edd.js +0 -1
  399. codex/static_root/assets/VCombobox-CBY8K5CP.fcf2221a9edd.js.br +0 -0
  400. codex/static_root/assets/VCombobox-CBY8K5CP.fcf2221a9edd.js.gz +0 -0
  401. codex/static_root/assets/VCombobox-CBY8K5CP.js +0 -1
  402. codex/static_root/assets/VCombobox-CBY8K5CP.js.br +0 -0
  403. codex/static_root/assets/VCombobox-CBY8K5CP.js.gz +0 -0
  404. codex/static_root/assets/VDialog-DXQz0Ylp.7b23d86dbd0d.css +0 -1
  405. codex/static_root/assets/VDialog-DXQz0Ylp.7b23d86dbd0d.css.br +0 -0
  406. codex/static_root/assets/VDialog-DXQz0Ylp.7b23d86dbd0d.css.gz +0 -0
  407. codex/static_root/assets/VDialog-DXQz0Ylp.css +0 -1
  408. codex/static_root/assets/VDialog-DXQz0Ylp.css.br +0 -0
  409. codex/static_root/assets/VDialog-DXQz0Ylp.css.gz +0 -0
  410. codex/static_root/assets/VDialog-Db4tJYqR.e21a79a5030e.js +0 -1
  411. codex/static_root/assets/VDialog-Db4tJYqR.e21a79a5030e.js.br +0 -0
  412. codex/static_root/assets/VDialog-Db4tJYqR.e21a79a5030e.js.gz +0 -0
  413. codex/static_root/assets/VDialog-Db4tJYqR.js +0 -1
  414. codex/static_root/assets/VDialog-Db4tJYqR.js.br +0 -0
  415. codex/static_root/assets/VDialog-Db4tJYqR.js.gz +0 -0
  416. codex/static_root/assets/VExpansionPanels-DN4yGbOt.163e59e38865.js +0 -1
  417. codex/static_root/assets/VExpansionPanels-DN4yGbOt.163e59e38865.js.br +0 -0
  418. codex/static_root/assets/VExpansionPanels-DN4yGbOt.163e59e38865.js.gz +0 -0
  419. codex/static_root/assets/VExpansionPanels-DN4yGbOt.js +0 -1
  420. codex/static_root/assets/VExpansionPanels-DN4yGbOt.js.br +0 -0
  421. codex/static_root/assets/VExpansionPanels-DN4yGbOt.js.gz +0 -0
  422. codex/static_root/assets/VRadioGroup-D2-PWEfs.ce7644863c25.js +0 -1
  423. codex/static_root/assets/VRadioGroup-D2-PWEfs.ce7644863c25.js.br +0 -0
  424. codex/static_root/assets/VRadioGroup-D2-PWEfs.ce7644863c25.js.gz +0 -0
  425. codex/static_root/assets/VRadioGroup-D2-PWEfs.js +0 -1
  426. codex/static_root/assets/VRadioGroup-D2-PWEfs.js.br +0 -0
  427. codex/static_root/assets/VRadioGroup-D2-PWEfs.js.gz +0 -0
  428. codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css +0 -1
  429. codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.br +0 -0
  430. codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.gz +0 -0
  431. codex/static_root/assets/VSelect-DLu8bpcs.css +0 -1
  432. codex/static_root/assets/VSelect-DLu8bpcs.css.br +0 -0
  433. codex/static_root/assets/VSelect-DLu8bpcs.css.gz +0 -0
  434. codex/static_root/assets/VSelect-DqXJ23Pc.a1360cbed050.js +0 -1
  435. codex/static_root/assets/VSelect-DqXJ23Pc.a1360cbed050.js.br +0 -0
  436. codex/static_root/assets/VSelect-DqXJ23Pc.a1360cbed050.js.gz +0 -0
  437. codex/static_root/assets/VSelect-DqXJ23Pc.js +0 -1
  438. codex/static_root/assets/VSelect-DqXJ23Pc.js.br +0 -0
  439. codex/static_root/assets/VSelect-DqXJ23Pc.js.gz +0 -0
  440. codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css +0 -1
  441. codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.br +0 -0
  442. codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.gz +0 -0
  443. codex/static_root/assets/VSelectionControl-CiViEYoC.css +0 -1
  444. codex/static_root/assets/VSelectionControl-CiViEYoC.css.br +0 -0
  445. codex/static_root/assets/VSelectionControl-CiViEYoC.css.gz +0 -0
  446. codex/static_root/assets/VSelectionControl-CxhDNufT.dd7ca99567fc.js +0 -1
  447. codex/static_root/assets/VSelectionControl-CxhDNufT.dd7ca99567fc.js.br +0 -0
  448. codex/static_root/assets/VSelectionControl-CxhDNufT.dd7ca99567fc.js.gz +0 -0
  449. codex/static_root/assets/VSelectionControl-CxhDNufT.js +0 -1
  450. codex/static_root/assets/VSelectionControl-CxhDNufT.js.br +0 -0
  451. codex/static_root/assets/VSelectionControl-CxhDNufT.js.gz +0 -0
  452. codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css +0 -1
  453. codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.br +0 -0
  454. codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.gz +0 -0
  455. codex/static_root/assets/VSlideGroup-BtXPhMOT.css +0 -1
  456. codex/static_root/assets/VSlideGroup-BtXPhMOT.css.br +0 -0
  457. codex/static_root/assets/VSlideGroup-BtXPhMOT.css.gz +0 -0
  458. codex/static_root/assets/VSlideGroup-DRbthp6q.7365c5542d47.js +0 -1
  459. codex/static_root/assets/VSlideGroup-DRbthp6q.7365c5542d47.js.br +0 -0
  460. codex/static_root/assets/VSlideGroup-DRbthp6q.7365c5542d47.js.gz +0 -0
  461. codex/static_root/assets/VSlideGroup-DRbthp6q.js +0 -1
  462. codex/static_root/assets/VSlideGroup-DRbthp6q.js.br +0 -0
  463. codex/static_root/assets/VSlideGroup-DRbthp6q.js.gz +0 -0
  464. codex/static_root/assets/VTable-Cbq-yJos.c884706cac21.js +0 -1
  465. codex/static_root/assets/VTable-Cbq-yJos.c884706cac21.js.br +0 -0
  466. codex/static_root/assets/VTable-Cbq-yJos.c884706cac21.js.gz +0 -0
  467. codex/static_root/assets/VTable-Cbq-yJos.js +0 -1
  468. codex/static_root/assets/VTable-Cbq-yJos.js.br +0 -0
  469. codex/static_root/assets/VTable-Cbq-yJos.js.gz +0 -0
  470. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css +0 -1
  471. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.br +0 -0
  472. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.gz +0 -0
  473. codex/static_root/assets/VTextField-Crc3gdNh.css +0 -1
  474. codex/static_root/assets/VTextField-Crc3gdNh.css.br +0 -0
  475. codex/static_root/assets/VTextField-Crc3gdNh.css.gz +0 -0
  476. codex/static_root/assets/VTextField-X5yAw35c.155e276dd747.js +0 -1
  477. codex/static_root/assets/VTextField-X5yAw35c.155e276dd747.js.br +0 -0
  478. codex/static_root/assets/VTextField-X5yAw35c.155e276dd747.js.gz +0 -0
  479. codex/static_root/assets/VTextField-X5yAw35c.js +0 -1
  480. codex/static_root/assets/VTextField-X5yAw35c.js.br +0 -0
  481. codex/static_root/assets/VTextField-X5yAw35c.js.gz +0 -0
  482. codex/static_root/assets/VWindowItem-CRqhVh-9.cd73d01e0591.js +0 -1
  483. codex/static_root/assets/VWindowItem-CRqhVh-9.cd73d01e0591.js.br +0 -0
  484. codex/static_root/assets/VWindowItem-CRqhVh-9.cd73d01e0591.js.gz +0 -0
  485. codex/static_root/assets/VWindowItem-CRqhVh-9.js +0 -1
  486. codex/static_root/assets/VWindowItem-CRqhVh-9.js.br +0 -0
  487. codex/static_root/assets/VWindowItem-CRqhVh-9.js.gz +0 -0
  488. codex/static_root/assets/admin-BFvQwinN.6cfff21f780a.js +0 -1
  489. codex/static_root/assets/admin-BFvQwinN.6cfff21f780a.js.br +0 -0
  490. codex/static_root/assets/admin-BFvQwinN.6cfff21f780a.js.gz +0 -0
  491. codex/static_root/assets/admin-BFvQwinN.js +0 -1
  492. codex/static_root/assets/admin-BFvQwinN.js.br +0 -0
  493. codex/static_root/assets/admin-BFvQwinN.js.gz +0 -0
  494. codex/static_root/assets/admin-DJ7pIXDQ.css +0 -1
  495. codex/static_root/assets/admin-DJ7pIXDQ.css.br +0 -0
  496. codex/static_root/assets/admin-DJ7pIXDQ.css.gz +0 -0
  497. codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css +0 -1
  498. codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.br +0 -0
  499. codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.gz +0 -0
  500. codex/static_root/assets/admin-menu-Jfmp_Yd8.39fa7a18b3b4.js +0 -1
  501. codex/static_root/assets/admin-menu-Jfmp_Yd8.39fa7a18b3b4.js.br +0 -0
  502. codex/static_root/assets/admin-menu-Jfmp_Yd8.39fa7a18b3b4.js.gz +0 -0
  503. codex/static_root/assets/admin-menu-Jfmp_Yd8.js +0 -1
  504. codex/static_root/assets/admin-menu-Jfmp_Yd8.js.br +0 -0
  505. codex/static_root/assets/admin-menu-Jfmp_Yd8.js.gz +0 -0
  506. codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.cbf73c71330d.js +0 -1
  507. codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.cbf73c71330d.js.br +0 -0
  508. codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.cbf73c71330d.js.gz +0 -0
  509. codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.js +0 -1
  510. codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.js.br +0 -0
  511. codex/static_root/assets/admin-settings-button-progress-BAk-B1sR.js.gz +0 -0
  512. codex/static_root/assets/browser-CctlSJji.css +0 -1
  513. codex/static_root/assets/browser-CctlSJji.css.br +0 -0
  514. codex/static_root/assets/browser-CctlSJji.css.gz +0 -0
  515. codex/static_root/assets/browser-CctlSJji.d42d796476d2.css +0 -1
  516. codex/static_root/assets/browser-CctlSJji.d42d796476d2.css.br +0 -0
  517. codex/static_root/assets/browser-CctlSJji.d42d796476d2.css.gz +0 -0
  518. codex/static_root/assets/browser-DJnEeVHz.a78c47516b08.js +0 -1
  519. codex/static_root/assets/browser-DJnEeVHz.a78c47516b08.js.br +0 -0
  520. codex/static_root/assets/browser-DJnEeVHz.a78c47516b08.js.gz +0 -0
  521. codex/static_root/assets/browser-DJnEeVHz.js +0 -1
  522. codex/static_root/assets/browser-DJnEeVHz.js.br +0 -0
  523. codex/static_root/assets/browser-DJnEeVHz.js.gz +0 -0
  524. codex/static_root/assets/change-password-dialog-DJtOQw7K.be7fa5ec653d.js +0 -1
  525. codex/static_root/assets/change-password-dialog-DJtOQw7K.be7fa5ec653d.js.br +0 -0
  526. codex/static_root/assets/change-password-dialog-DJtOQw7K.be7fa5ec653d.js.gz +0 -0
  527. codex/static_root/assets/change-password-dialog-DJtOQw7K.js +0 -1
  528. codex/static_root/assets/change-password-dialog-DJtOQw7K.js.br +0 -0
  529. codex/static_root/assets/change-password-dialog-DJtOQw7K.js.gz +0 -0
  530. codex/static_root/assets/confirm-dialog-CME4gKSZ.1606233e784c.js +0 -1
  531. codex/static_root/assets/confirm-dialog-CME4gKSZ.1606233e784c.js.br +0 -0
  532. codex/static_root/assets/confirm-dialog-CME4gKSZ.1606233e784c.js.gz +0 -0
  533. codex/static_root/assets/confirm-dialog-CME4gKSZ.js +0 -1
  534. codex/static_root/assets/confirm-dialog-CME4gKSZ.js.br +0 -0
  535. codex/static_root/assets/confirm-dialog-CME4gKSZ.js.gz +0 -0
  536. codex/static_root/assets/datetime-column-D_F30OLW.09cdce0ef96e.js +0 -1
  537. codex/static_root/assets/datetime-column-D_F30OLW.09cdce0ef96e.js.br +0 -2
  538. codex/static_root/assets/datetime-column-D_F30OLW.09cdce0ef96e.js.gz +0 -0
  539. codex/static_root/assets/datetime-column-D_F30OLW.js +0 -1
  540. codex/static_root/assets/datetime-column-D_F30OLW.js.br +0 -2
  541. codex/static_root/assets/datetime-column-D_F30OLW.js.gz +0 -0
  542. codex/static_root/assets/filter-ud5mPZpn.0f0a537959aa.js.br +0 -0
  543. codex/static_root/assets/filter-ud5mPZpn.0f0a537959aa.js.gz +0 -0
  544. codex/static_root/assets/filter-ud5mPZpn.js.br +0 -0
  545. codex/static_root/assets/filter-ud5mPZpn.js.gz +0 -0
  546. codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css +0 -1
  547. codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.br +0 -0
  548. codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.gz +0 -0
  549. codex/static_root/assets/flag-tab-CqWovuBu.css +0 -1
  550. codex/static_root/assets/flag-tab-CqWovuBu.css.br +0 -0
  551. codex/static_root/assets/flag-tab-CqWovuBu.css.gz +0 -0
  552. codex/static_root/assets/flag-tab-k2g6u_vi.3e1da9318af9.js +0 -1
  553. codex/static_root/assets/flag-tab-k2g6u_vi.3e1da9318af9.js.br +0 -0
  554. codex/static_root/assets/flag-tab-k2g6u_vi.3e1da9318af9.js.gz +0 -0
  555. codex/static_root/assets/flag-tab-k2g6u_vi.js +0 -1
  556. codex/static_root/assets/flag-tab-k2g6u_vi.js.br +0 -0
  557. codex/static_root/assets/flag-tab-k2g6u_vi.js.gz +0 -0
  558. codex/static_root/assets/group-tab-C6Ykf03Z.ff0cc204013a.js +0 -1
  559. codex/static_root/assets/group-tab-C6Ykf03Z.ff0cc204013a.js.br +0 -0
  560. codex/static_root/assets/group-tab-C6Ykf03Z.ff0cc204013a.js.gz +0 -0
  561. codex/static_root/assets/group-tab-C6Ykf03Z.js +0 -1
  562. codex/static_root/assets/group-tab-C6Ykf03Z.js.br +0 -0
  563. codex/static_root/assets/group-tab-C6Ykf03Z.js.gz +0 -0
  564. codex/static_root/assets/http-error-Ctcws1xe.cfd378a792d2.css +0 -1
  565. codex/static_root/assets/http-error-Ctcws1xe.cfd378a792d2.css.br +0 -0
  566. codex/static_root/assets/http-error-Ctcws1xe.cfd378a792d2.css.gz +0 -0
  567. codex/static_root/assets/http-error-Ctcws1xe.css +0 -1
  568. codex/static_root/assets/http-error-Ctcws1xe.css.br +0 -0
  569. codex/static_root/assets/http-error-Ctcws1xe.css.gz +0 -0
  570. codex/static_root/assets/http-error-DQBHK8v5.aea50b1327fd.js +0 -1
  571. codex/static_root/assets/http-error-DQBHK8v5.aea50b1327fd.js.br +0 -0
  572. codex/static_root/assets/http-error-DQBHK8v5.aea50b1327fd.js.gz +0 -0
  573. codex/static_root/assets/http-error-DQBHK8v5.js +0 -1
  574. codex/static_root/assets/http-error-DQBHK8v5.js.br +0 -0
  575. codex/static_root/assets/http-error-DQBHK8v5.js.gz +0 -0
  576. codex/static_root/assets/index-ClGiHwWc.0717bacf5135.js +0 -1
  577. codex/static_root/assets/index-ClGiHwWc.0717bacf5135.js.br +0 -0
  578. codex/static_root/assets/index-ClGiHwWc.0717bacf5135.js.gz +0 -0
  579. codex/static_root/assets/index-ClGiHwWc.js +0 -1
  580. codex/static_root/assets/index-ClGiHwWc.js.br +0 -0
  581. codex/static_root/assets/index-ClGiHwWc.js.gz +0 -0
  582. codex/static_root/assets/library-tab-BpinbqAu.53fa2625e727.js +0 -1
  583. codex/static_root/assets/library-tab-BpinbqAu.53fa2625e727.js.br +0 -0
  584. codex/static_root/assets/library-tab-BpinbqAu.53fa2625e727.js.gz +0 -0
  585. codex/static_root/assets/library-tab-BpinbqAu.js +0 -1
  586. codex/static_root/assets/library-tab-BpinbqAu.js.br +0 -0
  587. codex/static_root/assets/library-tab-BpinbqAu.js.gz +0 -0
  588. codex/static_root/assets/library-tab-Cat0MAK5.5911f022188e.css.br +0 -0
  589. codex/static_root/assets/library-tab-Cat0MAK5.5911f022188e.css.gz +0 -0
  590. codex/static_root/assets/library-tab-Cat0MAK5.css.br +0 -0
  591. codex/static_root/assets/library-tab-Cat0MAK5.css.gz +0 -0
  592. codex/static_root/assets/main-BVKrDunc.1c6f8cdc3afb.js +0 -35
  593. codex/static_root/assets/main-BVKrDunc.1c6f8cdc3afb.js.br +0 -0
  594. codex/static_root/assets/main-BVKrDunc.1c6f8cdc3afb.js.gz +0 -0
  595. codex/static_root/assets/main-BVKrDunc.js +0 -35
  596. codex/static_root/assets/main-BVKrDunc.js.br +0 -0
  597. codex/static_root/assets/main-BVKrDunc.js.gz +0 -0
  598. codex/static_root/assets/pager-full-pdf-C1ZSyXfG.f2e95b19621c.js +0 -1
  599. codex/static_root/assets/pager-full-pdf-C1ZSyXfG.f2e95b19621c.js.br +0 -0
  600. codex/static_root/assets/pager-full-pdf-C1ZSyXfG.f2e95b19621c.js.gz +0 -0
  601. codex/static_root/assets/pager-full-pdf-C1ZSyXfG.js +0 -1
  602. codex/static_root/assets/pager-full-pdf-C1ZSyXfG.js.br +0 -0
  603. codex/static_root/assets/pager-full-pdf-C1ZSyXfG.js.gz +0 -0
  604. codex/static_root/assets/pagination-toolbar-Cx_9nh6D.546121fa7a0d.css +0 -1
  605. codex/static_root/assets/pagination-toolbar-Cx_9nh6D.546121fa7a0d.css.br +0 -0
  606. codex/static_root/assets/pagination-toolbar-Cx_9nh6D.546121fa7a0d.css.gz +0 -0
  607. codex/static_root/assets/pagination-toolbar-Cx_9nh6D.css +0 -1
  608. codex/static_root/assets/pagination-toolbar-Cx_9nh6D.css.br +0 -0
  609. codex/static_root/assets/pagination-toolbar-Cx_9nh6D.css.gz +0 -0
  610. codex/static_root/assets/pagination-toolbar-Diad0EdD.140911bf86af.js +0 -1
  611. codex/static_root/assets/pagination-toolbar-Diad0EdD.140911bf86af.js.br +0 -0
  612. codex/static_root/assets/pagination-toolbar-Diad0EdD.140911bf86af.js.gz +0 -0
  613. codex/static_root/assets/pagination-toolbar-Diad0EdD.js +0 -1
  614. codex/static_root/assets/pagination-toolbar-Diad0EdD.js.br +0 -0
  615. codex/static_root/assets/pagination-toolbar-Diad0EdD.js.gz +0 -0
  616. codex/static_root/assets/pdf-doc-b-GyNR6z.c9d18f0d36ed.js +0 -305
  617. codex/static_root/assets/pdf-doc-b-GyNR6z.c9d18f0d36ed.js.br +0 -0
  618. codex/static_root/assets/pdf-doc-b-GyNR6z.c9d18f0d36ed.js.gz +0 -0
  619. codex/static_root/assets/pdf-doc-b-GyNR6z.js +0 -305
  620. codex/static_root/assets/pdf-doc-b-GyNR6z.js.br +0 -0
  621. codex/static_root/assets/pdf-doc-b-GyNR6z.js.gz +0 -0
  622. codex/static_root/assets/reader-2up6WEuE.c56ad81d14c6.js +0 -2
  623. codex/static_root/assets/reader-2up6WEuE.c56ad81d14c6.js.br +0 -0
  624. codex/static_root/assets/reader-2up6WEuE.c56ad81d14c6.js.gz +0 -0
  625. codex/static_root/assets/reader-2up6WEuE.js +0 -2
  626. codex/static_root/assets/reader-2up6WEuE.js.br +0 -0
  627. codex/static_root/assets/reader-2up6WEuE.js.gz +0 -0
  628. codex/static_root/assets/reader-Dd2KmdCN.115d4bfe9280.css.br +0 -0
  629. codex/static_root/assets/reader-Dd2KmdCN.115d4bfe9280.css.gz +0 -0
  630. codex/static_root/assets/reader-Dd2KmdCN.css.br +0 -0
  631. codex/static_root/assets/reader-Dd2KmdCN.css.gz +0 -0
  632. codex/static_root/assets/relation-chips-piYv1IcD.f8a2ae79de89.js +0 -1
  633. codex/static_root/assets/relation-chips-piYv1IcD.f8a2ae79de89.js.br +0 -0
  634. codex/static_root/assets/relation-chips-piYv1IcD.f8a2ae79de89.js.gz +0 -0
  635. codex/static_root/assets/relation-chips-piYv1IcD.js +0 -1
  636. codex/static_root/assets/relation-chips-piYv1IcD.js.br +0 -0
  637. codex/static_root/assets/relation-chips-piYv1IcD.js.gz +0 -0
  638. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css +0 -1
  639. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.br +0 -0
  640. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.gz +0 -0
  641. codex/static_root/assets/settings-drawer-DKncO_vX.css +0 -1
  642. codex/static_root/assets/settings-drawer-DKncO_vX.css.br +0 -0
  643. codex/static_root/assets/settings-drawer-DKncO_vX.css.gz +0 -0
  644. codex/static_root/assets/settings-drawer-yVoY-WCS.1240cc47a246.js +0 -2
  645. codex/static_root/assets/settings-drawer-yVoY-WCS.1240cc47a246.js.br +0 -0
  646. codex/static_root/assets/settings-drawer-yVoY-WCS.1240cc47a246.js.gz +0 -0
  647. codex/static_root/assets/settings-drawer-yVoY-WCS.js +0 -2
  648. codex/static_root/assets/settings-drawer-yVoY-WCS.js.br +0 -0
  649. codex/static_root/assets/settings-drawer-yVoY-WCS.js.gz +0 -0
  650. codex/static_root/assets/stats-tab-bqSPLvTp.05106cb03ef0.js +0 -1
  651. codex/static_root/assets/stats-tab-bqSPLvTp.05106cb03ef0.js.br +0 -0
  652. codex/static_root/assets/stats-tab-bqSPLvTp.05106cb03ef0.js.gz +0 -0
  653. codex/static_root/assets/stats-tab-bqSPLvTp.js +0 -1
  654. codex/static_root/assets/stats-tab-bqSPLvTp.js.br +0 -0
  655. codex/static_root/assets/stats-tab-bqSPLvTp.js.gz +0 -0
  656. codex/static_root/assets/task-tab-DWPKaGPT.da4df28dde5c.js +0 -1
  657. codex/static_root/assets/task-tab-DWPKaGPT.da4df28dde5c.js.br +0 -0
  658. codex/static_root/assets/task-tab-DWPKaGPT.da4df28dde5c.js.gz +0 -0
  659. codex/static_root/assets/task-tab-DWPKaGPT.js +0 -1
  660. codex/static_root/assets/task-tab-DWPKaGPT.js.br +0 -0
  661. codex/static_root/assets/task-tab-DWPKaGPT.js.gz +0 -0
  662. codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css +0 -1
  663. codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.br +0 -4
  664. codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.gz +0 -0
  665. codex/static_root/assets/transition-5LRFTPo5.css +0 -1
  666. codex/static_root/assets/transition-5LRFTPo5.css.br +0 -4
  667. codex/static_root/assets/transition-5LRFTPo5.css.gz +0 -0
  668. codex/static_root/assets/transition-DWVQ2EZ3.0bdfaa37b937.js +0 -1
  669. codex/static_root/assets/transition-DWVQ2EZ3.0bdfaa37b937.js.br +0 -0
  670. codex/static_root/assets/transition-DWVQ2EZ3.0bdfaa37b937.js.gz +0 -0
  671. codex/static_root/assets/transition-DWVQ2EZ3.js +0 -1
  672. codex/static_root/assets/transition-DWVQ2EZ3.js.br +0 -0
  673. codex/static_root/assets/transition-DWVQ2EZ3.js.gz +0 -0
  674. codex/static_root/assets/unauthorized-CEGD20tR.064b0993a944.js +0 -1
  675. codex/static_root/assets/unauthorized-CEGD20tR.064b0993a944.js.br +0 -0
  676. codex/static_root/assets/unauthorized-CEGD20tR.064b0993a944.js.gz +0 -0
  677. codex/static_root/assets/unauthorized-CEGD20tR.js +0 -1
  678. codex/static_root/assets/unauthorized-CEGD20tR.js.br +0 -0
  679. codex/static_root/assets/unauthorized-CEGD20tR.js.gz +0 -0
  680. codex/static_root/assets/unauthorized-DyojW2ic.css +0 -1
  681. codex/static_root/assets/unauthorized-DyojW2ic.css.br +0 -0
  682. codex/static_root/assets/unauthorized-DyojW2ic.css.gz +0 -0
  683. codex/static_root/assets/unauthorized-DyojW2ic.feefa7628a23.css +0 -1
  684. codex/static_root/assets/unauthorized-DyojW2ic.feefa7628a23.css.br +0 -0
  685. codex/static_root/assets/unauthorized-DyojW2ic.feefa7628a23.css.gz +0 -0
  686. codex/static_root/assets/user-tab-EsokzMCB.b0acc46b56a3.js +0 -1
  687. codex/static_root/assets/user-tab-EsokzMCB.b0acc46b56a3.js.br +0 -0
  688. codex/static_root/assets/user-tab-EsokzMCB.b0acc46b56a3.js.gz +0 -0
  689. codex/static_root/assets/user-tab-EsokzMCB.js +0 -1
  690. codex/static_root/assets/user-tab-EsokzMCB.js.br +0 -0
  691. codex/static_root/assets/user-tab-EsokzMCB.js.gz +0 -0
  692. codex/static_root/manifest.4a38de3db1c5.json +0 -712
  693. codex/static_root/manifest.4a38de3db1c5.json.br +0 -0
  694. codex/static_root/manifest.4a38de3db1c5.json.gz +0 -0
  695. {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/LICENSE +0 -0
  696. {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/WHEEL +0 -0
  697. {codex-1.7.2.dist-info → codex-1.7.3a0.dist-info}/entry_points.txt +0 -0
@@ -19,7 +19,10 @@ class BrowserOrderByView(BrowserGroupMtimeView):
19
19
  if not self._order_key:
20
20
  order_key: str = self.params["order_by"]
21
21
  if (order_key == "search_score" and not self.fts_mode) or (
22
- order_key == "filename" and not self.admin_flags["folder_view"]
22
+ order_key == "filename"
23
+ and not self.admin_flags["folder_view"]
24
+ or order_key == "child_count"
25
+ and self.TARGET == "cover"
23
26
  ):
24
27
  order_key = "sort_name"
25
28
  self._order_key = order_key
@@ -29,6 +32,8 @@ class BrowserOrderByView(BrowserGroupMtimeView):
29
32
  """Order by for comics (and covers)."""
30
33
  if not order_key:
31
34
  order_key = self.order_key
35
+ if order_key == "child_count":
36
+ order_key = "sort_name"
32
37
  if order_key == "sort_name":
33
38
  if not comic_sort_names:
34
39
  comic_sort_names = self._comic_sort_names
codex/views/frontend.py CHANGED
@@ -6,6 +6,7 @@ from rest_framework.permissions import AllowAny
6
6
  from rest_framework.renderers import TemplateHTMLRenderer
7
7
  from rest_framework.response import Response
8
8
 
9
+ from codex.models.admin import AdminFlag
9
10
  from codex.serializers.route import RouteSerializer
10
11
  from codex.views.session import SessionView
11
12
 
@@ -19,10 +20,24 @@ class IndexView(SessionView):
19
20
  renderer_classes: ClassVar[list] = [TemplateHTMLRenderer] # type: ignore
20
21
  template_name = "index.html"
21
22
 
22
- def get(self, *_args, **_kwargs):
23
- """Get the app index page."""
23
+ def _get_last_route(self):
24
+ """Get the last route from the session."""
24
25
  last_route = self.get_last_route(name=False)
25
26
  serializer = RouteSerializer(last_route)
26
- last_route = serializer.data
27
- extra_context = {"last_route": last_route}
27
+ return serializer.data
28
+
29
+ def _get_title(self):
30
+ """Get the title from the banner text."""
31
+ bt_flag = AdminFlag.objects.get(key="BT")
32
+ title = "Codex"
33
+ if bt_flag.value:
34
+ title = bt_flag.value + " - " + title
35
+ return title
36
+
37
+ def get(self, *_args, **_kwargs):
38
+ """Get the app index page."""
39
+ extra_context = {
40
+ "last_route": self._get_last_route(),
41
+ "title": self._get_title(),
42
+ }
28
43
  return Response(extra_context)
codex/views/mixins.py CHANGED
@@ -2,40 +2,45 @@
2
2
 
3
3
  from django.db.models.expressions import F
4
4
 
5
+ from codex.logger.logging import get_logger
5
6
  from codex.models.comic import Comic, Imprint, Volume
6
7
  from codex.views.const import GROUP_NAME_MAP
7
8
 
9
+ LOG = get_logger(__name__)
8
10
  _SHOW_GROUPS = tuple(GROUP_NAME_MAP.keys())
11
+ _GROUP_NAME_TARGETS = frozenset({"browser", "opds1", "opds2", "reader"})
12
+ _VARIABLE_SHOW = "pi"
9
13
 
10
14
 
11
15
  class SharedAnnotationsMixin:
12
16
  """Cross view annotation methods."""
13
17
 
14
18
  @staticmethod
15
- def _get_order_groups(model_group, pks):
19
+ def _get_order_groups(parent_group, pks, show):
16
20
  """Annotate sort_name."""
17
- if not pks or len(pks) > 1:
18
- order_groups = _SHOW_GROUPS
19
- elif model_group == "c":
21
+ if parent_group == "c":
20
22
  order_groups = ()
21
23
  else:
22
24
  for index, nav_group in enumerate(_SHOW_GROUPS):
23
- if nav_group == model_group:
24
- order_groups = _SHOW_GROUPS[index + 1 :]
25
+ if nav_group in _VARIABLE_SHOW and not show.get(nav_group):
26
+ continue
27
+ if nav_group == parent_group:
28
+ watermark = index
29
+ if pks and len(pks) == 1:
30
+ watermark += 1
31
+ order_groups = _SHOW_GROUPS[watermark:]
25
32
  break
26
33
  else:
27
34
  order_groups = _SHOW_GROUPS
28
35
  return order_groups
29
36
 
30
37
  @classmethod
31
- def get_sort_name_annotations(cls, model, model_group, pks, show):
38
+ def get_sort_name_annotations(cls, model, parent_group, pks, show):
32
39
  """Annotate sort names for browser subclasses and reader."""
33
40
  sort_name_annotations = {}
34
41
  if model is Comic:
35
- order_groups = cls._get_order_groups(model_group, pks)
42
+ order_groups = cls._get_order_groups(parent_group, pks, show)
36
43
  for order_group in order_groups:
37
- if show and not show.get(order_group):
38
- continue
39
44
  group_name = GROUP_NAME_MAP[order_group]
40
45
  ann_name = f"{group_name}_sort_name"
41
46
  name_field = "name" if group_name == "volume" else "sort_name"
@@ -50,7 +55,7 @@ class SharedAnnotationsMixin:
50
55
  """Annotate name fields by hoisting them up."""
51
56
  # Optimized to only lookup what is used on the frontend
52
57
  target = cls.TARGET # type: ignore
53
- if target not in frozenset({"browser", "opds1", "opds2", "reader"}):
58
+ if target not in _GROUP_NAME_TARGETS:
54
59
  return qs
55
60
  group_names = {}
56
61
  if qs.model is Comic:
@@ -69,20 +74,3 @@ class SharedAnnotationsMixin:
69
74
  elif qs.model is Imprint:
70
75
  group_names["publisher_name"] = F("publisher__name")
71
76
  return qs.annotate(**group_names)
72
-
73
-
74
- class BookmarkSearchMixin:
75
- """Create Bookmark Search kwargs."""
76
-
77
- @staticmethod
78
- def get_bookmark_search_kwargs(auth_filter, comic_filter=None):
79
- """Get the search kwargs for a user's authentication state."""
80
- # search kwargs are relative to the bookmark object.
81
- search_kwargs = {}
82
- search_kwargs.update(auth_filter)
83
-
84
- if comic_filter:
85
- for key, value in comic_filter.items():
86
- search_kwargs[f"comic__{key}"] = value
87
-
88
- return search_kwargs
codex/views/opds/const.py CHANGED
@@ -28,6 +28,7 @@ class Rel:
28
28
  SORT_NEW = "http://opds-spec.org/sort/new"
29
29
  POPULAR = "http://opds-spec.org/sort/popular"
30
30
  FEATURED = "http://opds-spec.org/featured"
31
+ PROGRESSION = "http://www.cantook.com/api/progression"
31
32
  SELF = "self"
32
33
  START = "start"
33
34
  UP = "up"
@@ -55,6 +56,7 @@ class MimeType:
55
56
  STREAM = "image/jpeg"
56
57
  OPDS_JSON = "application/opds+json"
57
58
  OPDS_PUB = "application/opds-publication+json"
59
+ PROGRESSION = "application/vnd.readium.progression+json"
58
60
  BOOK = "http://schema.org/Book"
59
61
  # COMIC = "https://schema.org/ComicStory"
60
62
  JPEG = "image/jpeg"
codex/views/opds/util.py CHANGED
@@ -59,8 +59,8 @@ def get_m2m_objects(pks) -> dict:
59
59
  for model in OPDS_M2M_MODELS:
60
60
  table = model.__name__.lower()
61
61
  rel = GroupACLMixin.get_rel_prefix(model)
62
- comic_filter = {rel + "__in": pks}
63
- qs = model.objects.filter(**comic_filter).order_by("name").only("name")
62
+ comic_filter = {rel + "in": pks}
63
+ qs = model.objects.filter(**comic_filter).only("name").order_by("name")
64
64
  cats[table] = qs
65
65
 
66
66
  return cats
@@ -46,8 +46,7 @@ class OPDS1EntryLinksMixin:
46
46
  }
47
47
  href = reverse("opds:bin:cover", kwargs=kwargs)
48
48
  href = update_href_query_params(href, query_params)
49
- mime_type = "image/webp"
50
- return OPDS1Link(rel, href, mime_type)
49
+ return OPDS1Link(rel, href, MimeType.WEBP)
51
50
  except Exception:
52
51
  LOG.exception("create thumb")
53
52
 
@@ -8,7 +8,7 @@ from rest_framework.response import Response
8
8
  from codex.logger.logging import get_logger
9
9
  from codex.models import AdminFlag
10
10
  from codex.serializers.browser.settings import OPDSSettingsSerializer
11
- from codex.serializers.opds.v2 import OPDS2FeedSerializer
11
+ from codex.serializers.opds.v2.feed import OPDS2FeedSerializer
12
12
  from codex.views.const import MAX_OBJ_PER_PAGE
13
13
  from codex.views.opds.auth import OPDSAuthMixin
14
14
  from codex.views.opds.const import BLANK_TITLE
@@ -20,7 +20,8 @@ from codex.views.opds.v2.const import (
20
20
  TOP_NAV_GROUP_SECTION_DATA,
21
21
  NavigationGroup,
22
22
  )
23
- from codex.views.opds.v2.links import HrefData, LinkData
23
+ from codex.views.opds.v2.href import HrefData
24
+ from codex.views.opds.v2.links import LinkData
24
25
  from codex.views.opds.v2.publications import OPDS2PublicationView
25
26
 
26
27
  LOG = get_logger(__name__)
@@ -0,0 +1,60 @@
1
+ """Href methods for OPDS v2.0 Feed."""
2
+
3
+ from abc import ABC
4
+ from dataclasses import dataclass
5
+
6
+ from django.urls import reverse
7
+ from rest_framework.views import APIView
8
+
9
+ from codex.views.opds.util import update_href_query_params
10
+ from codex.views.util import pop_name
11
+
12
+
13
+ @dataclass
14
+ class HrefData:
15
+ """Data for creating hrefs."""
16
+
17
+ kwargs: dict | None = None
18
+ query_params: dict | None = None
19
+ absolute_query_params: bool = False
20
+ url_name: str | None = None
21
+ min_page: int | None = None
22
+ max_page: int | None = None
23
+
24
+
25
+ class OPDS2HrefMixin(APIView, ABC):
26
+ """Create links method."""
27
+
28
+ @property
29
+ def num_pages(self) -> int:
30
+ """Dummy."""
31
+ return 1
32
+
33
+ def _href_page_validate(self, kwargs, data):
34
+ """Validate the page bounds."""
35
+ min_page = 1 if data.min_page is None else data.min_page
36
+ max_page = self.num_pages if data.max_page is None else data.max_page
37
+ page = int(kwargs["page"])
38
+ return page >= min_page and page <= max_page
39
+
40
+ def _href_update_query_params(self, href, data):
41
+ """Update the query params."""
42
+ if data.absolute_query_params and data.query_params:
43
+ href = update_href_query_params(href, data.query_params)
44
+ elif hasattr(self, "request"):
45
+ # if request link and not init static links
46
+ href = update_href_query_params(
47
+ href, self.request.GET, new_query_params=data.query_params
48
+ )
49
+ return href
50
+
51
+ def href(self, data):
52
+ """Create an href."""
53
+ url_name = data.url_name if data.url_name else "opds:v2:feed"
54
+ kwargs = data.kwargs if data.kwargs is not None else self.kwargs
55
+ if "page" in kwargs and not self._href_page_validate(kwargs, data):
56
+ return None
57
+
58
+ kwargs = pop_name(kwargs)
59
+ href = reverse(url_name, kwargs=kwargs)
60
+ return self._href_update_query_params(href, data)
@@ -1,30 +1,17 @@
1
1
  """Links methods for OPDS v2.0 Feed."""
2
2
 
3
+ from collections.abc import Mapping
3
4
  from copy import deepcopy
4
5
  from dataclasses import dataclass
5
6
  from datetime import datetime
6
7
  from urllib.parse import parse_qsl, urlparse
7
8
 
8
9
  from django.db.models import QuerySet
9
- from django.urls import reverse
10
10
 
11
11
  from codex.settings.settings import FALSY
12
12
  from codex.views.browser.browser import BrowserView
13
13
  from codex.views.opds.const import MimeType, Rel
14
- from codex.views.opds.util import update_href_query_params
15
- from codex.views.util import pop_name
16
-
17
-
18
- @dataclass
19
- class HrefData:
20
- """Data for creating hrefs."""
21
-
22
- kwargs: dict | None = None
23
- query_params: dict | None = None
24
- absolute_query_params: bool = False
25
- url_name: str | None = None
26
- min_page: int | None = None
27
- max_page: int | None = None
14
+ from codex.views.opds.v2.href import HrefData, OPDS2HrefMixin
28
15
 
29
16
 
30
17
  @dataclass
@@ -40,9 +27,10 @@ class LinkData:
40
27
  width: int | None = None
41
28
  href: str | None = None
42
29
  num_items: int | None = None
30
+ authenticate: Mapping | None = None
43
31
 
44
32
 
45
- class OPDS2LinksView(BrowserView):
33
+ class OPDS2LinksView(OPDS2HrefMixin, BrowserView):
46
34
  """Links methods for OPDS 2.0 Feed."""
47
35
 
48
36
  def __init__(self, *args, **kwargs):
@@ -70,41 +58,12 @@ class OPDS2LinksView(BrowserView):
70
58
  self._num_pages = self.group_and_books[2]
71
59
  return self._num_pages
72
60
 
73
- def _href_page_validate(self, kwargs, data):
74
- """Validate the page bounds."""
75
- min_page = 1 if data.min_page is None else data.min_page
76
- max_page = self.num_pages if data.max_page is None else data.max_page
77
- page = int(kwargs["page"])
78
- return page >= min_page and page <= max_page
79
-
80
- def _href_update_query_params(self, href, data):
81
- """Update the query params."""
82
- if data.absolute_query_params and data.query_params:
83
- href = update_href_query_params(href, data.query_params)
84
- elif hasattr(self, "request"):
85
- # if request link and not init static links
86
- href = update_href_query_params(
87
- href, self.request.GET, new_query_params=data.query_params
88
- )
89
- return href
90
-
91
- def _href(self, data):
92
- """Create an href."""
93
- url_name = data.url_name if data.url_name else "opds:v2:feed"
94
- kwargs = data.kwargs if data.kwargs is not None else self.kwargs
95
- if "page" in kwargs and not self._href_page_validate(kwargs, data):
96
- return None
97
-
98
- kwargs = pop_name(kwargs)
99
- href = reverse(url_name, kwargs=kwargs)
100
- return self._href_update_query_params(href, data)
101
-
102
61
  def link(self, data):
103
62
  """Create a link element."""
104
63
  if data.href:
105
64
  href = data.href
106
65
  else:
107
- href = self._href(data.href_data)
66
+ href = self.href(data.href_data)
108
67
  if not href:
109
68
  return None
110
69
  mime_type = data.mime_type if data.mime_type else MimeType.OPDS_JSON
@@ -118,8 +77,15 @@ class OPDS2LinksView(BrowserView):
118
77
  link["height"] = data.height
119
78
  if data.width:
120
79
  link["width"] = data.width
80
+
81
+ # Properties
82
+ if data.num_items or data.authenticate:
83
+ link["properties"] = {}
121
84
  if data.num_items:
122
- link["properties"] = {"number_of_items": data.num_items}
85
+ link["properties"]["number_of_items"] = data.num_items
86
+ if data.authenticate:
87
+ link["properties"]["authenticate"] = data.authenticate
88
+
123
89
  return link
124
90
 
125
91
  @staticmethod
@@ -0,0 +1,137 @@
1
+ """OPDS 2 Progression view."""
2
+ # https://github.com/opds-community/drafts/discussions/67#discussioncomment-6414507
3
+
4
+ from types import MappingProxyType
5
+
6
+ from django.db.models.expressions import F, Value
7
+ from django.db.models.fields import FloatField
8
+ from django.db.models.functions.comparison import Cast, Coalesce, Greatest, Least
9
+ from django.db.models.query_utils import FilteredRelation
10
+ from rest_framework.exceptions import ValidationError
11
+ from rest_framework.response import Response
12
+
13
+ from codex.logger.logging import get_logger
14
+ from codex.serializers.opds.v2.progression import OPDS2ProgressionSerializer
15
+ from codex.util import max_none
16
+ from codex.views.auth import AuthFilterGenericAPIView
17
+ from codex.views.bookmark import BookmarkFilterMixin, BookmarkPageView
18
+ from codex.views.const import GROUP_MODEL_MAP
19
+ from codex.views.opds.v2.href import HrefData, OPDS2HrefMixin
20
+
21
+ _EMPTY_DEVICE = MappingProxyType(
22
+ {
23
+ "id": "",
24
+ "name": "",
25
+ }
26
+ )
27
+ LOG = get_logger(__name__)
28
+
29
+
30
+ # This is an independent api requiring a separate get.
31
+ class OPDS2ProgressionView(
32
+ BookmarkFilterMixin, OPDS2HrefMixin, BookmarkPageView, AuthFilterGenericAPIView
33
+ ):
34
+ """OPDS 2 Progression view."""
35
+
36
+ serializer_class = OPDS2ProgressionSerializer
37
+
38
+ @property
39
+ def modified(self):
40
+ """Get modified from bookmark."""
41
+ return self._obj.bookmark_updated_at # type: ignore
42
+
43
+ @property
44
+ def device(self):
45
+ """Dummy device."""
46
+ # Codex doesn't record this.
47
+ return _EMPTY_DEVICE
48
+
49
+ @property
50
+ def title(self):
51
+ """The locator title is the page number."""
52
+ return f"Page {self._obj.page}" # type: ignore
53
+
54
+ @property
55
+ def _progression_href(self):
56
+ """Build a Progression HRef."""
57
+ acq_kwargs = {
58
+ "pk": self._obj.pk,
59
+ "page": self._obj.page, # type: ignore
60
+ }
61
+ max_page = max_none(self._obj.page_count - 1, 0) # type: ignore
62
+ data = HrefData(
63
+ acq_kwargs,
64
+ url_name="opds:bin:page",
65
+ min_page=0,
66
+ max_page=max_page, # type: ignore
67
+ # absolute_query_params=True,
68
+ )
69
+ return self.href(data)
70
+
71
+ @property
72
+ def _locations(self):
73
+ """Build the Locations object."""
74
+ return {
75
+ "position": self._obj.page, # type: ignore
76
+ "progression": self._obj.progress, # type: ignore
77
+ "total_progression": self._obj.progress, # type: ignore
78
+ }
79
+
80
+ @property
81
+ def locator(self):
82
+ """Build the Locator object."""
83
+ return {
84
+ "title": self.title, # See publication.py:103
85
+ "href": self._progression_href,
86
+ "type": "image/jpeg",
87
+ "locations": self._locations,
88
+ }
89
+
90
+ def get_object(self):
91
+ """Build the progression data object."""
92
+ group = self.kwargs.get("group")
93
+ pk = self.kwargs.get("pk")
94
+
95
+ if not pk:
96
+ reason = f"Bad primary key for {group}:{pk}"
97
+ return ValidationError(reason, code="422")
98
+
99
+ model = GROUP_MODEL_MAP.get(group)
100
+ if not model:
101
+ reason = f"No model found for group {group}"
102
+ raise ValidationError(reason, code="422")
103
+
104
+ acl_filter = self.get_group_acl_filter(model, self.request.user)
105
+ qs = model.objects.filter(acl_filter).distinct()
106
+
107
+ bm_rel = self.get_bm_rel(model)
108
+ bm_filter = self.get_my_bookmark_filter(bm_rel)
109
+ progress = Least(
110
+ F("page") / Greatest(Cast(F("page_count"), FloatField()), 1.0), Value(1.0)
111
+ )
112
+
113
+ qs = qs.annotate(
114
+ my_bookmark=FilteredRelation("bookmark", condition=bm_filter),
115
+ bookmark_updated_at=F("my_bookmark__updated_at"),
116
+ page=Coalesce(F("my_bookmark__page"), 0),
117
+ progress=progress,
118
+ )
119
+ self._obj = qs.only("page_count").distinct().get(pk=pk)
120
+
121
+ return {
122
+ "modified": self.modified,
123
+ "device": self.device,
124
+ "locator": self.locator,
125
+ }
126
+
127
+ def get(self, *args, **kwargs):
128
+ """Get Response."""
129
+ try:
130
+ obj = self.get_object()
131
+ serializer = self.get_serializer(obj)
132
+ return Response(serializer.data)
133
+ except Exception:
134
+ LOG.exception("progression")
135
+ raise
136
+
137
+ # PUT handled by BookmarkPageView parent
@@ -10,7 +10,9 @@ from codex.models import Comic
10
10
  from codex.settings.settings import FALSY
11
11
  from codex.views.opds.const import AUTHOR_ROLES, MimeType, Rel
12
12
  from codex.views.opds.util import get_contributors, get_m2m_objects
13
- from codex.views.opds.v2.top_links import HrefData, LinkData, OPDS2TopLinksView
13
+ from codex.views.opds.v2.href import HrefData
14
+ from codex.views.opds.v2.links import LinkData
15
+ from codex.views.opds.v2.top_links import OPDS2TopLinksView
14
16
 
15
17
  _MD_CONTRIBUTOR_MAP = MappingProxyType(
16
18
  {
@@ -154,11 +156,29 @@ class OPDS2PublicationView(OPDS2TopLinksView):
154
156
  acq_href_data,
155
157
  mime_type=download_mime_type,
156
158
  )
159
+ prog_kwargs = {"group": "c", "pk": obj.pk}
160
+ prog_href_data = HrefData(prog_kwargs, url_name="opds:v2:position")
161
+
162
+ auth_href_data = HrefData({}, url_name="opds:authentication:v1")
163
+ auth_link_data = LinkData(
164
+ Rel.AUTHENTICATION,
165
+ auth_href_data,
166
+ mime_type=MimeType.AUTHENTICATION,
167
+ )
168
+ auth_link = self.link(auth_link_data)
169
+
170
+ prog_link_data = LinkData(
171
+ Rel.PROGRESSION,
172
+ prog_href_data,
173
+ mime_type=MimeType.PROGRESSION,
174
+ authenticate=auth_link,
175
+ )
157
176
  links = [
158
177
  # Comic streaming link (unsupported)
159
178
  # self.link(self_link_data),
160
179
  self.link(alt_link_data),
161
180
  self.link(acq_link_data),
181
+ self.link(prog_link_data),
162
182
  ]
163
183
  pub["links"] = links
164
184
 
@@ -1,7 +1,8 @@
1
1
  """OPDS v2.0 top links section methods."""
2
2
 
3
3
  from codex.views.opds.const import MimeType, Rel
4
- from codex.views.opds.v2.links import HrefData, LinkData, OPDS2LinksView
4
+ from codex.views.opds.v2.href import HrefData
5
+ from codex.views.opds.v2.links import LinkData, OPDS2LinksView
5
6
 
6
7
 
7
8
  class OPDS2TopLinksView(OPDS2LinksView):
codex/views/public.py CHANGED
@@ -13,6 +13,7 @@ _ADMIN_FLAG_KEYS = frozenset(
13
13
  {
14
14
  AdminFlag.FlagChoices.NON_USERS.value,
15
15
  AdminFlag.FlagChoices.REGISTRATION.value,
16
+ AdminFlag.FlagChoices.BANNER_TEXT.value,
16
17
  }
17
18
  )
18
19
 
@@ -21,16 +22,22 @@ class AdminFlagsView(GenericAPIView, RetrieveModelMixin):
21
22
  """Get admin flags relevant to auth."""
22
23
 
23
24
  serializer_class = AuthAdminFlagsSerializer
24
- queryset = AdminFlag.objects.filter(key__in=_ADMIN_FLAG_KEYS).only("key", "on")
25
+ queryset = AdminFlag.objects.filter(key__in=_ADMIN_FLAG_KEYS).only(
26
+ "key", "on", "value"
27
+ )
25
28
 
26
29
  def get_object(self):
27
30
  """Get admin flags."""
28
31
  flags = {}
29
32
  for obj in self.get_queryset(): # type: ignore
30
33
  name = ADMIN_FLAG_CHOICES[obj.key].lower().replace(" ", "_")
31
- flags[name] = obj.on
34
+ if obj.key == AdminFlag.FlagChoices.BANNER_TEXT.value:
35
+ val = obj.value
36
+ else:
37
+ val = obj.on
38
+ flags[name] = val
32
39
  return flags
33
40
 
34
- def get(self, request, *args, **kwargs):
41
+ def get(self, *args, **kwargs):
35
42
  """Get admin flags relevant to auth."""
36
- return self.retrieve(request, *args, **kwargs)
43
+ return self.retrieve(*args, **kwargs)
@@ -6,7 +6,7 @@ from django.db.models import F
6
6
  from django.db.models.query import Q
7
7
 
8
8
  from codex.models import Bookmark, Comic
9
- from codex.views.bookmark import BookmarkBaseView
9
+ from codex.views.bookmark import BookmarkAuthMixin
10
10
  from codex.views.browser.filters.field import ComicFieldFilterView
11
11
  from codex.views.const import (
12
12
  FOLDER_GROUP,
@@ -15,7 +15,7 @@ from codex.views.const import (
15
15
  NONE_INTEGERFIELD,
16
16
  STORY_ARC_GROUP,
17
17
  )
18
- from codex.views.mixins import BookmarkSearchMixin, SharedAnnotationsMixin
18
+ from codex.views.mixins import SharedAnnotationsMixin
19
19
  from codex.views.reader.params import ReaderParamsView
20
20
 
21
21
  if TYPE_CHECKING:
@@ -34,9 +34,7 @@ _COMIC_FIELDS = (
34
34
  )
35
35
 
36
36
 
37
- class ReaderBooksView(
38
- BookmarkBaseView, ReaderParamsView, SharedAnnotationsMixin, BookmarkSearchMixin
39
- ):
37
+ class ReaderBooksView(SharedAnnotationsMixin, BookmarkAuthMixin, ReaderParamsView):
40
38
  """Get Books methods."""
41
39
 
42
40
  def _get_reader_arc_pks(
@@ -81,10 +79,10 @@ class ReaderBooksView(
81
79
  """Get ordering for query."""
82
80
  sort_name_annotations = {}
83
81
  if arc_group in ("v", "s"):
82
+ parent_group = "i" if arc_group == "s" else "s"
84
83
  show = self.params["show"]
85
- model_group = "i" if arc_group == "s" else "s"
86
84
  sort_name_annotations = self.get_sort_name_annotations(
87
- model, model_group, arc_pks, show
85
+ model, parent_group, arc_pks, show
88
86
  )
89
87
  if sort_name_annotations and model is Comic:
90
88
  ordering += (*sort_name_annotations.keys(),)
@@ -149,6 +147,19 @@ class ReaderBooksView(
149
147
  qs = qs.order_by(*ordering)
150
148
  return qs, arc_group
151
149
 
150
+ @staticmethod
151
+ def _get_bookmark_filter(auth_filter, comic_filter=None):
152
+ """Get the search kwargs for a user's authentication state."""
153
+ # search kwargs are relative to the bookmark object.
154
+ search_kwargs = {}
155
+ search_kwargs.update(auth_filter)
156
+
157
+ if comic_filter:
158
+ for key, value in comic_filter.items():
159
+ search_kwargs[f"comic__{key}"] = value
160
+
161
+ return search_kwargs
162
+
152
163
  def _append_with_settings(self, book, bookmark_filter):
153
164
  """Append bookmark to book list."""
154
165
  book.settings = (
@@ -169,7 +180,7 @@ class ReaderBooksView(
169
180
  """
170
181
  comics, arc_group = self._get_comics_list()
171
182
  auth_filter = self.get_bookmark_auth_filter()
172
- bookmark_filter = self.get_bookmark_search_kwargs(auth_filter)
183
+ bookmark_filter = self._get_bookmark_filter(auth_filter)
173
184
  books = {}
174
185
  prev_book = None
175
186
  pk = self.kwargs.get("pk")