codex 1.4.0a0__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 (279) 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/coverd.py +3 -11
  5. codex/librarian/covers/create.py +15 -21
  6. codex/librarian/covers/tasks.py +2 -16
  7. codex/librarian/importer/aggregate_metadata.py +75 -41
  8. codex/librarian/importer/clean_metadata.py +30 -7
  9. codex/librarian/importer/create_fks.py +154 -55
  10. codex/librarian/importer/deleted.py +11 -2
  11. codex/librarian/importer/failed_imports.py +44 -5
  12. codex/librarian/importer/importerd.py +37 -12
  13. codex/librarian/importer/link_comics.py +54 -31
  14. codex/librarian/importer/moved.py +55 -11
  15. codex/librarian/importer/query_fks.py +210 -48
  16. codex/librarian/importer/tasks.py +7 -7
  17. codex/librarian/janitor/cleanup.py +17 -5
  18. codex/librarian/librariand.py +10 -0
  19. codex/librarian/watchdog/events.py +11 -14
  20. codex/librarian/watchdog/observers.py +5 -1
  21. codex/logger/loggerd.py +7 -3
  22. codex/logger/logging.py +1 -1
  23. codex/migrations/0024_comic_gtin_comic_story_arc_number.py +24 -0
  24. codex/migrations/0025_add_story_arc_number.py +83 -0
  25. codex/models.py +21 -11
  26. codex/search/backend.py +1 -1
  27. codex/search/indexes.py +1 -1
  28. codex/serializers/browser.py +1 -0
  29. codex/serializers/metadata.py +5 -1
  30. codex/serializers/models.py +16 -1
  31. codex/serializers/opds/v1.py +1 -0
  32. codex/serializers/opds/v2.py +5 -2
  33. codex/serializers/reader.py +55 -16
  34. codex/settings/settings.py +1 -1
  35. codex/static_root/assets/admin-12749881.ef0f50bac290.js +41 -0
  36. codex/static_root/assets/admin-12749881.ef0f50bac290.js.br +0 -0
  37. codex/static_root/assets/admin-12749881.ef0f50bac290.js.gz +0 -0
  38. codex/static_root/assets/admin-12749881.js +41 -0
  39. codex/static_root/assets/admin-12749881.js.br +0 -0
  40. codex/static_root/assets/admin-12749881.js.gz +0 -0
  41. codex/static_root/assets/admin-beda768d.a614eee46307.css +1 -0
  42. codex/static_root/assets/admin-beda768d.a614eee46307.css.br +0 -0
  43. codex/static_root/assets/admin-beda768d.a614eee46307.css.gz +0 -0
  44. codex/static_root/assets/admin-beda768d.css +1 -0
  45. codex/static_root/assets/admin-beda768d.css.br +0 -0
  46. codex/static_root/assets/admin-beda768d.css.gz +0 -0
  47. codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css +1 -0
  48. codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.br +0 -0
  49. codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.gz +0 -0
  50. codex/static_root/assets/admin-drawer-panel-41c225cc.css +1 -0
  51. codex/static_root/assets/admin-drawer-panel-41c225cc.css.br +0 -0
  52. codex/static_root/assets/admin-drawer-panel-41c225cc.css.gz +0 -0
  53. codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js +1 -0
  54. codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.br +0 -0
  55. codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.gz +0 -0
  56. codex/static_root/assets/admin-drawer-panel-522f1e6c.js +1 -0
  57. codex/static_root/assets/admin-drawer-panel-522f1e6c.js.br +0 -0
  58. codex/static_root/assets/admin-drawer-panel-522f1e6c.js.gz +0 -0
  59. codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css +1 -0
  60. codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.br +0 -0
  61. codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.gz +0 -0
  62. codex/static_root/assets/browser-7f7d7134.css +1 -0
  63. codex/static_root/assets/browser-7f7d7134.css.br +0 -0
  64. codex/static_root/assets/browser-7f7d7134.css.gz +0 -0
  65. codex/static_root/assets/browser-af622672.d51aca96d64d.js +1 -0
  66. codex/static_root/assets/browser-af622672.d51aca96d64d.js.br +0 -0
  67. codex/static_root/assets/browser-af622672.d51aca96d64d.js.gz +0 -0
  68. codex/static_root/assets/browser-af622672.js +1 -0
  69. codex/static_root/assets/browser-af622672.js.br +0 -0
  70. codex/static_root/assets/browser-af622672.js.gz +0 -0
  71. codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js +1 -0
  72. codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.br +0 -0
  73. codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.gz +0 -0
  74. codex/static_root/assets/http-error-5e17b794.js +1 -0
  75. codex/static_root/assets/http-error-5e17b794.js.br +0 -0
  76. codex/static_root/assets/http-error-5e17b794.js.gz +0 -0
  77. codex/static_root/assets/{main-a6ac9581.2fd9e52cbcc3.css → main-0898f4bb.181e0145c642.css} +1 -1
  78. codex/static_root/assets/main-0898f4bb.181e0145c642.css.br +0 -0
  79. codex/static_root/assets/{main-a6ac9581.2fd9e52cbcc3.css.gz → main-0898f4bb.181e0145c642.css.gz} +0 -0
  80. codex/static_root/assets/{main-a6ac9581.css → main-0898f4bb.css} +1 -1
  81. codex/static_root/assets/main-0898f4bb.css.br +0 -0
  82. codex/static_root/assets/{main-a6ac9581.css.gz → main-0898f4bb.css.gz} +0 -0
  83. codex/static_root/assets/main-9e76a4c3.6844a407d14c.js +1 -0
  84. codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.br +0 -0
  85. codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.gz +0 -0
  86. codex/static_root/assets/main-9e76a4c3.js +1 -0
  87. codex/static_root/assets/main-9e76a4c3.js.br +0 -0
  88. codex/static_root/assets/main-9e76a4c3.js.gz +0 -0
  89. codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js +1 -0
  90. codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.br +0 -0
  91. codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.gz +0 -0
  92. codex/static_root/assets/metadata-dialog-62c29ce0.js +1 -0
  93. codex/static_root/assets/metadata-dialog-62c29ce0.js.br +0 -0
  94. codex/static_root/assets/metadata-dialog-62c29ce0.js.gz +0 -0
  95. codex/static_root/assets/{metadata-dialog-785c4cfc.694a251cda37.css → metadata-dialog-cb306ffd.cc304996d7bb.css} +1 -1
  96. codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.br +0 -0
  97. codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.gz +0 -0
  98. codex/static_root/assets/{metadata-dialog-785c4cfc.css → metadata-dialog-cb306ffd.css} +1 -1
  99. codex/static_root/assets/metadata-dialog-cb306ffd.css.br +0 -0
  100. codex/static_root/assets/metadata-dialog-cb306ffd.css.gz +0 -0
  101. codex/static_root/assets/{page-pdf-abfd509d.3870dab8eaf4.js → page-pdf-157ba97e.613d7c2beb77.js} +61 -51
  102. codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.br +0 -0
  103. codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.gz +0 -0
  104. codex/static_root/assets/{page-pdf-abfd509d.js → page-pdf-157ba97e.js} +61 -51
  105. codex/static_root/assets/page-pdf-157ba97e.js.br +0 -0
  106. codex/static_root/assets/page-pdf-157ba97e.js.gz +0 -0
  107. codex/static_root/assets/reader-36266549.0b2cf1291f27.js +1 -0
  108. codex/static_root/assets/reader-36266549.0b2cf1291f27.js.br +0 -0
  109. codex/static_root/assets/reader-36266549.0b2cf1291f27.js.gz +0 -0
  110. codex/static_root/assets/reader-36266549.js +1 -0
  111. codex/static_root/assets/reader-36266549.js.br +0 -0
  112. codex/static_root/assets/reader-36266549.js.gz +0 -0
  113. codex/static_root/assets/reader-7f004141.506eecc6954b.css +1 -0
  114. codex/static_root/assets/reader-7f004141.506eecc6954b.css.br +0 -0
  115. codex/static_root/assets/reader-7f004141.506eecc6954b.css.gz +0 -0
  116. codex/static_root/assets/reader-7f004141.css +1 -0
  117. codex/static_root/assets/reader-7f004141.css.br +0 -0
  118. codex/static_root/assets/reader-7f004141.css.gz +0 -0
  119. codex/static_root/js/choices-admin.24cecf0a0568.json +1 -0
  120. codex/static_root/js/choices-admin.24cecf0a0568.json.br +0 -0
  121. codex/static_root/js/choices-admin.24cecf0a0568.json.gz +0 -0
  122. codex/static_root/js/choices-admin.json +1 -1
  123. codex/static_root/js/choices-admin.json.br +0 -0
  124. codex/static_root/js/choices-admin.json.gz +0 -0
  125. codex/static_root/js/choices.8c58714cf5b2.json +1 -0
  126. codex/static_root/js/choices.8c58714cf5b2.json.br +5 -0
  127. codex/static_root/js/choices.8c58714cf5b2.json.gz +0 -0
  128. codex/static_root/js/choices.json +1 -1
  129. codex/static_root/js/choices.json.br +0 -0
  130. codex/static_root/js/choices.json.gz +0 -0
  131. codex/static_root/{manifest.64a989215af8.json → manifest.d2f93a519ada.json} +34 -34
  132. codex/static_root/manifest.d2f93a519ada.json.br +0 -0
  133. codex/static_root/manifest.d2f93a519ada.json.gz +0 -0
  134. codex/static_root/manifest.json +34 -34
  135. codex/static_root/manifest.json.br +0 -0
  136. codex/static_root/manifest.json.gz +0 -0
  137. codex/static_root/staticfiles.json +1 -1
  138. codex/templates/headers-script-globals.html +1 -1
  139. codex/templates/{opds → opds_v1}/index.xml +3 -1
  140. codex/templates/{opds/opensearch.xml → opds_v1/opensearch_v1.xml} +1 -1
  141. codex/templates/search/indexes/codex/comic_text.txt +2 -2
  142. codex/urls/converters.py +1 -1
  143. codex/urls/opds/authentication.py +1 -1
  144. codex/urls/opds/root.py +8 -12
  145. codex/urls/opds/v1.py +12 -5
  146. codex/urls/opds/v2.py +2 -2
  147. codex/views/admin/tasks.py +6 -1
  148. codex/views/bookmark.py +2 -2
  149. codex/views/browser/base.py +23 -7
  150. codex/views/browser/browser.py +66 -56
  151. codex/views/browser/browser_annotations.py +159 -50
  152. codex/views/browser/browser_order_by.py +51 -105
  153. codex/views/browser/choices.py +75 -38
  154. codex/views/browser/filters/bookmark.py +6 -9
  155. codex/views/browser/filters/field.py +9 -6
  156. codex/views/browser/filters/group.py +12 -27
  157. codex/views/browser/filters/search.py +5 -10
  158. codex/views/browser/metadata.py +44 -19
  159. codex/views/download.py +1 -1
  160. codex/views/frontend.py +2 -3
  161. codex/views/mixins.py +15 -2
  162. codex/views/opds/const.py +8 -1
  163. codex/views/opds/util.py +37 -1
  164. codex/views/opds/v1/__init__.py +1 -1
  165. codex/views/opds/v1/data.py +21 -0
  166. codex/views/opds/v1/entry/__init__.py +1 -0
  167. codex/views/opds/v1/entry/data.py +23 -0
  168. codex/views/opds/v1/entry/entry.py +151 -0
  169. codex/views/opds/v1/entry/links.py +135 -0
  170. codex/views/opds/v1/facets.py +190 -0
  171. codex/views/opds/v1/feed.py +199 -0
  172. codex/views/opds/v1/links.py +198 -0
  173. codex/views/opds/{opensearch.py → v1/opensearch_v1.py} +3 -3
  174. codex/views/opds/v2/__init__.py +1 -1
  175. codex/views/opds/v2/const.py +10 -2
  176. codex/views/opds/v2/feed.py +82 -21
  177. codex/views/opds/v2/links.py +1 -1
  178. codex/views/opds/v2/publications.py +1 -1
  179. codex/views/opds/v2/top_links.py +1 -1
  180. codex/views/reader/page.py +6 -7
  181. codex/views/reader/reader.py +191 -61
  182. codex/views/session.py +2 -1
  183. {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/METADATA +10 -41
  184. {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/RECORD +187 -185
  185. codex/librarian/importer/db_ops.py +0 -248
  186. codex/pdf.py +0 -115
  187. codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js +0 -48
  188. codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js.br +0 -0
  189. codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js.gz +0 -0
  190. codex/static_root/assets/admin-73d93dc7.js +0 -48
  191. codex/static_root/assets/admin-73d93dc7.js.br +0 -0
  192. codex/static_root/assets/admin-73d93dc7.js.gz +0 -0
  193. codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css +0 -1
  194. codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css.br +0 -0
  195. codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css.gz +0 -0
  196. codex/static_root/assets/admin-79555229.css +0 -1
  197. codex/static_root/assets/admin-79555229.css.br +0 -0
  198. codex/static_root/assets/admin-79555229.css.gz +0 -0
  199. codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js +0 -1
  200. codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js.br +0 -0
  201. codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js.gz +0 -0
  202. codex/static_root/assets/admin-drawer-panel-64bcc083.js +0 -1
  203. codex/static_root/assets/admin-drawer-panel-64bcc083.js.br +0 -0
  204. codex/static_root/assets/admin-drawer-panel-64bcc083.js.gz +0 -0
  205. codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css +0 -1
  206. codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.br +0 -2
  207. codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.gz +0 -0
  208. codex/static_root/assets/admin-drawer-panel-cce8c0aa.css +0 -1
  209. codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.br +0 -2
  210. codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.gz +0 -0
  211. codex/static_root/assets/browser-7325db61.css +0 -1
  212. codex/static_root/assets/browser-7325db61.css.br +0 -0
  213. codex/static_root/assets/browser-7325db61.css.gz +0 -0
  214. codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css +0 -1
  215. codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.br +0 -0
  216. codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.gz +0 -0
  217. codex/static_root/assets/browser-d2caeed7.2262000a6d55.js +0 -1
  218. codex/static_root/assets/browser-d2caeed7.2262000a6d55.js.br +0 -0
  219. codex/static_root/assets/browser-d2caeed7.2262000a6d55.js.gz +0 -0
  220. codex/static_root/assets/browser-d2caeed7.js +0 -1
  221. codex/static_root/assets/browser-d2caeed7.js.br +0 -0
  222. codex/static_root/assets/browser-d2caeed7.js.gz +0 -0
  223. codex/static_root/assets/http-error-0221c37d.480d5066da92.js +0 -1
  224. codex/static_root/assets/http-error-0221c37d.480d5066da92.js.br +0 -0
  225. codex/static_root/assets/http-error-0221c37d.480d5066da92.js.gz +0 -0
  226. codex/static_root/assets/http-error-0221c37d.js +0 -1
  227. codex/static_root/assets/http-error-0221c37d.js.br +0 -0
  228. codex/static_root/assets/http-error-0221c37d.js.gz +0 -0
  229. codex/static_root/assets/main-a6ac9581.2fd9e52cbcc3.css.br +0 -0
  230. codex/static_root/assets/main-a6ac9581.css.br +0 -0
  231. codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js +0 -1
  232. codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js.br +0 -0
  233. codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js.gz +0 -0
  234. codex/static_root/assets/main-e33dcfb0.js +0 -1
  235. codex/static_root/assets/main-e33dcfb0.js.br +0 -0
  236. codex/static_root/assets/main-e33dcfb0.js.gz +0 -0
  237. codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.br +0 -0
  238. codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.gz +0 -0
  239. codex/static_root/assets/metadata-dialog-785c4cfc.css.br +0 -0
  240. codex/static_root/assets/metadata-dialog-785c4cfc.css.gz +0 -0
  241. codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js +0 -1
  242. codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js.br +0 -0
  243. codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js.gz +0 -0
  244. codex/static_root/assets/metadata-dialog-8b0e8aaa.js +0 -1
  245. codex/static_root/assets/metadata-dialog-8b0e8aaa.js.br +0 -0
  246. codex/static_root/assets/metadata-dialog-8b0e8aaa.js.gz +0 -0
  247. codex/static_root/assets/page-pdf-abfd509d.3870dab8eaf4.js.br +0 -0
  248. codex/static_root/assets/page-pdf-abfd509d.3870dab8eaf4.js.gz +0 -0
  249. codex/static_root/assets/page-pdf-abfd509d.js.br +0 -0
  250. codex/static_root/assets/page-pdf-abfd509d.js.gz +0 -0
  251. codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css +0 -1
  252. codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css.br +0 -0
  253. codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css.gz +0 -0
  254. codex/static_root/assets/reader-a8b8f766.css +0 -1
  255. codex/static_root/assets/reader-a8b8f766.css.br +0 -0
  256. codex/static_root/assets/reader-a8b8f766.css.gz +0 -0
  257. codex/static_root/assets/reader-fe9345d2.759c31f82998.js +0 -1
  258. codex/static_root/assets/reader-fe9345d2.759c31f82998.js.br +0 -0
  259. codex/static_root/assets/reader-fe9345d2.759c31f82998.js.gz +0 -0
  260. codex/static_root/assets/reader-fe9345d2.js +0 -1
  261. codex/static_root/assets/reader-fe9345d2.js.br +0 -0
  262. codex/static_root/assets/reader-fe9345d2.js.gz +0 -0
  263. codex/static_root/js/choices-admin.3d958ea7f83b.json +0 -1
  264. codex/static_root/js/choices-admin.3d958ea7f83b.json.br +0 -0
  265. codex/static_root/js/choices-admin.3d958ea7f83b.json.gz +0 -0
  266. codex/static_root/js/choices.6bfc2a3d293f.json +0 -1
  267. codex/static_root/js/choices.6bfc2a3d293f.json.br +0 -0
  268. codex/static_root/js/choices.6bfc2a3d293f.json.gz +0 -0
  269. codex/static_root/manifest.64a989215af8.json.br +0 -0
  270. codex/static_root/manifest.64a989215af8.json.gz +0 -0
  271. codex/urls/opds/opensearch.py +0 -18
  272. codex/views/opds/v1/browser.py +0 -346
  273. codex/views/opds/v1/entry.py +0 -278
  274. codex/views/opds/v1/start.py +0 -28
  275. codex/views/opds/v1/util.py +0 -162
  276. codex/views/opds/v2/start.py +0 -28
  277. {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/LICENSE +0 -0
  278. {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/WHEEL +0 -0
  279. {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/entry_points.txt +0 -0
@@ -1 +0,0 @@
1
- import{a as b,bm as u,b as f,c as m,o as c,e as _,w as l,k as g,t as k,bn as ie,bo as le,g as s,E as P,B as y,f as C,bp as I,ao as R,D as w,d as v,bq as ze,br as Fe,j as r,aD as L,bl as ce,bs as He,bt as Me,bu as Ge,bv as de,r as G,bw as ue,F as B,bx as ge,by as pe,aH as q,be as O,b8 as qe,b2 as H,aW as Ke,bz as je,M as Ye,J as Xe,K as D,ar as M,aY as K,at as Je,au as Ze,bA as Qe,bB as et,bC as tt,bD as ot,bE as at,n as j,p as Y,N as he,bF as st,T as _e,bG as me,bH as nt,bI as rt,bJ as it,bK as X,a9 as lt,bL as ct,bd as dt,bM as ut,bN as J,bO as ke,bP as gt,aA as z,bQ as pt,bR as ht,bS as _t,bT as mt,bU as kt,bV as vt,bW as ft,bX as bt,bY as Pt,bZ as wt,q as St,v as yt,V as Z,bk as ve,C as Q,bj as Ct,b_ as Tt,bh as ee,b$ as Rt}from"./admin-73d93dc7.js";import{_ as E,a as fe,b as be,P as Bt,c as Pe}from"./metadata-dialog-8b0e8aaa.js";const It={name:"ReaderNavButton",components:{PaginationNavButton:E},props:{value:{type:Number,required:!0},twoPages:{type:Boolean,default:!1}},computed:{...b(u,{storePage:e=>e.page,vertical:e=>{var t;return(t=e.activeSettings)==null?void 0:t.vertical},toRoute(e){return{params:{pk:e.pk,page:this.value}}}}),title(){return"to page "+this.value},disabled(){return this.value===this.storePage||this.twoPages&&!!(this.value%2)&&this.value-1===this.storePage}},methods:{...f(u,["setActivePage"]),onClick(){this.setActivePage(this.value,!0)}}};function Dt(e,t,a,d,i,o){const n=E;return e.vertical?(c(),_(n,{key:0,disabled:o.disabled,title:o.title,onClick:o.onClick},{default:l(()=>[g(k(a.value),1)]),_:1},8,["disabled","title","onClick"])):(c(),_(n,{key:1,disabled:o.disabled,title:o.title,to:e.toRoute},{default:l(()=>[g(k(a.value),1)]),_:1},8,["disabled","title","to"]))}const we=m(It,[["render",Dt]]),$t={name:"ReaderBookChangeNavButton",components:{PaginationNavButton:E},props:{direction:{type:String,required:!0}},computed:{...b(u,{toRoute(e){const t=e.routes.books;if(!t)return"";const a=t[this.direction];return a?{params:{...a}}:""}}),title(){return(this.direction==="prev"?"Previous":"Next")+" Book"},icon(){return this.direction==="prev"?ie:le},disabled(){return!this.toRoute}}};function Vt(e,t,a,d,i,o){const n=E;return c(),_(n,{icon:"",variant:"plain",disabled:o.disabled,title:o.title,to:e.toRoute},{default:l(()=>[s(P,null,{default:l(()=>[g(k(o.icon),1)]),_:1})]),_:1},8,["disabled","title","to"])}const Se=m($t,[["render",Vt]]),V="prev",x="next",xt={name:"ReaderNavToolbar",components:{PaginationSlider:fe,ReaderNavButton:we,PaginationToolbar:be,ReaderBookChangeNavButton:Se},computed:{...y(u,["activeSettings","activeBook"]),...b(u,{storePage:e=>e.page,key(e){return`${e.pk} - ${this.twoPages}`}}),maxPage(){return this.activeBook?this.activeBook.maxPage:0},twoPages(){return this.activeSettings.twoPages},step(){return this.activeSettings.twoPages?2:1},trackColor(){return this.twoPages&&+this.storePage>=this.maxPage-1?this.$vuetify.theme.current.colors.primary:""},readInReverse(){return this.activeSettings.readInReverse},min(){return this.readInReverse?this.maxPage:0},max(){return this.readInReverse?0:this.maxPage},bookPrev(){return this.readInReverse?"next":"prev"},bookNext(){return this.readInReverse?"prev":"next"}},mounted(){document.addEventListener("keyup",this._keyListener)},beforeUnmount(){document.removeEventListener("keyup",this._keyListener)},methods:{...f(u,["routeToBook","routeToDirection","routeToDirectionOne","routeToPage","setActivePage"]),onSliderUpdate(e){this.activeSettings.vertical?this.setActivePage(e,!0):this.routeToPage(e)},_keyListener(e){switch(e.stopPropagation(),e.key){case" ":!e.shiftKey&&window.innerHeight+window.scrollY+1>=document.body.scrollHeight?this.routeToDirection(x):e.shiftKey&&window.scrollY===0&&this.routeToDirection(V);break;case"j":case"ArrowRight":this.routeToDirection(x);break;case"k":case"ArrowLeft":this.routeToDirection(V);break;case",":this.routeToDirectionOne(V);break;case".":this.routeToDirectionOne(x);break;case"n":this.routeToBook(x);break;case"p":this.routeToBook(V);break}}}};function Lt(e,t,a,d,i,o){const n=Se,h=we,p=fe,T=be;return o.maxPage?(c(),_(T,{key:0},{default:l(()=>[s(n,{direction:o.bookPrev},null,8,["direction"]),s(h,{value:o.min,"two-pages":o.twoPages},null,8,["value","two-pages"]),(c(),_(p,{key:e.key,"model-value":e.storePage,min:0,max:o.maxPage,step:o.step,"track-color":o.trackColor,reverse:o.readInReverse,"onUpdate:modelValue":t[0]||(t[0]=$=>o.onSliderUpdate($))},null,8,["model-value","max","step","track-color","reverse"])),s(h,{value:o.max,"two-pages":o.twoPages},null,8,["value","two-pages"]),s(n,{direction:o.bookNext},null,8,["direction"])]),_:1})):C("",!0)}const ye=m(xt,[["render",Lt]]),Ot={rel:"prefetch",as:"image"},Ce=function(e){const t=[];for(const a of e)a&&t.push({...Ot,href:a});return{link:t}};const Et={name:"PageChangeLink",props:{direction:{type:String,required:!0}},head(){return Ce([this.prefetchSrc1,this.prefetchSrc2])},computed:{...b(u,{params(e){return e.routes[this.computedDirection]},prefetchSrc2(e){const t=e.books.get(this.params.pk);if(!t||!this.getSettings(t).twoPages)return!1;const d={...this.params,page:this.params.page+1};return d.page>t.maxPage?!1:I(d)},directionClass(e){let t=this.direction;return e.activeSettings.vertical&&(t+="Vertical"),t},pageChangeClass(e){let t="pageChange";return t+=e.activeSettings.vertical?"Row":"Column",t}}),computedDirection(){return this.normalizeDirection(this.direction)},prefetchSrc1(){return this.params?I(this.params):!1},route(){return this.params?{params:this.params}:{}},label(){return`${this.computedDirection==="prev"?"Previous":"Next"} Page`}},methods:{...f(u,["getSettings","normalizeDirection"])}};function Nt(e,t,a,d,i,o){const n=w("router-link");return c(),_(n,{to:o.route,"aria-label":o.label,class:R({[e.pageChangeClass]:!0,[e.directionClass]:!0}),onClick:t[0]||(t[0]=h=>h.stopImmediatePropagation())},null,8,["to","aria-label","class"])}const Te=m(Et,[["render",Nt],["__scopeId","data-v-29292846"]]);const At={name:"LoadingPage",components:{Placeholder:Bt},props:{twoPages:{type:Boolean,required:!0}}};function Ut(e,t,a,d,i,o){const n=w("Placeholder");return c(),v("div",{class:R(["pageLoading",{twoPages:a.twoPages}])},[s(n,{class:"pagePlaceholder"})],2)}const Wt=m(At,[["render",Ut],["__scopeId","data-v-13c9bf0b"]]);const te="unauthorized",zt={name:"ErrorPage",props:{twoPages:{type:Boolean,required:!0},type:{type:String,required:!0}},computed:{text(){return this.type===te?"Protected page":"Failed to load page"},icon(){return this.type===te?ze:Fe}}},Ft={class:"pageErrorContainer"},Ht={class:"pageErrorText"};function Mt(e,t,a,d,i,o){return c(),v("div",{class:R(["pageError",{twoPages:a.twoPages}])},[r("div",Ft,[r("div",Ht,k(o.text),1),s(P,{class:"pageErrorIcon"},{default:l(()=>[g(k(o.icon),1)]),_:1})])],2)}const Gt=m(zt,[["render",Mt],["__scopeId","data-v-3f1e3f4f"]]);const qt={name:"ImgPage",props:{src:{type:String,required:!0},fitToClass:{type:Object,required:!0}},emits:["load","error"]},Kt=["src"];function jt(e,t,a,d,i,o){return c(),v("img",L({class:a.fitToClass,src:a.src},e.$attrs,{onError:t[0]||(t[0]=n=>e.$emit("error")),onLoad:t[1]||(t[1]=n=>e.$emit("load"))}),null,16,Kt)}const oe=m(qt,[["render",jt],["__scopeId","data-v-5997abff"]]);const ae=ce(He(()=>Ge(()=>import("./page-pdf-abfd509d.js"),["assets/page-pdf-abfd509d.js","assets/admin-73d93dc7.js","assets/admin-79555229.css","assets/page-pdf-2d7d4f5d.css"]))),Yt=333,Xt={S:"Screen",W:"Width",H:"Height",O:"Original"},Jt={name:"BookPage",components:{ErrorPage:Gt,LoadingPage:Wt,PDFPage:ae,ImgPage:oe},props:{book:{type:Object,required:!0},page:{type:Number,required:!0}},data(){return{showProgress:!1,loaded:!1,error:!1}},computed:{pageClass(){return this.settings.vertical?"pageVertical":"pageHorizontal"},fitToClass(){let e={};const t=Xt[this.settings.fitTo];if(t){let a="fitTo";a+=t.charAt(0).toUpperCase()+t.slice(1).toLowerCase(),this.settings.twoPages&&(a+="Two",this.settings.vertical&&(a+="Vertical")),e[a]=!0}return e},src(){const e={pk:this.book.pk,page:this.page};return I(e)},component(){return this.book.fileType==="PDF"?ae:oe},settings(){return this.getSettings(this.book)}},mounted(){setTimeout(function(){this.loaded||(this.loading=!0)},Yt)},methods:{...f(u,["getSettings"]),onLoad(){this.showProgress=!1,this.loaded=!0,this.error=!1},onError(){this.error="load",this.showProgress=!1},onUnauthorized(){this.error="unauthorized",this.showProgress=!1}}},Zt=["id","data-page"];function Qt(e,t,a,d,i,o){const n=w("ErrorPage"),h=w("LoadingPage");return c(),v("div",{id:`page${a.page}`,"data-page":a.page,class:R(o.pageClass)},[i.error?(c(),_(n,{key:0,"two-pages":o.settings.twoPages,type:i.error},null,8,["two-pages","type"])):i.showProgress&&!i.loaded?(c(),_(h,{key:1,"two-pages":o.settings.twoPages},null,8,["two-pages"])):(c(),_(Me(o.component),{key:2,src:o.src,"fit-to-class":o.fitToClass,book:a.book,onError:o.onError,onLoad:o.onLoad,onUnauthorized:o.onUnauthorized},null,40,["src","fit-to-class","book","onError","onLoad","onUnauthorized"]))],10,Zt)}const Re=m(Jt,[["render",Qt],["__scopeId","data-v-f9e0354b"]]);const eo=48,to=48,oo={name:"PagesHorizontalWindow",components:{BookPage:Re,PageChangeLink:Te},props:{book:{type:Object,required:!0}},data(){return{activePage:0,pages:[]}},computed:{...y(u,["isOnCoverPage"]),...b(u,{prevBook:e=>{var t;return(t=e.routes.books)==null?void 0:t.prev},nextBook:e=>{var t;return(t=e.routes.books)==null?void 0:t.next},storePage:e=>e.page,storePk:e=>e.pk}),settings(){return this.getSettings(this.book)},twoPages(){return this.settings.twoPages},readInReverse(){return this.settings.readInReverse},secondPage(){return this.settings.twoPages&&!this.isOnCoverPage},windowIndex(){const e=this.activePage-this.pages[0];return Math.min(Math.max(0,e),this.book.maxPage)}},watch:{twoPages(){this.setActivePage(this.storePage,!0)},storePage(e){if(this.book.pk===this.storePk){this.activePage=e;const t=this.pages.at(0),a=this.pages.at(-1);(e<t||e>a)&&this.setPages()}},prevBook(e){this.book.pk===e.pk&&(this.activePage=this.book.maxPage)},nextBook(e){this.book.pk===e.pk&&(this.activePage=0)}},created(){this.book.pk===this.storePk?this.activePage=+this.$route.params.page:this.book.pk===this.prevBook.pk?this.activePage=this.book.maxPage:this.activePage=0,this.setPages()},methods:{...f(u,["getSettings","setBookChangeFlag","setActivePage"]),setPages(){const e=Math.max(this.activePage-eo,0),t=Math.min(this.activePage+to,this.book.maxPage),a=de.range(e,t+1);this.pages=a}}};function ao(e,t,a,d,i,o){const n=Te,h=w("BookPage");return c(),_(ge,{"show-arrows":"",continuous:"","model-value":o.windowIndex,reverse:o.readInReverse},{prev:l(()=>[s(n,{direction:"prev"})]),next:l(()=>[s(n,{direction:"next"})]),default:l(()=>[(c(!0),v(B,null,G(i.pages,p=>(c(),_(ue,{key:`c/${a.book.pk}/${p}`,class:"windowItem",disabled:"",eager:p>=e.storePage-1&&p<=e.storePage+2,"model-value":p,transition:!0},{default:l(()=>[!o.readInReverse||o.secondPage&&p<a.book.maxPage?(c(),_(h,{key:0,book:a.book,page:o.readInReverse?p+1:p},null,8,["book","page"])):C("",!0),o.readInReverse||o.secondPage&&p<a.book.maxPage?(c(),_(h,{key:1,book:a.book,page:o.readInReverse?p:p+1},null,8,["book","page"])):C("",!0)]),_:2},1032,["eager","model-value"]))),128))]),_:1},8,["model-value","reverse"])}const so=m(oo,[["render",ao],["__scopeId","data-v-da9ab604"]]);function Be(e,t){const{self:a=!1}=t.modifiers??{},d=t.value,i=typeof d=="object"&&d.options||{passive:!0},o=typeof d=="function"||"handleEvent"in d?d:d.handler,n=a?e:t.arg?document.querySelector(t.arg):window;n&&(n.addEventListener("scroll",o,i),e._onScroll=Object(e._onScroll),e._onScroll[t.instance.$.uid]={handler:o,options:i,target:a?void 0:n})}function Ie(e,t){var o;if(!((o=e._onScroll)!=null&&o[t.instance.$.uid]))return;const{handler:a,options:d,target:i=e}=e._onScroll[t.instance.$.uid];i.removeEventListener("scroll",a,d),delete e._onScroll[t.instance.$.uid]}function no(e,t){t.value!==t.oldValue&&(Ie(e,t),Be(e,t))}const ro={mounted:Be,unmounted:Ie,updated:no},io=12,se=250,ne={threshold:[.75]},lo={S:ne,H:ne},co={name:"PagesVerticalScroller",components:{BookPage:Re,VVirtualScroll:pe},props:{book:{type:Object,required:!0}},data(){return{innerHeight:window.innerHeight,innerWidth:window.innerWidth,intersectorOn:!1,programmaticScroll:!1}},computed:{...b(u,{storePage:e=>e.page}),...q(u,["reactWithScroll"]),settings(){return this.getSettings(this.book)},readInReverse(){return this.settings.readInReverse},items(){var a;const e=((a=this.book)==null?void 0:a.maxPage)+1,t=de.range(0,e);return this.readInReverse&&t.reverse(),t},visibleItems(){var e;return Math.min(((e=this.book)==null?void 0:e.maxPage)??0,io)},intersectOptions(){return lo[this.settings.fitTo]}},watch:{storePage(e){this.reactWithScroll&&this.scrollToPage(e)}},mounted(){window.addEventListener("resize",this.onResize),setTimeout(()=>{this.scrollToPage(this.storePage)},se)},beforeUnmount(){window.removeEventListener("resize",this.onResize)},methods:{...f(u,["setActivePage","setBookChangeFlag","getSettings"]),onIntersect(e,t){if(e&&this.intersectorOn){const d=+t[0].target.dataset.page;this.setActivePage(d,!1)}},onScroll(){if(this.programmaticScroll)return;this.intersectorOn=!0;const e=this.$refs.verticalScroll.$el,t=e.scrollTop;if(this.storePage===0&&t===0)this.setBookChangeFlag("prev");else if(this.storePage===this.book.maxPage){const a=e.scrollTopMax||e.scrollHeight-e.clientTop;window.innerHeight+t+1>=a&&this.setBookChangeFlag("next")}},scrollToPage(e){this.intersectorOn=!1,this.programmaticScroll=!0,this.$refs.verticalScroll.scrollToIndex(e),setTimeout(()=>{this.programmaticScroll=!1},se)},onResize(){this.innerHeight=window.innerHeight,this.innerWidth=window.innerWidth}}},uo=["data-page"];function go(e,t,a,d,i,o){const n=w("BookPage"),h=pe;return O((c(),_(h,{id:"verticalScroll",ref:"verticalScroll",items:o.items,"visible-items":o.visibleItems,height:i.innerHeight,width:i.innerWidth},{default:l(({item:p})=>[s(n,{book:a.book,page:p},null,8,["book","page"]),O(r("div",{class:"pageTracker","data-page":p},null,8,uo),[[qe,{handler:o.onIntersect,options:o.intersectOptions},void 0,{quiet:!0}]])]),_:1},8,["items","visible-items","height","width"])),[[ro,o.onScroll,"#verticalScroll"]])}const po=m(co,[["render",go],["__scopeId","data-v-c93f9571"]]),ho={name:"PagesView",components:{PagesHorizontalWindow:so,PagesVerticalScroller:po},props:{book:{type:Object,required:!0}},emits:["click"],computed:{...b(u,{storePk:e=>e.pk}),settings(){return this.getSettings(this.book)},vertical(){return this.settings.vertical}},watch:{$route(e){+e.params.pk===this.book.pk&&this.setActivePage(+e.params.page,!0)}},created(){this.book.pk===this.storePk&&this.setActivePage(+this.$route.params.page,!0)},methods:{...f(u,["getSettings","setActivePage"])}};function _o(e,t,a,d,i,o){const n=w("PagesVerticalScroller"),h=w("PagesHorizontalWindow");return o.vertical?(c(),_(n,{key:0,book:a.book,onClick:e.$emit.click},null,8,["book","onClick"])):(c(),_(h,{key:1,book:a.book,onClick:e.$emit.click},null,8,["book","onClick"]))}const De=m(ho,[["render",_o]]);const mo={name:"BookChangeActivator",props:{direction:{type:String,required:!0}},computed:{...y(u,["prevBookChangeShow","nextBookChangeShow"]),show(){return this[this.computedDirection+"BookChangeShow"]},computedDirection(){return this.normalizeDirection(this.direction)}},methods:{...f(u,["setBookChangeFlag","normalizeDirection"])}};function ko(e,t,a,d,i,o){return o.show?(c(),v("div",{key:0,class:R(["bookChangeColumn",{[a.direction]:!0}]),onClick:t[0]||(t[0]=H(n=>e.setBookChangeFlag(a.direction),["stop"]))},null,2)):C("",!0)}const $e=m(mo,[["render",ko],["__scopeId","data-v-2a8ea6a5"]]);const vo={name:"BooksWindow",components:{BookChangeActivator:$e,PagesView:De},emits:["click"],computed:{...b(u,{books:e=>e.books,bookChange:e=>e.bookChange,activeBookPk:e=>e.pk,bookRoutes:e=>e.routes.books})},watch:{$route(e,t){(!t||!t.params||+e.params.pk!=+t.params.pk)&&this.loadBooks(e.params)}},beforeMount(){this.setBookChangeFlag()},created(){this.loadBooks(this.$route.params)},methods:{...f(u,["setBookChangeFlag","loadBooks"]),click(){this.setBookChangeFlag(),this.$emit("click")},eager(e){return this.bookChange&&this.bookRoutes[this.bookChange]&&this.bookRoutes[this.bookChange].pk===e}}};function fo(e,t,a,d,i,o){const n=$e,h=De;return c(),_(ge,{id:"booksWindow",direction:"vertical","model-value":e.activeBookPk,"show-arrows":"",onClick:o.click},{prev:l(()=>[s(n,{direction:"prev"})]),next:l(()=>[s(n,{direction:"next"})]),default:l(()=>[(c(!0),v(B,null,G(e.books,([p,T])=>(c(),_(ue,{key:`c/${p}`,class:"windowItem",disabled:"",eager:o.eager(p),value:p,transition:!0},{default:l(()=>[s(h,{book:T,onClick:o.click},null,8,["book","onClick"])]),_:2},1032,["eager","value"]))),128))]),_:1},8,["model-value","onClick"])}const Ve=m(vo,[["render",fo],["__scopeId","data-v-ca6f1824"]]);const bo={name:"ReaderTitleToolbar",components:{MetadataDialog:Pe,SettingsDrawerButton:Ke},data(){return{mdiClose:je,routeChanged:!1}},head(){return{meta:[{hid:"description",name:"description",content:`reader ${this.activeTitle} page ${this.storePage}`}]}},computed:{...y(u,["activeTitle","activeBook"]),...b(u,{bookChange:e=>e.bookChange,seriesPosition:function(e){return this.activeBook&&e.seriesCount>1?`${this.activeBook.seriesIndex}/${e.seriesCount}`:""},vertical:e=>e.activeSettings.vertical,pk:e=>e.pk||0}),...b(Ye,{lastRoute:e=>e.page.routes.last}),...q(D,["isSettingsDrawerOpen"]),closeBookRoute:function(){const e={name:"browser",params:this.lastRoute};return e.params?e.hash=`#card-${this.pk}`:e.params=window.CODEX.LAST_ROUTE||Xe.browser.route,e}},watch:{$route(e,t){t&&(this.routeChanged=!0)}},mounted(){document.addEventListener("keyup",this._keyListener)},beforeUnmount(){document.removeEventListener("keyup",this._keyListener)},methods:{...f(D,["setTimestamp"]),...f(u,["routeToDirection","routeToDirectionOne","routeToBook","setBookChangeFlag"]),openMetadata(){this.$refs.metadataDialog.dialog=!0},onCloseBook(){this.routeChanged&&this.setTimestamp()},_keyListener(e){switch(e.stopPropagation(),e.key){case"Escape":this.$refs.closeBook.$el.click();break;case"m":this.openMetadata();break}}}},Po={key:0},wo={key:0,id:"seriesPosition",title:"Series Position"};function So(e,t,a,d,i,o){const n=Pe,h=w("SettingsDrawerButton");return c(),_(Ze,{id:"readerTopToolbar",class:"codexToolbar",density:"compact"},{default:l(()=>[s(K,null,{default:l(()=>[s(M,{id:"closeBook",ref:"closeBook",to:o.closeBookRoute,size:"large",onClick:o.onCloseBook},{default:l(()=>[e.$vuetify.display.smAndDown?(c(),_(P,{key:1,title:"Close Book"},{default:l(()=>[g(k(i.mdiClose),1)]),_:1})):(c(),v("span",Po,"close book"))]),_:1},8,["to","onClick"])]),_:1}),g(" "+k(e.bookChange)+" ",1),s(Je,{id:"toolbarTitle",class:"codexToolbarTitle"},{default:l(()=>[g(k(e.activeTitle),1)]),_:1}),e.seriesPosition?(c(),v("span",wo,k(e.seriesPosition),1)):C("",!0),s(K,null,{default:l(()=>[s(M,{id:"tagButton",onClick:H(o.openMetadata,["stop"])},{default:l(()=>[s(n,{ref:"metadataDialog",group:"c",pk:e.pk},null,8,["pk"])]),_:1},8,["onClick"]),s(h,{id:"settingsButton",onClick:t[0]||(t[0]=H(p=>e.isSettingsDrawerOpen=!0,["stop"]))})]),_:1})]),_:1})}const xe=m(bo,[["render",So],["__scopeId","data-v-4075b1a2"]]);const yo={name:"BookChangeDrawer",props:{direction:{type:String,required:!0}},head(){return Ce([this.prefetchSrc1,this.prefetchSrc2])},computed:{...y(u,["prevBookChangeShow","nextBookChangeShow"]),...b(u,{prefetchSrc2(e){if(!this.params)return!1;const t=e.books.get(this.params.pk),a=this.getSettings(t);if(!this.isDrawerOpen||!this.params||!a.twoPages)return!1;const d={pk:this.params.pk,page:this.params.page+1};return I(d)},isDrawerOpen(e){return e.bookChange===this.computedDirection},params(e){return e.routes.books[this.computedDirection]},icon(){return this.computedDirection==="next"?le:ie}}),show(){return this[this.computedDirection+"BookChangeShow"]},prefetchSrc1(){return!this.isDrawerOpen||!this.params?!1:I(this.params)},route(){return this.params?{params:this.params}:{}},label(){return`${this.computedDirection==="prev"?"Previous":"Next"} Book`},computedDirection(){return this.normalizeDirection(this.direction)}},methods:{...f(u,["getSettings","normalizeDirection"])}};function Co(e,t,a,d,i,o){const n=w("router-link");return o.show?(c(),_(Qe,{key:0,class:R({bookChangeDrawer:!0,[a.direction]:!0}),"disable-resize-watcher":"","disable-route-watcher":"",location:o.computedDirection==="prev"?"left":"right","model-value":e.isDrawerOpen,scrim:!1,temporary:"",touchless:""},{default:l(()=>[s(n,{class:"navLink",to:o.route,"aria-label":o.label,title:o.label,onClick:t[0]||(t[0]=h=>h.stopImmediatePropagation())},{default:l(()=>[s(P,{class:"bookChangeIcon"},{default:l(()=>[g(k(e.icon),1)]),_:1})]),_:1},8,["to","aria-label","title"])]),_:1},8,["class","location","model-value"])):C("",!0)}const Le=m(yo,[["render",Co],["__scopeId","data-v-33b1e110"]]);const To={name:"DownloadPanel",data(){return{mdiDownload:et,mdiFileImage:tt}},computed:{...y(u,["activeTitle"]),...b(u,{storePage:e=>e.page,pageSrc(e){const t={pk:e.pk,page:e.page};return ot(t)},downloadURL(e){return at(e.pk)}}),...y(u,["activeBook"]),pageName:function(){return`${this.activeTitle} - page ${this.storePage}.jpg`}},methods:{...f(D,["downloadIOSPWAFix"]),downloadPage(){this.downloadIOSPWAFix(this.pageSrc,this.pageName)},downloadBook(){this.downloadIOSPWAFix(this.downloadURL,this.activeBook.filename)}}},Ro={id:"downloadPanel"};function Bo(e,t,a,d,i,o){return c(),v("div",Ro,[s(Y,{onClick:o.downloadPage},{default:l(()=>[s(j,null,{default:l(()=>[s(P,null,{default:l(()=>[g(k(i.mdiFileImage),1)]),_:1}),g("Download Page "+k(e.storePage),1)]),_:1})]),_:1},8,["onClick"]),s(Y,{onClick:o.downloadBook},{default:l(()=>[s(j,null,{default:l(()=>[s(P,null,{default:l(()=>[g(k(i.mdiDownload),1)]),_:1}),g("Download Book ")]),_:1})]),_:1},8,["onClick"])])}const Oe=m(To,[["render",Bo],["__scopeId","data-v-c3777662"]]);const F=he()({name:"VRadio",props:{...st({falseIcon:"$radioOff",trueIcon:"$radioOn"})},setup(e,t){let{slots:a}=t;return _e(()=>s(me,L(e,{class:"v-radio",type:"radio"}),a)),{}}});const re=he()({name:"VRadioGroup",inheritAttrs:!1,props:{height:{type:[Number,String],default:"auto"},...nt(),...rt(it(),["multiple"]),trueIcon:{type:X,default:"$radioOn"},falseIcon:{type:X,default:"$radioOff"},type:{type:String,default:"radio"}},emits:{"update:modelValue":e=>!0},setup(e,t){let{attrs:a,slots:d}=t;const i=lt(),o=ct(()=>e.id||`radio-group-${i}`),n=dt(e,"modelValue");return _e(()=>{const[h,p]=ut(a),[T,$]=J.filterProps(e),[N,ta]=me.filterProps(e),A=d.label?d.label({label:e.label,props:{for:o.value}}):e.label;return s(J,L({class:"v-radio-group"},h,T,{modelValue:n.value,"onUpdate:modelValue":U=>n.value=U,id:o.value}),{...d,default:U=>{let{id:W,messagesId:Ne,isDisabled:Ae,isReadonly:Ue}=U;return s(B,null,[A&&s(ke,{id:W.value},{default:()=>[A]}),s(gt,L(N,{id:W.value,"aria-describedby":Ne.value,defaultsTarget:"VRadio",trueIcon:e.trueIcon,falseIcon:e.falseIcon,type:e.type,disabled:Ae.value,readonly:Ue.value,"aria-labelledby":A?W.value:void 0,multiple:!1},p,{modelValue:n.value,"onUpdate:modelValue":We=>n.value=We}),d)])}})}),{}}}),Io=["fitTo","twoPages","vertical","readInReverse"],Do={name:"ReaderSettingsPanel",data(){return{isGlobalScope:!1}},computed:{...b(u,{fitToChoices(e){const t=[];for(const a of e.choices.fitTo)a.value&&t.push(a);return t},selectedSettings:function(e){return this.isGlobalScope||!e.activeBook?e.readerSettings:e.activeBook.settings},isClearSettingsButtonDisabled:function(e){if(this.isGlobalScope||!e.activeBook)return!0;for(const t of Io){const a=e.activeBook.settings[t];if(!e.choices.nullValues.has(a))return!1}return!0},verticalLabel(e){const t=e.activeBook&&e.activeBook.settings?e.activeBook.settings.vertical:void 0;return t==null?"Horizontal or Vertical":t?"Scroll Pages Vertically":"Turn Pages Horizontally"},params(e){return{pk:e.pk,page:e.page}}}),...q(u,["readRTLInReverse"])},mounted(){document.addEventListener("keyup",this._keyListener)},beforeUnmount(){document.removeEventListener("keyup",this._keyListener)},methods:{...f(u,["clearSettingsLocal","setSettingsGlobal","setSettingsLocal"]),settingsDialogChanged:function(e){this.isGlobalScope?this.setSettingsGlobal(this.params,e):this.setSettingsLocal(this.params,e)},_keyListener:function(e){e.stopPropagation();let t;switch(e.key){case"w":t={fitTo:"W"};break;case"h":t={fitTo:"H"};break;case"s":t={fitTo:"S"};break;case"o":t={fitTo:"O"};break;case"2":t={twoPages:!this.selectedSettings.twoPages};break;case"r":t={readInReverse:!this.selectedSettings.readInReverse};break;case"v":t={vertical:!this.selectedSettings.vertical}}t&&this.setSettingsLocal(this.params,t)}}},$o={id:"readerSettings"};function Vo(e,t,a,d,i,o){return c(),v("div",$o,[s(re,{modelValue:i.isGlobalScope,"onUpdate:modelValue":t[0]||(t[0]=n=>i.isGlobalScope=n),density:"compact",label:"Scope","hide-details":"auto"},{default:l(()=>[s(F,{label:"Only this comic",value:!1}),s(F,{label:"Default for all comics",value:!0})]),_:1},8,["modelValue"]),s(re,{class:"displayRadioGroup",density:"compact",label:"Display","hide-details":"auto","model-value":e.selectedSettings.fitTo,"onUpdate:modelValue":t[1]||(t[1]=n=>o.settingsDialogChanged({fitTo:n}))},{default:l(()=>[(c(!0),v(B,null,G(e.fitToChoices,n=>(c(),_(F,{key:n.value,label:n.title,value:n.value},null,8,["label","value"]))),128))]),_:1},8,["model-value"]),s(z,{disabled:e.selectedSettings.vertical,class:"displayTwoPages",density:"compact",label:"Two pages","hide-details":"auto","model-value":e.selectedSettings.twoPages,"true-value":!0,indeterminate:e.selectedSettings.twoPages===null||e.selectedSettings.twoPages===void 0,"onUpdate:modelValue":t[2]||(t[2]=n=>o.settingsDialogChanged({twoPages:n}))},null,8,["disabled","model-value","indeterminate"]),s(ke,null,{default:l(()=>[g("Reading Direction")]),_:1}),s(pt,{class:"vertical",density:"compact",label:e.verticalLabel,"hide-details":"auto","model-value":e.selectedSettings.vertical,"true-value":!0,indeterminate:e.selectedSettings.vertical===null||e.selectedSettings.vertical===void 0,"onUpdate:modelValue":t[3]||(t[3]=n=>o.settingsDialogChanged({vertical:n}))},null,8,["label","model-value","indeterminate"]),s(z,{class:"readInReverse",density:"compact",label:"Read in Reverse","hide-details":"auto","model-value":e.selectedSettings.readInReverse,"true-value":!0,indeterminate:e.selectedSettings.readInReverse===null||e.selectedSettings.readInReverse===void 0,"onUpdate:modelValue":t[4]||(t[4]=n=>o.settingsDialogChanged({readInReverse:n}))},null,8,["model-value","indeterminate"]),i.isGlobalScope?(c(),_(z,{key:0,"model-value":e.selectedSettings.readRtlInReverse,class:"readRtlInReverse",density:"compact",label:"Read RTL comics in Reverse","hide-details":"auto","true-value":!0,"onUpdate:modelValue":t[5]||(t[5]=n=>o.settingsDialogChanged({readRtlInReverse:n}))},null,8,["model-value"])):C("",!0),i.isGlobalScope?C("",!0):(c(),_(M,{key:1,id:"clearSettingsButton",disabled:e.isClearSettingsButtonDisabled,title:"Use the default settings for all comics for this comic",onClick:t[6]||(t[6]=n=>e.clearSettingsLocal(e.params))},{default:l(()=>[g(" Clear Comic Settings ")]),_:1},8,["disabled"]))])}const Ee=m(Do,[["render",Vo],["__scopeId","data-v-72cc1f00"]]);const xo={name:"KeyboardShortcutsPanel",data(){return{mdiMenuLeft:ht,mdiMenuRight:_t,mdiKeyboardOutline:mt,mdiKeyboardSpace:kt,mdiAppleKeyboardShift:vt}},computed:{...y(D,["isMobile"])}},S=e=>(St("data-v-41483eb9"),e=e(),yt(),e),Lo=S(()=>r("h4",null,"Navigation",-1)),Oo={class:"highlight-table"},Eo=S(()=>r("td",null,"Previous page",-1)),No=S(()=>r("td",null,"Next page",-1)),Ao=S(()=>r("tr",null,[r("td",null,","),r("td",null,[g("Next "),r("em",null,"only"),g(" one page in two page mode.")])],-1)),Uo=S(()=>r("tr",null,[r("td",null,"."),r("td",null,[g("Previous "),r("em",null,"only"),g(" one page in two page mode.")])],-1)),Wo=S(()=>r("tr",null,[r("td",null,"p"),r("td",null,"Previous Book")],-1)),zo=S(()=>r("tr",null,[r("td",null,"n"),r("td",null,"Next Book")],-1)),Fo=S(()=>r("h4",null,"Fit Page To",-1)),Ho=S(()=>r("table",{class:"highlight-table"},[r("tbody",null,[r("tr",null,[r("td",null,"s"),r("td",null,"screen")]),r("tr",null,[r("td",null,"h"),r("td",null,"screen height")]),r("tr",null,[r("td",null,"w"),r("td",null,"screen width")]),r("tr",null,[r("td",null,"o"),r("td",null,"Show page at original size")]),r("tr",null,[r("td",null,"2"),r("td",null,"Toggle two page view")]),r("tr",null,[r("td",null,"r"),r("td",null,"Toggle Read in Reverse")])])],-1)),Mo=S(()=>r("h4",null,"Other",-1)),Go=S(()=>r("table",{class:"highlight-table"},[r("tbody",null,[r("tr",null,[r("td",null,"m"),r("td",null,"Show comic's metadata")]),r("tr",null,[r("td",null,"esc"),r("td",null,"Close book")])])],-1));function qo(e,t,a,d,i,o){return e.isMobile?C("",!0):(c(),_(wt,{key:0},{default:l(()=>[s(ft,{id:"readerKeyboardShortcuts"},{default:l(()=>[s(bt,{id:"shortcutsTitle"},{default:l(()=>[s(P,{id:"keyboardIcon"},{default:l(()=>[g(k(i.mdiKeyboardOutline),1)]),_:1}),g("Keyboard Shortcuts ")]),_:1}),s(Pt,null,{default:l(()=>[Lo,r("table",Oo,[r("tbody",null,[r("tr",null,[r("td",null,[s(P,{class:"keyIcon",density:"compact"},{default:l(()=>[g(k(i.mdiMenuLeft),1)]),_:1}),g(", j, "),r("div",null,[s(P,{class:"keyIcon",density:"compact"},{default:l(()=>[g(k(i.mdiAppleKeyboardShift),1)]),_:1}),s(P,{class:"keyIcon",density:"compact"},{default:l(()=>[g(k(i.mdiKeyboardSpace),1)]),_:1})])]),Eo]),r("tr",null,[r("td",null,[s(P,{class:"keyIcon",density:"compact"},{default:l(()=>[g(k(i.mdiMenuRight),1)]),_:1}),g(", k, "),s(P,{class:"keyIcon",density:"compact"},{default:l(()=>[g(k(i.mdiKeyboardSpace),1)]),_:1})]),No]),Ao,Uo,Wo,zo])]),Fo,Ho,Mo,Go]),_:1})]),_:1})]),_:1}))}const Ko=m(xo,[["render",qo],["__scopeId","data-v-41483eb9"]]),jo={name:"ReaderSettingsSuperPanel",components:{DownloadPanel:Oe,ReaderKeyboardShortcutsPanel:Ko,ReaderSettingsPanel:Ee}};function Yo(e,t,a,d,i,o){const n=Ee,h=w("ReaderKeyboardShortcutsPanel"),p=Oe;return c(),v(B,null,[s(n),s(Z),s(h),s(Z),s(p)],64)}const Xo=m(jo,[["render",Yo]]);const Jo={name:"MainReader",components:{BookChangeDrawer:Le,BooksWindow:Ve,ReaderNavToolbar:ye,ReaderTitleToolbar:xe,SettingsDrawer:ve},data(){return{showToolbars:!1,ReaderSettingsSuperPanel:ce(Xo)}},computed:{...y(Q,["isCodexViewable"]),...b(Q,{user:e=>e.user})},watch:{user(){this.loadReaderSettings()},isCodexViewable(){this.loadReaderSettings()}},created(){D().isSettingsDrawerOpen=!1,this.loadReaderSettings()},methods:{...f(u,["loadReaderSettings"]),toggleToolbars(){this.showToolbars=!this.showToolbars}}},Zo={key:0,id:"readerContainer"},Qo={key:1,id:"announcement"};function ea(e,t,a,d,i,o){const n=Le,h=xe,p=Ve,T=ye,$=w("router-link"),N=ve;return c(),v(B,null,[s(Ct,{id:"readerWrapper"},{default:l(()=>[s(n,{direction:"prev"}),e.isCodexViewable?(c(),v("div",Zo,[s(Tt,null,{default:l(()=>[O(s(h,null,null,512),[[ee,i.showToolbars]])]),_:1}),s(p,{onClick:o.toggleToolbars},null,8,["onClick"]),s(Rt,null,{default:l(()=>[O(s(T,null,null,512),[[ee,i.showToolbars]])]),_:1})])):(c(),v("div",Qo,[r("h1",null,[s($,{to:{name:"home"}},{default:l(()=>[g(" Log in ")]),_:1}),g(" to read comics ")])]))]),_:1}),s(n,{direction:"next"}),s(N,{title:"Reader Settings",panel:i.ReaderSettingsSuperPanel,temporary:""},null,8,["panel"])],64)}const sa=m(Jo,[["render",ea],["__scopeId","data-v-dff2aac6"]]);export{sa as default};
@@ -1 +0,0 @@
1
- {"statusTitles":{"CCC":"Create Covers","CCD":"Remove Covers","CFO":"Find Orphan Covers","IDM":"Move Folders","IFM":"Move Books","ITR":"Read Book Tags","ITQ":"Discover Missing Tags","ITC":"Create Missing Tags","IDU":"Modify Folders","IFU":"Modify Books","IFC":"Create Books","IMQ":"Prepare Tags for Linking","IMC":"Link Books to Tags","IDD":"Remove Folders","IFD":"Remove Books","IFI":"Record Failed Imports","JTD":"Cleanup Stale Tags","JCU":"Update Codex Software","JCR":"Restart Codex","JCS":"Stop Codex","JDO":"Optimize Database","JDB":"Backup Database","JSD":"Cleanup Expired Sessions","SIX":"Search Index Clear","SIU":"Search Index Update","SID":"Search Index Remove Stale Records","SIM":"Search Index Optimize","WPO":"Poll Library"},"tasks":[{"title":"Libraries","tasks":[{"value":"poll","title":"Poll All Libraries","desc":"Update Libraries if changes detected"},{"value":"poll_force","title":"Force Update All Libraries","desc":"Forcibly update all comics in all libraries","confirm":"This can take a long time"},{"value":"force_update_all_failed_imports","title":"Update All Failed Imports","desc":"Forcibly update all failed imports in all libraries","confirm":"This can take a long time"}]},{"title":"Covers","tasks":[{"value":"purge_comic_covers","title":"Remove Comic Covers","desc":"from every library","confirm":"Are you sure?"}]},{"title":"Search Index","tasks":[{"value":"search_index_update","title":"Update Search Index","desc":"with recently changed comics"},{"value":"search_index_rebuild","title":"Rebuild Search Index","desc":"Delete and rebuild the search index from scratch","confirm":"This can take a long time"},{"value":"search_index_remove_stale","title":"Remove Stale Index Entries","desc":"Remove search index entries that are no longer in the library."},{"value":"search_index_merge_small","title":"Merge Small Search Index Segments","desc":"Improves search lookup times. Runs nightly if the Optimize Admin Flag is off."},{"value":"search_index_optimize","title":"Merge Search Index Into One Segment","desc":"Reduces disk space and improves search lookup times. Runs nightly if the Optimize Admin Flag is on.","confirm":"This can take a long time."},{"value":"search_index_abort","title":"Abort Search Indexing","desc":"Aborts search index update and remove tasks."}]},{"title":"Database","tasks":[{"value":"db_vacuum","title":"Optimize & Compact Database","desc":"Run the sqlite3 optimize & vacuum and pragmas. Runs nightly"},{"value":"db_backup","title":"Backup Database","desc":"Runs nightly"},{"value":"db_search_sync","title":"Sync Search Index to DB","desc":"Check to see if this database matches the current search index. If there is a mismatch, rebuild the search index. Runs on startup."},{"value":"watchdog_sync","title":"Sync Watchdog with DB","desc":"Ensure the Watchdog file watcher is enabled per database preferences for each library"}]},{"title":"Codex Software","tasks":[{"value":"codex_update","title":"Update Codex","desc":"If Codex updates to a new version, it will restart","confirm":"Are you sure?"},{"value":"codex_restart","title":"Restart Codex Server","desc":"Immediately","confirm":"Are you sure?"},{"value":"codex_shutdown","title":"Shutdown Codex Server","desc":"Immediately","confirm":"Are you sure?"}]},{"title":"Notify","tasks":[{"value":"notify_library_changed","title":"Notify Library Changed ","desc":"Signal all clients that the libraries have changed and the browser should fetch new data."},{"value":"notify_librarian_status","title":"Notify Librarian Status","desc":"Signal Admin clients to fetch librarian status."}]},{"title":"Cleanup","tasks":[{"value":"cleanup_fks","title":"Remove Orphan Tags","desc":"After deleting comics, unused linked objects remain in case new comics use them. Runs nightly."},{"value":"cleanup_sessions","title":"Cleanup Sessions","desc":"Remove corrupt and expired sessoions. Runs nightly."},{"value":"cleanup_covers","title":"Remove Orphan Covers","desc":"from comics that no longer exist. Runs nightly."},{"value":"librarian_clear_status","title":"Clear Librarian Statuses","desc":"Mark all Librarian tasks finished."},{"value":"janitor_nightly","title":"Run Nightly Maintenance","desc":"Runs several tasks above that also run nightly.","confirm":"Launches several tasks that run nightly anyway."}]}],"adminFlags":{"FV":"Folder View","RG":"Registration","NU":"Non Users","AU":"Auto Update","SO":"Search Index Full Optimization"}}
@@ -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)