api.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/api */
  2. /** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/web */
  3. import type { webReadConfig } from '@/web'
  4. import ajax from './axios'
  5. import type {
  6. BaseBook,
  7. Book,
  8. BookChapter,
  9. BookProgress,
  10. SeachBook,
  11. } from '@/book'
  12. import type { Source } from '@/source'
  13. export type LeagdoApiResponse<T> = {
  14. isSuccess: boolean
  15. errorMsg: string
  16. data: T
  17. }
  18. export let legado_http_entry_point = ''
  19. export let legado_webSocket_entry_point = ''
  20. let wsOnError: typeof WebSocket.prototype.onerror = () => {}
  21. let wsOnMessage: typeof WebSocket.prototype.onmessage = () => {}
  22. export const setWebsocketOnMessage = (callback: typeof wsOnMessage) =>
  23. (wsOnMessage = callback)
  24. export const setWebsocketOnError = (callback: typeof wsOnError) => {
  25. //WebSocket.prototype.onerror = callback
  26. wsOnError = callback
  27. }
  28. export const setApiEntryPoint = (
  29. http_entry_point: string,
  30. webSocket_entry_point: string,
  31. ) => {
  32. legado_http_entry_point = new URL(http_entry_point).toString()
  33. legado_webSocket_entry_point = new URL(webSocket_entry_point).toString()
  34. ajax.defaults.baseURL = legado_http_entry_point
  35. }
  36. // 书架API
  37. // Http
  38. const getReadConfig = async (http_url = legado_http_entry_point) => {
  39. const { data } = await ajax.get<LeagdoApiResponse<string>>('getReadConfig', {
  40. baseURL: http_url.toString(),
  41. timeout: 3000,
  42. })
  43. if (data.isSuccess) {
  44. try {
  45. return JSON.parse(data.data) as webReadConfig
  46. } catch {}
  47. }
  48. }
  49. const saveReadConfig = (config: webReadConfig) =>
  50. ajax.post<LeagdoApiResponse<string>>('saveReadConfig', config)
  51. /** @deprecated: 使用`API.saveBookProgressWithBeacon`以确保在页面或者直接关闭的情况下保存进度 */
  52. const saveBookProgress = (bookProgress: BookProgress) =>
  53. ajax.post('saveBookProgress', bookProgress)
  54. /**主要在直接关闭浏览器情况下可靠发送书籍进度 */
  55. const saveBookProgressWithBeacon = (bookProgress: BookProgress) => {
  56. if (!bookProgress) return
  57. // 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
  58. navigator.sendBeacon(
  59. new URL('saveBookProgress', legado_http_entry_point),
  60. JSON.stringify(bookProgress),
  61. )
  62. }
  63. const getBookShelf = () => ajax.get<LeagdoApiResponse<Book[]>>('getBookshelf')
  64. const getChapterList = (/** @type {string} */ bookUrl: string) =>
  65. ajax.get<LeagdoApiResponse<BookChapter[]>>(
  66. 'getChapterList?url=' + encodeURIComponent(bookUrl),
  67. )
  68. const getBookContent = (
  69. /** @type {string} */ bookUrl: string,
  70. /** @type {number} */ chapterIndex: number,
  71. ) =>
  72. ajax.get<LeagdoApiResponse<string>>(
  73. 'getBookContent?url=' +
  74. encodeURIComponent(bookUrl) +
  75. '&index=' +
  76. chapterIndex,
  77. )
  78. // webSocket
  79. const search = (
  80. searchKey: string,
  81. onReceive: (data: SeachBook[]) => void,
  82. onFinish: () => void,
  83. ) => {
  84. const socket = new WebSocket(
  85. new URL('searchBook', legado_webSocket_entry_point),
  86. )
  87. socket.onerror = wsOnError
  88. socket.onopen = () => {
  89. socket.send(`{"key":"${searchKey}"}`)
  90. }
  91. socket.onmessage = event => {
  92. try {
  93. onReceive(JSON.parse(event.data))
  94. wsOnMessage?.call(socket, event)
  95. } catch {
  96. onFinish()
  97. }
  98. }
  99. socket.onclose = () => {
  100. onFinish()
  101. }
  102. }
  103. const saveBook = (book: BaseBook) =>
  104. ajax.post<LeagdoApiResponse<string>>('saveBook', book)
  105. const deleteBook = (book: BaseBook) =>
  106. ajax.post<LeagdoApiResponse<string>>('deleteBook', book)
  107. const isBookSource = /bookSource/i.test(location.href)
  108. // 源编辑API
  109. // Http
  110. const getSources = () =>
  111. isBookSource ? ajax.get('getBookSources') : ajax.get('getRssSources')
  112. const saveSource = (data: Source) =>
  113. isBookSource
  114. ? ajax.post<LeagdoApiResponse<string>>('saveBookSource', data)
  115. : ajax.post<LeagdoApiResponse<string>>('saveRssSource', data)
  116. const saveSources = (data: Source[]) =>
  117. isBookSource
  118. ? ajax.post<LeagdoApiResponse<Source[]>>('saveBookSources', data)
  119. : ajax.post<LeagdoApiResponse<Source[]>>('saveRssSources', data)
  120. const deleteSource = (data: Source[]) =>
  121. isBookSource
  122. ? ajax.post<LeagdoApiResponse<string>>('deleteBookSources', data)
  123. : ajax.post<LeagdoApiResponse<string>>('deleteRssSources', data)
  124. // webSocket
  125. const debug = (
  126. /** @type {string} */ sourceUrl: string,
  127. /** @type {string} */ searchKey: string,
  128. /** @type {(data: string) => void} */ onReceive: (data: string) => void,
  129. /** @type {() => void} */ onFinish: () => void,
  130. ) => {
  131. const url = new URL(
  132. `${isBookSource ? 'bookSource' : 'rssSource'}Debug`,
  133. legado_webSocket_entry_point,
  134. )
  135. const socket = new WebSocket(url)
  136. socket.onerror = wsOnError
  137. socket.onopen = () => {
  138. socket.send(JSON.stringify({ tag: sourceUrl, key: searchKey }))
  139. }
  140. socket.onmessage = event => {
  141. onReceive(event.data)
  142. wsOnMessage?.call(socket, event)
  143. }
  144. socket.onclose = () => {
  145. onFinish()
  146. }
  147. }
  148. /**
  149. * 从阅读获取需要特定处理的书籍封面
  150. * @param {string} coverUrl
  151. */
  152. const getProxyCoverUrl = (coverUrl: string) => {
  153. if (coverUrl.startsWith(legado_http_entry_point)) return coverUrl
  154. return new URL(
  155. 'cover?path=' + encodeURIComponent(coverUrl),
  156. legado_http_entry_point,
  157. ).toString()
  158. }
  159. /**
  160. * 从阅读获取需要特定处理的图片
  161. * @param {string} bookUrl
  162. * @param {string} src
  163. * @param {number|`${number}`} width
  164. */
  165. const getProxyImageUrl = (
  166. bookUrl: string,
  167. src: string,
  168. width: number | `${number}`,
  169. ) => {
  170. if (src.startsWith(legado_http_entry_point)) return src
  171. return new URL(
  172. 'image?path=' +
  173. encodeURIComponent(src) +
  174. '&url=' +
  175. encodeURIComponent(bookUrl) +
  176. '&width=' +
  177. width,
  178. legado_http_entry_point,
  179. ).toString()
  180. }
  181. export default {
  182. getReadConfig,
  183. saveReadConfig,
  184. saveBookProgress,
  185. saveBookProgressWithBeacon,
  186. getBookShelf,
  187. getChapterList,
  188. getBookContent,
  189. search,
  190. saveBook,
  191. deleteBook,
  192. getSources,
  193. saveSources,
  194. saveSource,
  195. deleteSource,
  196. debug,
  197. getProxyCoverUrl,
  198. getProxyImageUrl,
  199. }