codex 1.4.0a1__py3-none-any.whl → 1.4.1__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 (259) hide show
  1. codex/config_default.yaml +12 -4
  2. codex/db_functions.py +4 -2
  3. codex/integrity.py +17 -6
  4. codex/librarian/covers/create.py +6 -8
  5. codex/librarian/importer/aggregate_metadata.py +75 -41
  6. codex/librarian/importer/clean_metadata.py +30 -7
  7. codex/librarian/importer/create_fks.py +154 -55
  8. codex/librarian/importer/deleted.py +11 -2
  9. codex/librarian/importer/failed_imports.py +41 -5
  10. codex/librarian/importer/importerd.py +34 -11
  11. codex/librarian/importer/link_comics.py +54 -31
  12. codex/librarian/importer/moved.py +55 -11
  13. codex/librarian/importer/query_fks.py +210 -48
  14. codex/librarian/importer/tasks.py +7 -7
  15. codex/librarian/janitor/cleanup.py +17 -5
  16. codex/librarian/librariand.py +10 -0
  17. codex/librarian/watchdog/events.py +11 -14
  18. codex/librarian/watchdog/observers.py +5 -1
  19. codex/logger/loggerd.py +7 -3
  20. codex/logger/logging.py +1 -1
  21. codex/migrations/0024_comic_gtin_comic_story_arc_number.py +24 -0
  22. codex/migrations/0025_add_story_arc_number.py +83 -0
  23. codex/models.py +21 -11
  24. codex/search/backend.py +1 -1
  25. codex/search/indexes.py +1 -1
  26. codex/serializers/browser.py +1 -0
  27. codex/serializers/metadata.py +5 -1
  28. codex/serializers/models.py +16 -1
  29. codex/serializers/opds/v1.py +1 -0
  30. codex/serializers/opds/v2.py +5 -2
  31. codex/serializers/reader.py +55 -16
  32. codex/settings/settings.py +1 -1
  33. codex/static_root/assets/admin-12749881.ef0f50bac290.js +41 -0
  34. codex/static_root/assets/admin-12749881.ef0f50bac290.js.br +0 -0
  35. codex/static_root/assets/admin-12749881.ef0f50bac290.js.gz +0 -0
  36. codex/static_root/assets/admin-12749881.js +41 -0
  37. codex/static_root/assets/admin-12749881.js.br +0 -0
  38. codex/static_root/assets/admin-12749881.js.gz +0 -0
  39. codex/static_root/assets/admin-beda768d.a614eee46307.css +1 -0
  40. codex/static_root/assets/admin-beda768d.a614eee46307.css.br +0 -0
  41. codex/static_root/assets/admin-beda768d.a614eee46307.css.gz +0 -0
  42. codex/static_root/assets/admin-beda768d.css +1 -0
  43. codex/static_root/assets/admin-beda768d.css.br +0 -0
  44. codex/static_root/assets/admin-beda768d.css.gz +0 -0
  45. codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css +1 -0
  46. codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.br +0 -0
  47. codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.gz +0 -0
  48. codex/static_root/assets/admin-drawer-panel-41c225cc.css +1 -0
  49. codex/static_root/assets/admin-drawer-panel-41c225cc.css.br +0 -0
  50. codex/static_root/assets/admin-drawer-panel-41c225cc.css.gz +0 -0
  51. codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js +1 -0
  52. codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.br +0 -0
  53. codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.gz +0 -0
  54. codex/static_root/assets/admin-drawer-panel-522f1e6c.js +1 -0
  55. codex/static_root/assets/admin-drawer-panel-522f1e6c.js.br +0 -0
  56. codex/static_root/assets/admin-drawer-panel-522f1e6c.js.gz +0 -0
  57. codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css +1 -0
  58. codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.br +0 -0
  59. codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.gz +0 -0
  60. codex/static_root/assets/browser-7f7d7134.css +1 -0
  61. codex/static_root/assets/browser-7f7d7134.css.br +0 -0
  62. codex/static_root/assets/browser-7f7d7134.css.gz +0 -0
  63. codex/static_root/assets/browser-af622672.d51aca96d64d.js +1 -0
  64. codex/static_root/assets/browser-af622672.d51aca96d64d.js.br +0 -0
  65. codex/static_root/assets/browser-af622672.d51aca96d64d.js.gz +0 -0
  66. codex/static_root/assets/browser-af622672.js +1 -0
  67. codex/static_root/assets/browser-af622672.js.br +0 -0
  68. codex/static_root/assets/browser-af622672.js.gz +0 -0
  69. codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js +1 -0
  70. codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.br +0 -0
  71. codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.gz +0 -0
  72. codex/static_root/assets/http-error-5e17b794.js +1 -0
  73. codex/static_root/assets/http-error-5e17b794.js.br +0 -0
  74. codex/static_root/assets/http-error-5e17b794.js.gz +0 -0
  75. codex/static_root/assets/main-9e76a4c3.6844a407d14c.js +1 -0
  76. codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.br +0 -0
  77. codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.gz +0 -0
  78. codex/static_root/assets/main-9e76a4c3.js +1 -0
  79. codex/static_root/assets/main-9e76a4c3.js.br +0 -0
  80. codex/static_root/assets/main-9e76a4c3.js.gz +0 -0
  81. codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js +1 -0
  82. codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.br +0 -0
  83. codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.gz +0 -0
  84. codex/static_root/assets/metadata-dialog-62c29ce0.js +1 -0
  85. codex/static_root/assets/metadata-dialog-62c29ce0.js.br +0 -0
  86. codex/static_root/assets/metadata-dialog-62c29ce0.js.gz +0 -0
  87. codex/static_root/assets/{metadata-dialog-785c4cfc.694a251cda37.css → metadata-dialog-cb306ffd.cc304996d7bb.css} +1 -1
  88. codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.br +0 -0
  89. codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.gz +0 -0
  90. codex/static_root/assets/{metadata-dialog-785c4cfc.css → metadata-dialog-cb306ffd.css} +1 -1
  91. codex/static_root/assets/metadata-dialog-cb306ffd.css.br +0 -0
  92. codex/static_root/assets/metadata-dialog-cb306ffd.css.gz +0 -0
  93. codex/static_root/assets/{page-pdf-c603e996.ab2d147c9ae1.js → page-pdf-157ba97e.613d7c2beb77.js} +61 -51
  94. codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.br +0 -0
  95. codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.gz +0 -0
  96. codex/static_root/assets/{page-pdf-c603e996.js → page-pdf-157ba97e.js} +61 -51
  97. codex/static_root/assets/page-pdf-157ba97e.js.br +0 -0
  98. codex/static_root/assets/page-pdf-157ba97e.js.gz +0 -0
  99. codex/static_root/assets/reader-36266549.0b2cf1291f27.js +1 -0
  100. codex/static_root/assets/reader-36266549.0b2cf1291f27.js.br +0 -0
  101. codex/static_root/assets/reader-36266549.0b2cf1291f27.js.gz +0 -0
  102. codex/static_root/assets/reader-36266549.js +1 -0
  103. codex/static_root/assets/reader-36266549.js.br +0 -0
  104. codex/static_root/assets/reader-36266549.js.gz +0 -0
  105. codex/static_root/assets/reader-7f004141.506eecc6954b.css +1 -0
  106. codex/static_root/assets/reader-7f004141.506eecc6954b.css.br +0 -0
  107. codex/static_root/assets/reader-7f004141.506eecc6954b.css.gz +0 -0
  108. codex/static_root/assets/reader-7f004141.css +1 -0
  109. codex/static_root/assets/reader-7f004141.css.br +0 -0
  110. codex/static_root/assets/reader-7f004141.css.gz +0 -0
  111. codex/static_root/js/choices.8c58714cf5b2.json +1 -0
  112. codex/static_root/js/choices.8c58714cf5b2.json.br +5 -0
  113. codex/static_root/js/choices.8c58714cf5b2.json.gz +0 -0
  114. codex/static_root/js/choices.json +1 -1
  115. codex/static_root/js/choices.json.br +0 -0
  116. codex/static_root/js/choices.json.gz +0 -0
  117. codex/static_root/{manifest.c0e270b2e6b6.json → manifest.d2f93a519ada.json} +32 -32
  118. codex/static_root/manifest.d2f93a519ada.json.br +0 -0
  119. codex/static_root/manifest.d2f93a519ada.json.gz +0 -0
  120. codex/static_root/manifest.json +32 -32
  121. codex/static_root/manifest.json.br +0 -0
  122. codex/static_root/manifest.json.gz +0 -0
  123. codex/static_root/staticfiles.json +1 -1
  124. codex/templates/headers-script-globals.html +1 -1
  125. codex/templates/{opds → opds_v1}/index.xml +3 -1
  126. codex/templates/{opds/opensearch.xml → opds_v1/opensearch_v1.xml} +1 -1
  127. codex/templates/search/indexes/codex/comic_text.txt +2 -2
  128. codex/urls/converters.py +1 -1
  129. codex/urls/opds/authentication.py +1 -1
  130. codex/urls/opds/root.py +8 -12
  131. codex/urls/opds/v1.py +12 -5
  132. codex/urls/opds/v2.py +2 -2
  133. codex/views/bookmark.py +2 -2
  134. codex/views/browser/base.py +23 -7
  135. codex/views/browser/browser.py +51 -41
  136. codex/views/browser/browser_annotations.py +159 -50
  137. codex/views/browser/browser_order_by.py +50 -106
  138. codex/views/browser/choices.py +75 -38
  139. codex/views/browser/filters/bookmark.py +6 -9
  140. codex/views/browser/filters/field.py +9 -6
  141. codex/views/browser/filters/group.py +12 -27
  142. codex/views/browser/filters/search.py +5 -10
  143. codex/views/browser/metadata.py +44 -19
  144. codex/views/download.py +1 -1
  145. codex/views/frontend.py +2 -3
  146. codex/views/mixins.py +15 -2
  147. codex/views/opds/const.py +8 -1
  148. codex/views/opds/util.py +37 -1
  149. codex/views/opds/v1/__init__.py +1 -1
  150. codex/views/opds/v1/data.py +21 -0
  151. codex/views/opds/v1/entry/__init__.py +1 -0
  152. codex/views/opds/v1/entry/data.py +23 -0
  153. codex/views/opds/v1/entry/entry.py +151 -0
  154. codex/views/opds/v1/entry/links.py +135 -0
  155. codex/views/opds/v1/facets.py +190 -0
  156. codex/views/opds/v1/feed.py +199 -0
  157. codex/views/opds/v1/links.py +198 -0
  158. codex/views/opds/{opensearch.py → v1/opensearch_v1.py} +3 -3
  159. codex/views/opds/v2/__init__.py +1 -1
  160. codex/views/opds/v2/const.py +10 -2
  161. codex/views/opds/v2/feed.py +82 -21
  162. codex/views/opds/v2/links.py +1 -1
  163. codex/views/opds/v2/publications.py +1 -1
  164. codex/views/opds/v2/top_links.py +1 -1
  165. codex/views/reader/page.py +6 -7
  166. codex/views/reader/reader.py +191 -61
  167. codex/views/session.py +2 -1
  168. {codex-1.4.0a1.dist-info → codex-1.4.1.dist-info}/METADATA +10 -41
  169. {codex-1.4.0a1.dist-info → codex-1.4.1.dist-info}/RECORD +172 -170
  170. codex/librarian/importer/db_ops.py +0 -251
  171. codex/pdf.py +0 -115
  172. codex/static_root/assets/admin-75c007ce.199fccf24c8d.js +0 -48
  173. codex/static_root/assets/admin-75c007ce.199fccf24c8d.js.br +0 -0
  174. codex/static_root/assets/admin-75c007ce.199fccf24c8d.js.gz +0 -0
  175. codex/static_root/assets/admin-75c007ce.js +0 -48
  176. codex/static_root/assets/admin-75c007ce.js.br +0 -0
  177. codex/static_root/assets/admin-75c007ce.js.gz +0 -0
  178. codex/static_root/assets/admin-848d48b1.5de8a0c45636.css +0 -1
  179. codex/static_root/assets/admin-848d48b1.5de8a0c45636.css.br +0 -0
  180. codex/static_root/assets/admin-848d48b1.5de8a0c45636.css.gz +0 -0
  181. codex/static_root/assets/admin-848d48b1.css +0 -1
  182. codex/static_root/assets/admin-848d48b1.css.br +0 -0
  183. codex/static_root/assets/admin-848d48b1.css.gz +0 -0
  184. codex/static_root/assets/admin-drawer-panel-a110c068.edf187333272.js +0 -1
  185. codex/static_root/assets/admin-drawer-panel-a110c068.edf187333272.js.br +0 -0
  186. codex/static_root/assets/admin-drawer-panel-a110c068.edf187333272.js.gz +0 -0
  187. codex/static_root/assets/admin-drawer-panel-a110c068.js +0 -1
  188. codex/static_root/assets/admin-drawer-panel-a110c068.js.br +0 -0
  189. codex/static_root/assets/admin-drawer-panel-a110c068.js.gz +0 -0
  190. codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css +0 -1
  191. codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.br +0 -2
  192. codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.gz +0 -0
  193. codex/static_root/assets/admin-drawer-panel-cce8c0aa.css +0 -1
  194. codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.br +0 -2
  195. codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.gz +0 -0
  196. codex/static_root/assets/browser-2c2380fd.8b515af7a743.js +0 -1
  197. codex/static_root/assets/browser-2c2380fd.8b515af7a743.js.br +0 -0
  198. codex/static_root/assets/browser-2c2380fd.8b515af7a743.js.gz +0 -0
  199. codex/static_root/assets/browser-2c2380fd.js +0 -1
  200. codex/static_root/assets/browser-2c2380fd.js.br +0 -0
  201. codex/static_root/assets/browser-2c2380fd.js.gz +0 -0
  202. codex/static_root/assets/browser-7325db61.css +0 -1
  203. codex/static_root/assets/browser-7325db61.css.br +0 -0
  204. codex/static_root/assets/browser-7325db61.css.gz +0 -0
  205. codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css +0 -1
  206. codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.br +0 -0
  207. codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.gz +0 -0
  208. codex/static_root/assets/http-error-402decbe.9ea8de1df13f.js +0 -1
  209. codex/static_root/assets/http-error-402decbe.9ea8de1df13f.js.br +0 -0
  210. codex/static_root/assets/http-error-402decbe.9ea8de1df13f.js.gz +0 -0
  211. codex/static_root/assets/http-error-402decbe.js +0 -1
  212. codex/static_root/assets/http-error-402decbe.js.br +0 -0
  213. codex/static_root/assets/http-error-402decbe.js.gz +0 -0
  214. codex/static_root/assets/main-a7f327e9.6641fe833335.js +0 -1
  215. codex/static_root/assets/main-a7f327e9.6641fe833335.js.br +0 -0
  216. codex/static_root/assets/main-a7f327e9.6641fe833335.js.gz +0 -0
  217. codex/static_root/assets/main-a7f327e9.js +0 -1
  218. codex/static_root/assets/main-a7f327e9.js.br +0 -0
  219. codex/static_root/assets/main-a7f327e9.js.gz +0 -0
  220. codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.br +0 -0
  221. codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.gz +0 -0
  222. codex/static_root/assets/metadata-dialog-785c4cfc.css.br +0 -0
  223. codex/static_root/assets/metadata-dialog-785c4cfc.css.gz +0 -0
  224. codex/static_root/assets/metadata-dialog-8a0bd8e1.c213b08d582f.js +0 -1
  225. codex/static_root/assets/metadata-dialog-8a0bd8e1.c213b08d582f.js.br +0 -0
  226. codex/static_root/assets/metadata-dialog-8a0bd8e1.c213b08d582f.js.gz +0 -0
  227. codex/static_root/assets/metadata-dialog-8a0bd8e1.js +0 -1
  228. codex/static_root/assets/metadata-dialog-8a0bd8e1.js.br +0 -0
  229. codex/static_root/assets/metadata-dialog-8a0bd8e1.js.gz +0 -0
  230. codex/static_root/assets/page-pdf-c603e996.ab2d147c9ae1.js.br +0 -0
  231. codex/static_root/assets/page-pdf-c603e996.ab2d147c9ae1.js.gz +0 -0
  232. codex/static_root/assets/page-pdf-c603e996.js.br +0 -0
  233. codex/static_root/assets/page-pdf-c603e996.js.gz +0 -0
  234. codex/static_root/assets/reader-c2965a5f.b011260169f7.js +0 -1
  235. codex/static_root/assets/reader-c2965a5f.b011260169f7.js.br +0 -0
  236. codex/static_root/assets/reader-c2965a5f.b011260169f7.js.gz +0 -0
  237. codex/static_root/assets/reader-c2965a5f.js +0 -1
  238. codex/static_root/assets/reader-c2965a5f.js.br +0 -0
  239. codex/static_root/assets/reader-c2965a5f.js.gz +0 -0
  240. codex/static_root/assets/reader-d8534888.2821de925986.css +0 -1
  241. codex/static_root/assets/reader-d8534888.2821de925986.css.br +0 -0
  242. codex/static_root/assets/reader-d8534888.2821de925986.css.gz +0 -0
  243. codex/static_root/assets/reader-d8534888.css +0 -1
  244. codex/static_root/assets/reader-d8534888.css.br +0 -0
  245. codex/static_root/assets/reader-d8534888.css.gz +0 -0
  246. codex/static_root/js/choices.6bfc2a3d293f.json +0 -1
  247. codex/static_root/js/choices.6bfc2a3d293f.json.br +0 -0
  248. codex/static_root/js/choices.6bfc2a3d293f.json.gz +0 -0
  249. codex/static_root/manifest.c0e270b2e6b6.json.br +0 -0
  250. codex/static_root/manifest.c0e270b2e6b6.json.gz +0 -0
  251. codex/urls/opds/opensearch.py +0 -18
  252. codex/views/opds/v1/browser.py +0 -346
  253. codex/views/opds/v1/entry.py +0 -278
  254. codex/views/opds/v1/start.py +0 -28
  255. codex/views/opds/v1/util.py +0 -162
  256. codex/views/opds/v2/start.py +0 -28
  257. {codex-1.4.0a1.dist-info → codex-1.4.1.dist-info}/LICENSE +0 -0
  258. {codex-1.4.0a1.dist-info → codex-1.4.1.dist-info}/WHEEL +0 -0
  259. {codex-1.4.0a1.dist-info → codex-1.4.1.dist-info}/entry_points.txt +0 -0
@@ -1 +0,0 @@
1
- .pageChangeColumn[data-v-29292846]{position:fixed;height:100%;width:33vw}.pageChangeRow[data-v-29292846]{position:fixed;height:33vh;width:100%}.prev[data-v-29292846]{left:0px;cursor:w-resize}.prevVertical[data-v-29292846]{top:0px;cursor:n-resize}.next[data-v-29292846]{right:0px;cursor:e-resize}.nextVertical[data-v-29292846]{bottom:0px;cursor:s-resize}.pageLoading[data-v-13c9bf0b]{display:inline-flex;height:100vh;width:100vw;justify-content:center;align-items:center}.twoPages[data-v-13c9bf0b]{max-width:50vw}.pagePlaceholder[data-v-13c9bf0b]{height:50%!important;width:50%!important}.pageError[data-v-3f1e3f4f]{display:inline-block;height:100vh}.twoPages[data-v-3f1e3f4f]{max-width:50vw}.pageErrorContainer[data-v-3f1e3f4f]{display:grid;place-items:center;height:100%}.pageErrorText[data-v-3f1e3f4f]{position:relative;text-align:center;font-size:4vw;color:rgb(var(--v-theme-textDisabled));padding:.5em}.pageErrorIcon[data-v-3f1e3f4f]{color:rgb(var(--v-theme-error));width:75%;height:75%;opacity:.33}.pageErrorText[data-v-3f1e3f4f],.pageErrorIcon[data-v-3f1e3f4f]{grid-row:1;grid-column:1}.fitToScreen[data-v-5997abff],.fitToScreenTwo[data-v-5997abff]{height:100vh;object-fit:contain}.fitToScreenTwoVertical[data-v-5997abff]{height:50vh;object-fit:contain}.fitToScreen[data-v-5997abff]{width:100vw}.fitToHeight[data-v-5997abff],.fitToHeightTwo[data-v-5997abff]{height:100vh}.fitToHeightTwoVertical[data-v-5997abff]{height:50vh}.fitToWidth[data-v-5997abff]{width:100vw}.fitToWidthTwo[data-v-5997abff]{width:50vw}.fitToWidthTwoVertical[data-v-5997abff]{width:100vw}.fitToScreenTwo[data-v-5997abff]{max-width:50vw}.fitToOrig[data-v-5997abff],.fitToOrigTwo[data-v-5997abff],.fitToOrigTwoVertical[data-v-5997abff]{object-fit:none}.pageHorizontal[data-v-f9e0354b]{display:inline;font-size:0}.pageVertical[data-v-f9e0354b]{display:block;font-size:0}.windowItem[data-v-da9ab604]{font-size:0}[data-v-91a60a25] .v-virtual-scroll__item{position:relative}.pageTracker[data-v-91a60a25]{position:absolute;top:0;left:0;z-index:15;width:95vw;height:100%}.v-virtual-scroll{display:block;flex:1 1 auto;max-width:100%;overflow:auto;position:relative}.v-virtual-scroll__container{display:block}.bookChangeColumn[data-v-2a8ea6a5]{position:fixed;height:100%;width:33vw}.prev[data-v-2a8ea6a5]{cursor:n-resize;left:0px}.next[data-v-2a8ea6a5]{cursor:s-resize;right:0px}[data-v-ca6f1824] .windowItem{min-height:100vh;text-align:center}[data-v-ca6f1824] .v-window__controls{position:fixed;top:48px;height:calc(100vh - 96px);padding:0}#readerTopToolbar[data-v-4075b1a2]{width:100%;top:0px;padding-top:env(safe-area-inset-top);padding-left:calc(env(safe-area-inset-left)/2);padding-right:calc(env(safe-area-inset-right)/2);z-index:20}[data-v-4075b1a2] .v-toolbar__content{padding:0}#toolbarTitle[data-v-4075b1a2]{font-size:clamp(8pt,2.5vw,18pt);line-height:normal}[data-v-4075b1a2] .v-toolbar-title__placeholder{text-overflow:clip;white-space:normal}#seriesPosition[data-v-4075b1a2]{padding-left:10px;padding-right:10px;color:rgb(var(--v-theme-textSecondary));text-align:center}#tagButton[data-v-4075b1a2]{min-width:24px;height:24px;width:24px;margin:0}@media (max-width: 959.98px){#closeBook[data-v-4075b1a2]{min-width:32px}#tagButton[data-v-4075b1a2]{padding-left:2px;padding-right:2px;min-width:16px}#settingsButton[data-v-4075b1a2]{padding-left:2px;padding-right:2px}#seriesPosition[data-v-4075b1a2]{padding-left:0;padding-right:0}}.prev[data-v-33b1e110]{cursor:n-resize;left:0px}.next[data-v-33b1e110]{cursor:s-resize;right:0px}.navLink[data-v-33b1e110]{display:block;height:100%}.bookChangeIcon[data-v-33b1e110]{top:50%;left:50%;transform:translate(-50%) translateY(-50%);height:25%;width:25%;color:#fff}.bookChangeDrawer{width:33vw!important;opacity:.75!important;z-index:15!important}#downloadPanel[data-v-c3777662]{background-color:rgb(var(--v-theme-background))}#readerSettings[data-v-72cc1f00]{padding-top:10px;padding-left:15px;padding-right:env(safe-area-inset-right);padding-bottom:10px;background-color:inherit}.displayRadioGroup[data-v-72cc1f00]{margin-top:15px}.displayTwoPages[data-v-72cc1f00]{margin-top:5px;margin-bottom:10px}.readRtlInReverse[data-v-72cc1f00]{transition:visibility .25s,opacity .25s}#clearSettingsButton[data-v-72cc1f00]{margin-bottom:4px;transition:visibility .25s,opacity .25s}.v-radio-group>.v-input__control{flex-direction:column}.v-radio-group>.v-input__control>.v-label{margin-inline-start:8px}.v-radio-group>.v-input__control>.v-label+.v-selection-control-group{margin-top:8px}#readerKeyboardShortcuts[data-v-41483eb9]{background-color:rgb(var(--v-theme-background))}#shortcutsTitle[data-v-41483eb9]{padding:0 10px 0 17px;font-size:16px}#keyboardIcon[data-v-41483eb9]{margin-right:.33em!important;color:rgb(var(--v-theme-textDisabled))}[data-v-41483eb9] .v-expansion-panel-title--active{min-height:48px!important}[data-v-41483eb9] .v-expansion-panel-text__wrapper{padding-left:10px;padding-right:10px}.highlight-table[data-v-41483eb9]{margin-top:10px;border-collapse:collapse;color:rgb(var(--v-theme-textDisabled))}h4[data-v-41483eb9]{padding-left:.5em;color:rgb(var(--v-theme-textSecondary))}.highlight-table tr[data-v-41483eb9]:nth-child(odd){background-color:rgb(var(--v-theme-surface))!important}.highlight-table td[data-v-41483eb9]{min-width:60px;padding:5px}.highlight-table td[data-v-41483eb9]:first-child{text-align:center}.keyIcon[data-v-41483eb9]{margin:0}#readerContainer[data-v-dff2aac6]{max-width:100%;position:relative}#announcement[data-v-dff2aac6]{text-align:center}
@@ -1 +0,0 @@
1
- .pageChangeColumn[data-v-29292846]{position:fixed;height:100%;width:33vw}.pageChangeRow[data-v-29292846]{position:fixed;height:33vh;width:100%}.prev[data-v-29292846]{left:0px;cursor:w-resize}.prevVertical[data-v-29292846]{top:0px;cursor:n-resize}.next[data-v-29292846]{right:0px;cursor:e-resize}.nextVertical[data-v-29292846]{bottom:0px;cursor:s-resize}.pageLoading[data-v-13c9bf0b]{display:inline-flex;height:100vh;width:100vw;justify-content:center;align-items:center}.twoPages[data-v-13c9bf0b]{max-width:50vw}.pagePlaceholder[data-v-13c9bf0b]{height:50%!important;width:50%!important}.pageError[data-v-3f1e3f4f]{display:inline-block;height:100vh}.twoPages[data-v-3f1e3f4f]{max-width:50vw}.pageErrorContainer[data-v-3f1e3f4f]{display:grid;place-items:center;height:100%}.pageErrorText[data-v-3f1e3f4f]{position:relative;text-align:center;font-size:4vw;color:rgb(var(--v-theme-textDisabled));padding:.5em}.pageErrorIcon[data-v-3f1e3f4f]{color:rgb(var(--v-theme-error));width:75%;height:75%;opacity:.33}.pageErrorText[data-v-3f1e3f4f],.pageErrorIcon[data-v-3f1e3f4f]{grid-row:1;grid-column:1}.fitToScreen[data-v-5997abff],.fitToScreenTwo[data-v-5997abff]{height:100vh;object-fit:contain}.fitToScreenTwoVertical[data-v-5997abff]{height:50vh;object-fit:contain}.fitToScreen[data-v-5997abff]{width:100vw}.fitToHeight[data-v-5997abff],.fitToHeightTwo[data-v-5997abff]{height:100vh}.fitToHeightTwoVertical[data-v-5997abff]{height:50vh}.fitToWidth[data-v-5997abff]{width:100vw}.fitToWidthTwo[data-v-5997abff]{width:50vw}.fitToWidthTwoVertical[data-v-5997abff]{width:100vw}.fitToScreenTwo[data-v-5997abff]{max-width:50vw}.fitToOrig[data-v-5997abff],.fitToOrigTwo[data-v-5997abff],.fitToOrigTwoVertical[data-v-5997abff]{object-fit:none}.pageHorizontal[data-v-f9e0354b]{display:inline;font-size:0}.pageVertical[data-v-f9e0354b]{display:block;font-size:0}.windowItem[data-v-da9ab604]{font-size:0}[data-v-91a60a25] .v-virtual-scroll__item{position:relative}.pageTracker[data-v-91a60a25]{position:absolute;top:0;left:0;z-index:15;width:95vw;height:100%}.v-virtual-scroll{display:block;flex:1 1 auto;max-width:100%;overflow:auto;position:relative}.v-virtual-scroll__container{display:block}.bookChangeColumn[data-v-2a8ea6a5]{position:fixed;height:100%;width:33vw}.prev[data-v-2a8ea6a5]{cursor:n-resize;left:0px}.next[data-v-2a8ea6a5]{cursor:s-resize;right:0px}[data-v-ca6f1824] .windowItem{min-height:100vh;text-align:center}[data-v-ca6f1824] .v-window__controls{position:fixed;top:48px;height:calc(100vh - 96px);padding:0}#readerTopToolbar[data-v-4075b1a2]{width:100%;top:0px;padding-top:env(safe-area-inset-top);padding-left:calc(env(safe-area-inset-left)/2);padding-right:calc(env(safe-area-inset-right)/2);z-index:20}[data-v-4075b1a2] .v-toolbar__content{padding:0}#toolbarTitle[data-v-4075b1a2]{font-size:clamp(8pt,2.5vw,18pt);line-height:normal}[data-v-4075b1a2] .v-toolbar-title__placeholder{text-overflow:clip;white-space:normal}#seriesPosition[data-v-4075b1a2]{padding-left:10px;padding-right:10px;color:rgb(var(--v-theme-textSecondary));text-align:center}#tagButton[data-v-4075b1a2]{min-width:24px;height:24px;width:24px;margin:0}@media (max-width: 959.98px){#closeBook[data-v-4075b1a2]{min-width:32px}#tagButton[data-v-4075b1a2]{padding-left:2px;padding-right:2px;min-width:16px}#settingsButton[data-v-4075b1a2]{padding-left:2px;padding-right:2px}#seriesPosition[data-v-4075b1a2]{padding-left:0;padding-right:0}}.prev[data-v-33b1e110]{cursor:n-resize;left:0px}.next[data-v-33b1e110]{cursor:s-resize;right:0px}.navLink[data-v-33b1e110]{display:block;height:100%}.bookChangeIcon[data-v-33b1e110]{top:50%;left:50%;transform:translate(-50%) translateY(-50%);height:25%;width:25%;color:#fff}.bookChangeDrawer{width:33vw!important;opacity:.75!important;z-index:15!important}#downloadPanel[data-v-c3777662]{background-color:rgb(var(--v-theme-background))}#readerSettings[data-v-72cc1f00]{padding-top:10px;padding-left:15px;padding-right:env(safe-area-inset-right);padding-bottom:10px;background-color:inherit}.displayRadioGroup[data-v-72cc1f00]{margin-top:15px}.displayTwoPages[data-v-72cc1f00]{margin-top:5px;margin-bottom:10px}.readRtlInReverse[data-v-72cc1f00]{transition:visibility .25s,opacity .25s}#clearSettingsButton[data-v-72cc1f00]{margin-bottom:4px;transition:visibility .25s,opacity .25s}.v-radio-group>.v-input__control{flex-direction:column}.v-radio-group>.v-input__control>.v-label{margin-inline-start:8px}.v-radio-group>.v-input__control>.v-label+.v-selection-control-group{margin-top:8px}#readerKeyboardShortcuts[data-v-41483eb9]{background-color:rgb(var(--v-theme-background))}#shortcutsTitle[data-v-41483eb9]{padding:0 10px 0 17px;font-size:16px}#keyboardIcon[data-v-41483eb9]{margin-right:.33em!important;color:rgb(var(--v-theme-textDisabled))}[data-v-41483eb9] .v-expansion-panel-title--active{min-height:48px!important}[data-v-41483eb9] .v-expansion-panel-text__wrapper{padding-left:10px;padding-right:10px}.highlight-table[data-v-41483eb9]{margin-top:10px;border-collapse:collapse;color:rgb(var(--v-theme-textDisabled))}h4[data-v-41483eb9]{padding-left:.5em;color:rgb(var(--v-theme-textSecondary))}.highlight-table tr[data-v-41483eb9]:nth-child(odd){background-color:rgb(var(--v-theme-surface))!important}.highlight-table td[data-v-41483eb9]{min-width:60px;padding:5px}.highlight-table td[data-v-41483eb9]:first-child{text-align:center}.keyIcon[data-v-41483eb9]{margin:0}#readerContainer[data-v-dff2aac6]{max-width:100%;position:relative}#announcement[data-v-dff2aac6]{text-align:center}
@@ -1 +0,0 @@
1
- {"browser":{"bookmarkFilter":[{"title":"All","value":"ALL","default":true},{"title":"Unread","value":"UNREAD"},{"title":"In Progress","value":"IN_PROGRESS"},{"title":"Read","value":"READ"}],"q":"","topGroup":[{"title":"Publishers","value":"p","default":true},{"title":"Imprints","value":"i"},{"title":"Series","value":"s"},{"title":"Volumes","value":"v"},{"title":"Issues","value":"c"},{"title":"Folder View","value":"f"}],"settingsGroup":[{"title":"Publishers","value":"p","default":true},{"title":"Imprints","value":"i"},{"title":"Series","value":"s","default":true},{"title":"Volumes","value":"v"}],"orderBy":[{"title":"Name","value":"sort_name","default":true},{"title":"Publish Date","value":"date"},{"title":"Added Time","value":"created_at"},{"title":"Updated Time","value":"updated_at"},{"title":"Page Count","value":"page_count"},{"title":"File Size","value":"size"},{"title":"Filename","value":"path"},{"title":"Critical Rating","value":"critical_rating"},{"title":"Community Rating","value":"community_rating"},{"title":"Age Rating","value":"age_rating"},{"title":"Search Score","value":"search_score"}],"vuetifyNullCode":-1,"groupNames":{"p":"Publishers","i":"Imprints","s":"Series","v":"Volumes","c":"Issues","f":"Folders"},"route":{"group":"r","pk":0,"page":1}},"reader":{"fitTo":[{"title":"Fit to Screen","value":"S","default":true},{"title":"Fit to Width","value":"W"},{"title":"Fit to Height","value":"H"},{"title":"Original Size","value":"O"},{"title":"","value":""}]},"websockets":{"LIBRARY_CHANGED":"LIBRARY_CHANGED","LIBRARIAN_STATUS":"LIBRARIAN_STATUS","FAILED_IMPORTS":"FAILED_IMPORTS"}}
@@ -1,18 +0,0 @@
1
- """codex:opds:v1 URL Configuration."""
2
- from django.urls import path
3
- from django.views.decorators.cache import cache_page
4
-
5
- from codex.views.opds.opensearch import OpenSearchView
6
-
7
- TIMEOUT = 60 * 60
8
-
9
- app_name = "opensearch"
10
-
11
-
12
- urlpatterns = [
13
- path(
14
- "/1.1",
15
- cache_page(TIMEOUT)(OpenSearchView.as_view()),
16
- name="1.1",
17
- ),
18
- ]
@@ -1,346 +0,0 @@
1
- """OPDS browser."""
2
-
3
- from datetime import datetime, timezone
4
-
5
- from django.urls import reverse
6
- from django.utils.http import urlencode
7
- from drf_spectacular.utils import extend_schema
8
- from rest_framework.authentication import BasicAuthentication, SessionAuthentication
9
- from rest_framework.response import Response
10
-
11
- from codex.logger.logging import get_logger
12
- from codex.serializers.opds.v1 import (
13
- OPDS1TemplateSerializer,
14
- )
15
- from codex.views.browser.browser import BrowserView
16
- from codex.views.opds.const import (
17
- BLANK_TITLE,
18
- FALSY,
19
- MimeType,
20
- Rel,
21
- )
22
- from codex.views.opds.util import update_href_query_params
23
- from codex.views.opds.v1.entry import OPDS1Entry, OPDS1EntryObject
24
- from codex.views.opds.v1.util import (
25
- DEFAULT_FACETS,
26
- Facet,
27
- FacetGroups,
28
- OPDSLink,
29
- OpdsNs,
30
- RootFacetGroups,
31
- RootTopLinks,
32
- TopLinks,
33
- UserAgents,
34
- )
35
- from codex.views.template import CodexXMLTemplateView
36
-
37
- LOG = get_logger(__name__)
38
-
39
-
40
- class OPDS1BrowserView(BrowserView, CodexXMLTemplateView):
41
- """The main opds browser."""
42
-
43
- authentication_classes = (BasicAuthentication, SessionAuthentication)
44
- template_name = "opds/index.xml"
45
- serializer_class = OPDS1TemplateSerializer
46
-
47
- @property
48
- def opds_ns(self):
49
- """Dynamic opds namespace."""
50
- try:
51
- return OpdsNs.ACQUISITION if self.is_aq_feed else OpdsNs.CATALOG
52
- except Exception as exc:
53
- LOG.exception(exc)
54
-
55
- @property
56
- def id_tag(self):
57
- """Feed id is the url."""
58
- try:
59
- return self.request.build_absolute_uri()
60
- except Exception as exc:
61
- LOG.exception(exc)
62
-
63
- @property
64
- def title(self):
65
- """Create the feed title."""
66
- result = ""
67
- try:
68
- if browser_title := self.obj.get("browser_title"):
69
- parent_name = browser_title.get("parent_name", "All")
70
- if not parent_name and self.kwargs.get("pk") == 0:
71
- parent_name = "All"
72
- group_name = browser_title.get("group_name")
73
- result = " ".join(filter(None, (parent_name, group_name))).strip()
74
-
75
- if not result:
76
- result = BLANK_TITLE
77
- except Exception as exc:
78
- LOG.exception(exc)
79
- return result
80
-
81
- @property
82
- def updated(self):
83
- """Hack in feed update time from cover timestamp."""
84
- try:
85
- if ts := self.obj.get("covers_timestamp"):
86
- return datetime.fromtimestamp(ts, tz=timezone.utc)
87
- except Exception as exc:
88
- LOG.exception(exc)
89
-
90
- @property
91
- def items_per_page(self):
92
- """Return opensearch:itemsPerPage."""
93
- try:
94
- if self.params.get("q"):
95
- return self.MAX_OBJ_PER_PAGE
96
- except Exception as exc:
97
- LOG.exception(exc)
98
-
99
- @property
100
- def total_results(self):
101
- """Return opensearch:totalResults."""
102
- try:
103
- if self.params.get("q"):
104
- return self.obj.get("total_count", 0)
105
- except Exception as exc:
106
- LOG.exception(exc)
107
-
108
- def _facet(self, kwargs, facet_group, facet_title, new_query_params):
109
- href = reverse("opds:v1:browser", kwargs=kwargs)
110
- facet_active = False
111
- for key, val in new_query_params.items():
112
- if self.request.query_params.get(key) == val:
113
- facet_active = True
114
- break
115
- href = update_href_query_params(
116
- href, self.request.query_params, new_query_params
117
- )
118
-
119
- title = " ".join(filter(None, (facet_group.title_prefix, facet_title))).strip()
120
- return OPDSLink(
121
- Rel.FACET,
122
- href,
123
- MimeType.NAV,
124
- title=title,
125
- facet_group=facet_group.query_param,
126
- facet_active=facet_active,
127
- )
128
-
129
- def _facet_entry(self, item, facet_group, facet, query_params):
130
- name = " ".join(
131
- filter(None, (facet_group.glyph, facet_group.title_prefix, facet.title))
132
- ).strip()
133
- entry_obj = OPDS1EntryObject(
134
- group=item.get("group"),
135
- pk=item.get("pk"),
136
- name=name,
137
- query_params=query_params,
138
- )
139
- issue_max = self.obj.get("issue_max")
140
- data = (self.acquisition_groups, issue_max, False)
141
- return OPDS1Entry(entry_obj, {**self.request.query_params}, data)
142
-
143
- def _is_facet_active(self, facet_group, facet):
144
- compare = [facet.value]
145
- default_val = DEFAULT_FACETS.get(facet_group.query_param)
146
- if facet.value == default_val:
147
- compare += [None]
148
- return self.request.query_params.get(facet_group.query_param) in compare
149
-
150
- def _facet_or_facet_entry(self, facet_group, facet, entries):
151
- # This logic preempts facet:activeFacet but no one uses it.
152
- # don't add default facets if in default mode.
153
- if self._is_facet_active(facet_group, facet):
154
- return None
155
-
156
- group = self.kwargs.get("group")
157
- if (
158
- facet_group.query_param == "topGroup"
159
- and (group == "f" and facet.value != "f")
160
- or (group != "f" and facet.value == "f")
161
- ):
162
- kwargs = {"group": facet.value, "pk": 0, "page": 1}
163
- else:
164
- kwargs = self.kwargs
165
-
166
- qps = {facet_group.query_param: facet.value}
167
- if entries and self.kwargs.get("page") == 1:
168
- facet = self._facet_entry(kwargs, facet_group, facet, qps)
169
- else:
170
- facet = self._facet(kwargs, facet_group, facet.title, qps)
171
- return facet
172
-
173
- def _facet_group(self, facet_group, entries):
174
- facets = []
175
- for facet in facet_group.facets:
176
- if facet_obj := self._facet_or_facet_entry(facet_group, facet, entries):
177
- facets += [facet_obj]
178
- return facets
179
-
180
- def _facets(self, entries=False, root=True):
181
- facets = []
182
- if not self.skip_order_facets:
183
- facets += self._facet_group(FacetGroups.ORDER_BY, entries)
184
- facets += self._facet_group(FacetGroups.ORDER_REVERSE, entries)
185
- if root:
186
- facets += self._facet_group(RootFacetGroups.TOP_GROUP, entries)
187
- if facet_obj := self._facet_or_facet_entry(
188
- RootFacetGroups.TOP_GROUP, Facet("f", "Folder View"), entries
189
- ):
190
- facets += [facet_obj]
191
- return facets
192
-
193
- def _nav_link(self, kwargs, rel):
194
- href = reverse("opds:v1:browser", kwargs={**kwargs})
195
- href = update_href_query_params(href, self.request.query_params)
196
- return OPDSLink(rel, href, MimeType.NAV)
197
-
198
- def _top_link(self, top_link):
199
- href = reverse("opds:v1:browser", kwargs={**top_link.kwargs, "page": 1})
200
- if top_link.query_params:
201
- href += "?" + urlencode(top_link.query_params, doseq=True)
202
- return OPDSLink(top_link.rel, href, top_link.mime_type)
203
-
204
- def _root_nav_links(self):
205
- """Navigation Root Links."""
206
- links = []
207
- if route := self.obj.get("up_route"):
208
- links += [self._nav_link(route, Rel.UP)]
209
- page = self.kwargs.get("page", 1)
210
- if page > 1:
211
- route = {**self.kwargs, "page": page - 1}
212
- links += [self._nav_link(route, Rel.PREV)]
213
- if page < self.obj.get("num_pages", 1):
214
- route = {**self.kwargs, "page": page + 1}
215
- links += [self._nav_link(route, Rel.NEXT)]
216
- return links
217
-
218
- def is_top_link_displayed(self, top_link):
219
- """Determine if this top link should be displayed."""
220
- for key, value in top_link.kwargs.items():
221
- if str(self.kwargs.get(key)) != str(value):
222
- return False
223
-
224
- for key, value in top_link.query_params.items():
225
- if str(self.request.query_params.get(key)) != str(value):
226
- return False
227
-
228
- return True
229
-
230
- @property
231
- def links(self):
232
- """Create all the links."""
233
- links = []
234
- try:
235
- mime_type = MimeType.ACQUISITION if self.is_aq_feed else MimeType.NAV
236
- links += [
237
- OPDSLink("self", self.request.get_full_path(), mime_type),
238
- OPDSLink(
239
- Rel.AUTHENTICATION,
240
- reverse("opds:authentication:v1"),
241
- MimeType.AUTHENTICATION,
242
- ),
243
- OPDSLink("search", reverse("opds:opensearch:1.1"), MimeType.OPENSEARCH),
244
- ]
245
- links += self._root_nav_links()
246
- if self.use_facets:
247
- for top_link in TopLinks.ALL + RootTopLinks.ALL:
248
- if not self.is_top_link_displayed(top_link):
249
- links += [self._top_link(top_link)]
250
- if facets := self._facets():
251
- links += facets
252
- except Exception as exc:
253
- LOG.exception(exc)
254
- return links
255
-
256
- def _top_link_entry(self, top_link):
257
- """Create a entry instead of a facet."""
258
- name = " ".join(filter(None, (top_link.glyph, top_link.title)))
259
- entry_obj = OPDS1EntryObject(
260
- **top_link.kwargs,
261
- name=name,
262
- query_params=top_link.query_params,
263
- summary=top_link.desc,
264
- )
265
- issue_max = self.obj.get("issue_max")
266
- data = (self.acquisition_groups, issue_max, False)
267
- return OPDS1Entry(entry_obj, {}, data)
268
-
269
- def _add_top_links(self, top_links):
270
- """Add a list of top links as entries if they should be enabled."""
271
- entries = []
272
- for tl in top_links:
273
- if not self.is_top_link_displayed(tl):
274
- entries += [self._top_link_entry(tl)]
275
- return entries
276
-
277
- def _get_entries_section(self, key, metadata):
278
- """Get entries by key section."""
279
- entries = []
280
- issue_max = self.obj.get("issue_max")
281
- data = (self.acquisition_groups, issue_max, metadata)
282
- if objs := self.obj.get(key):
283
- for obj in objs:
284
- entries += [OPDS1Entry(obj, self.request.query_params, data)]
285
- return entries
286
-
287
- @property
288
- def entries(self):
289
- """Create all the entries."""
290
- entries = []
291
- try:
292
- at_root = self.kwargs.get("pk") == 0
293
- if not self.use_facets and self.kwargs.get("page") == 1:
294
- entries += self._add_top_links(TopLinks.ALL)
295
- if at_root:
296
- entries += self._add_top_links(RootTopLinks.ALL)
297
- entries += self._facets(entries=True, root=at_root)
298
-
299
- entries += self._get_entries_section("groups", False)
300
- metadata = (
301
- self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
302
- )
303
- entries += self._get_entries_section("books", metadata)
304
- except Exception as exc:
305
- LOG.exception(exc)
306
- return entries
307
-
308
- def get_object(self):
309
- """Get the browser page and serialize it for this subclass."""
310
- group = self.kwargs.get("group")
311
- self.acquisition_groups = frozenset(self.valid_nav_groups[-2:])
312
- self.is_opds_1_acquisition = group in self.acquisition_groups
313
- self.is_opds_metadata = (
314
- self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
315
- )
316
- self.obj = super().get_object()
317
- self.is_aq_feed = self.obj.get("model_group") == "c"
318
- return self
319
-
320
- def _detect_user_agent(self):
321
- # Hacks for clients that don't support facets
322
- user_agent = self.request.headers.get("User-Agent")
323
- self.use_facets = False
324
- self.skip_order_facets = False
325
- if not user_agent:
326
- return
327
- for prefix in UserAgents.FACET_SUPPORT:
328
- if user_agent.startswith(prefix):
329
- self.use_facets = True
330
- break
331
- for prefix in UserAgents.CLIENT_REORDERS:
332
- if user_agent.startswith(prefix):
333
- self.skip_order_facets = True
334
- break
335
-
336
- @extend_schema(request=BrowserView.input_serializer_class)
337
- def get(self, *args, **kwargs):
338
- """Get the feed."""
339
- self.parse_params()
340
- self.validate_settings()
341
- self._detect_user_agent()
342
- self.skip_order_facets |= self.kwargs.get("group") == "c"
343
-
344
- obj = self.get_object()
345
- serializer = self.get_serializer(obj)
346
- return Response(serializer.data, content_type=self.content_type)