danmuSpider.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * @File : danmuSpider.js
  3. * @Author : jade
  4. * @Date : 2024/3/13 13:39
  5. * @Email : jadehh@1ive.com
  6. * @Software : Samples
  7. * @Desc :
  8. */
  9. import {_, load, Uri} from "./cat.js";
  10. import * as Utils from "./utils.js";
  11. import {JadeLogging} from "./log.js";
  12. import {VodDetail, VodShort} from "./vod.js";
  13. import {parseXML} from "./bilibili_ASS_Danmaku_Downloader.js";
  14. class DanmuSpider {
  15. constructor() {
  16. this.siteUrl = "https://search.youku.com"
  17. this.reconnectTimes = 0
  18. this.maxReconnectTimes = 5
  19. this.jadeLog = new JadeLogging(this.getAppName(), "DEBUG")
  20. }
  21. getAppName() {
  22. return "弹幕"
  23. }
  24. getHeader() {
  25. return {"User-Agent": Utils.CHROME, "Referer": this.siteUrl + "/"};
  26. }
  27. async reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer) {
  28. await this.jadeLog.error("请求失败,请检查url:" + reqUrl + ",两秒后重试")
  29. Utils.sleep(2)
  30. if (this.reconnectTimes < this.maxReconnectTimes) {
  31. this.reconnectTimes = this.reconnectTimes + 1
  32. return await this.fetch(reqUrl, params, headers, redirect_url, return_cookie, buffer)
  33. } else {
  34. await this.jadeLog.error("请求失败,重连失败")
  35. return null
  36. }
  37. }
  38. async getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response) {
  39. {
  40. if (response.headers["location"] !== undefined) {
  41. if (redirect_url) {
  42. await this.jadeLog.debug(`返回重定向连接:${response.headers["location"]}`)
  43. return response.headers["location"]
  44. } else {
  45. return this.fetch(response.headers["location"], params, headers, redirect_url, return_cookie, buffer)
  46. }
  47. } else if (response.content.length > 0) {
  48. this.reconnectTimes = 0
  49. if (return_cookie) {
  50. return {"cookie": response.headers["set-cookie"], "content": response.content}
  51. } else {
  52. return response.content
  53. }
  54. } else if (buffer === 1) {
  55. this.reconnectTimes = 0
  56. return response.content
  57. } else {
  58. await this.jadeLog.error(`请求失败,请求url为:${reqUrl},回复内容为:${JSON.stringify(response)}`)
  59. return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
  60. }
  61. }
  62. }
  63. async fetch(reqUrl, params, headers, redirect_url = false, return_cookie = false, buffer = 0) {
  64. let data = Utils.objectToStr(params)
  65. let url = reqUrl
  66. if (!_.isEmpty(data)) {
  67. url = reqUrl + "?" + data
  68. }
  69. let uri = new Uri(url);
  70. let response;
  71. response = await req(uri.toString(), {method: "get", headers: headers, buffer: buffer, data: null})
  72. if (response.code === 200 || response.code === 302 || response.code === 301 || return_cookie) {
  73. return await this.getResponse(reqUrl, params, headers, redirect_url, return_cookie, buffer, response)
  74. } else {
  75. await this.jadeLog.error(`请求失败,失败原因为:状态码出错,请求url为:${uri},回复内容为:${JSON.stringify(response)}`)
  76. return await this.reconnnect(reqUrl, params, headers, redirect_url, return_cookie, buffer)
  77. }
  78. }
  79. async getHtml(url = this.siteUrl, headers = this.getHeader()) {
  80. let html = await this.fetch(url, null, headers)
  81. if (!_.isEmpty(html)) {
  82. return load(html)
  83. } else {
  84. await this.jadeLog.error(`html获取失败`, true)
  85. }
  86. }
  87. async parseVodShortListFromJson(obj, vodDetail) {
  88. for (const componentObj of obj["pageComponentList"]) {
  89. if (componentObj["commonData"] !== undefined) {
  90. let searchVodDetail = new VodDetail()
  91. let commonData = componentObj["commonData"]
  92. searchVodDetail.type_name = commonData["feature"]
  93. if (commonData["notice"] !== undefined) {
  94. searchVodDetail.vod_actor = commonData["notice"].replaceAll("演员:", "").replaceAll(" ", "")
  95. }
  96. if (commonData["director"] !== undefined) {
  97. searchVodDetail.vod_director = commonData["director"].replaceAll("导演:", "").replaceAll(" ", "")
  98. }
  99. if (vodDetail.type_name === "电影") {
  100. searchVodDetail.vod_id = commonData["leftButtonDTO"]["action"]["value"]
  101. } else {
  102. searchVodDetail.vod_id = commonData["showId"]
  103. }
  104. searchVodDetail.vod_name = commonData["titleDTO"]["displayName"]
  105. if ( searchVodDetail.vod_name === vodDetail.vod_name || searchVodDetail.type_name.indexOf(vodDetail.vod_year) > -1 || searchVodDetail.type_name.indexOf(vodDetail.type_name) > -1 || searchVodDetail.vod_director === vodDetail.vod_director) {
  106. await this.jadeLog.debug(`匹配视频网站成功,名称为:${searchVodDetail.vod_name},类型为:${searchVodDetail.type_name},导演为:${searchVodDetail.vod_director}`, true)
  107. return searchVodDetail
  108. }
  109. }
  110. }
  111. await this.jadeLog.warning("没有匹配到弹幕网站")
  112. return null
  113. }
  114. async parseVodUrlFromJsonByEpisodeId(obj, episodeId) {
  115. for (const serises of obj["serisesList"]) {
  116. if (Utils.isNumeric(episodeId["episodeId"])) {
  117. if (parseInt(episodeId["episodeId"]).toString() === serises["displayName"]) {
  118. return serises["action"]["value"]
  119. }
  120. }
  121. }
  122. await this.jadeLog.error("没有找到匹配的集数")
  123. return ""
  124. }
  125. async downloadDanmu(url) {
  126. let json = JSON.parse(await this.fetch(url, null, this.getHeader()))
  127. let xml = parseXML(json)
  128. let params = {"do": "set", "key": "danmu", "value": xml}
  129. await req("http://127.0.0.1:9978/cache", {method: "post", data: params, postType: "form-data"});
  130. return "http://127.0.0.1:9978/cache?do=get&key=danmu"
  131. }
  132. async search(vodDetail, episodeId) {
  133. let params = {"pg": "1", "keyword": vodDetail.vod_name}
  134. let searchObj = JSON.parse(await this.fetch(this.siteUrl + "/api/search", params, this.getHeader()))
  135. let searchDetail = await this.parseVodShortListFromJson(searchObj, vodDetail)
  136. if (!_.isEmpty(searchDetail)){
  137. return await this.getVideoUrl(searchDetail.vod_id, episodeId)
  138. }else{
  139. return ""
  140. }
  141. }
  142. async getVideoUrl(showId, episodeId) {
  143. let url = "";
  144. if (!_.isEmpty(showId)) {
  145. if (showId.startsWith("http")) {
  146. url = showId
  147. } else {
  148. let params = {"appScene": "show_episode", "showIds": showId}
  149. let matchObj = JSON.parse(await this.fetch(this.siteUrl + "/api/search", params, this.getHeader()))
  150. url = await this.parseVodUrlFromJsonByEpisodeId(matchObj, episodeId)
  151. }
  152. if (!_.isEmpty(url)) {
  153. await this.jadeLog.debug(`弹幕视频播放连接为:${url}`)
  154. return await this.downloadDanmu("https://dmku.thefilehosting.com/?ac=dm&url=" + url)
  155. }
  156. }
  157. return url
  158. }
  159. async getDammu(voddetail, episodeId) {
  160. return await this.search(voddetail, episodeId)
  161. }
  162. }
  163. export {DanmuSpider}