douban.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. /*
  2. * @File : nivod.js
  3. * @Author : jade
  4. * @Date : 2023/12/19 14:23
  5. * @Email : jadehh@1ive.com
  6. * @Software : Samples
  7. * @Desc :
  8. */
  9. import {VodShort} from "../lib/vod.js";
  10. import {Spider} from "./spider.js";
  11. import {_, Crypto} from "../lib/cat.js";
  12. class DoubanSpider extends Spider {
  13. constructor() {
  14. super();
  15. this.siteUrl = 'https://frodo.douban.com/api/v2'
  16. this.apiKey = "0ac44ae016490db2204ce0a042db2916"
  17. this.UserAgents = ["api-client/1 com.douban.frodo/7.22.0.beta9(231) Android/23 product/Mate 40 vendor/HUAWEI model/Mate 40 brand/HUAWEI rom/android network/wifi platform/AndroidPad", "api-client/1 com.douban.frodo/7.18.0(230) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1", "api-client/1 com.douban.frodo/7.1.0(205) Android/29 product/perseus vendor/Xiaomi model/Mi MIX 3 rom/miui6 network/wifi platform/mobile nd/1", "api-client/1 com.douban.frodo/7.3.0(207) Android/22 product/MI 9 vendor/Xiaomi model/MI 9 brand/Android rom/miui6 network/wifi platform/mobile nd/1"]
  18. }
  19. getHeader() {
  20. return {
  21. "Host": "frodo.douban.com",
  22. "Connection": "Keep-Alive",
  23. "Referer": "https://servicewechat.com/wx2f9b06c1de1ccfca/84/page-frame.html",
  24. "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 MicroMessenger/7.0.9.501 NetType/WIFI MiniProgramEnv/Windows WindowsWechat"
  25. }
  26. }
  27. getSearchHeader() {
  28. let randomNumber = Math.floor(Math.random() * this.UserAgents.length); // 生成一个介于0到9之间的随机整数
  29. return {
  30. 'User-Agent': this.UserAgents[randomNumber]
  31. }
  32. }
  33. getName() {
  34. return "🍥┃豆瓣┃🍥"
  35. }
  36. getAppName() {
  37. return "豆瓣"
  38. }
  39. getJSName() {
  40. return "douban"
  41. }
  42. getType() {
  43. return 3
  44. }
  45. async setClasses() {
  46. let classes = [{
  47. "type_name": "热门电影", "type_id": "hot_gaia"
  48. }, {
  49. "type_name": "热播剧集", "type_id": "tv_hot"
  50. }, {
  51. "type_name": "热播综艺", "type_id": "show_hot"
  52. }, {
  53. "type_name": "电影筛选", "type_id": "movie"
  54. }, {
  55. "type_name": "电视筛选", "type_id": "tv"
  56. }, {
  57. "type_name": "电影榜单", "type_id": "rank_list_movie"
  58. }, {
  59. "type_name": "电视剧榜单", "type_id": "rank_list_tv"
  60. }]
  61. for (const class_dic of classes) {
  62. this.classes.push({"type_name": class_dic["type_name"], "type_id": class_dic["type_id"]})
  63. }
  64. }
  65. async setFilterObj() {
  66. this.filterObj = {
  67. "hot_gaia": [{
  68. "key": "sort", "name": "排序", "value": [{
  69. "n": "热度", "v": "recommend"
  70. }, {
  71. "n": "最新", "v": "time"
  72. }, {
  73. "n": "评分", "v": "rank"
  74. }]
  75. }, {
  76. "key": "area", "name": "地区", "value": [{
  77. "n": "全部", "v": "全部"
  78. }, {
  79. "n": "华语", "v": "华语"
  80. }, {
  81. "n": "欧美", "v": "欧美"
  82. }, {
  83. "n": "韩国", "v": "韩国"
  84. }, {
  85. "n": "日本", "v": "日本"
  86. }]
  87. }], "tv_hot": [{
  88. "key": "type", "name": "分类", "value": [{
  89. "n": "综合", "v": "tv_hot"
  90. }, {
  91. "n": "国产剧", "v": "tv_domestic"
  92. }, {
  93. "n": "欧美剧", "v": "tv_american"
  94. }, {
  95. "n": "日剧", "v": "tv_japanese"
  96. }, {
  97. "n": "韩剧", "v": "tv_korean"
  98. }, {
  99. "n": "动画", "v": "tv_animation"
  100. }]
  101. }], "show_hot": [{
  102. "key": "type", "name": "分类", "value": [{
  103. "n": "综合", "v": "show_hot"
  104. }, {
  105. "n": "国内", "v": "show_domestic"
  106. }, {
  107. "n": "国外", "v": "show_foreign"
  108. }]
  109. }], "movie": [{
  110. "key": "类型", "name": "类型", "value": [{
  111. "n": "全部类型", "v": ""
  112. }, {
  113. "n": "喜剧", "v": "喜剧"
  114. }, {
  115. "n": "爱情", "v": "爱情"
  116. }, {
  117. "n": "动作", "v": "动作"
  118. }, {
  119. "n": "科幻", "v": "科幻"
  120. }, {
  121. "n": "动画", "v": "动画"
  122. }, {
  123. "n": "悬疑", "v": "悬疑"
  124. }, {
  125. "n": "犯罪", "v": "犯罪"
  126. }, {
  127. "n": "惊悚", "v": "惊悚"
  128. }, {
  129. "n": "冒险", "v": "冒险"
  130. }, {
  131. "n": "音乐", "v": "音乐"
  132. }, {
  133. "n": "历史", "v": "历史"
  134. }, {
  135. "n": "奇幻", "v": "奇幻"
  136. }, {
  137. "n": "恐怖", "v": "恐怖"
  138. }, {
  139. "n": "战争", "v": "战争"
  140. }, {
  141. "n": "传记", "v": "传记"
  142. }, {
  143. "n": "歌舞", "v": "歌舞"
  144. }, {
  145. "n": "武侠", "v": "武侠"
  146. }, {
  147. "n": "情色", "v": "情色"
  148. }, {
  149. "n": "灾难", "v": "灾难"
  150. }, {
  151. "n": "西部", "v": "西部"
  152. }, {
  153. "n": "纪录片", "v": "纪录片"
  154. }, {
  155. "n": "短片", "v": "短片"
  156. }]
  157. }, {
  158. "key": "地区", "name": "地区", "value": [{
  159. "n": "全部地区", "v": ""
  160. }, {
  161. "n": "华语", "v": "华语"
  162. }, {
  163. "n": "欧美", "v": "欧美"
  164. }, {
  165. "n": "韩国", "v": "韩国"
  166. }, {
  167. "n": "日本", "v": "日本"
  168. }, {
  169. "n": "中国大陆", "v": "中国大陆"
  170. }, {
  171. "n": "美国", "v": "美国"
  172. }, {
  173. "n": "中国香港", "v": "中国香港"
  174. }, {
  175. "n": "中国台湾", "v": "中国台湾"
  176. }, {
  177. "n": "英国", "v": "英国"
  178. }, {
  179. "n": "法国", "v": "法国"
  180. }, {
  181. "n": "德国", "v": "德国"
  182. }, {
  183. "n": "意大利", "v": "意大利"
  184. }, {
  185. "n": "西班牙", "v": "西班牙"
  186. }, {
  187. "n": "印度", "v": "印度"
  188. }, {
  189. "n": "泰国", "v": "泰国"
  190. }, {
  191. "n": "俄罗斯", "v": "俄罗斯"
  192. }, {
  193. "n": "加拿大", "v": "加拿大"
  194. }, {
  195. "n": "澳大利亚", "v": "澳大利亚"
  196. }, {
  197. "n": "爱尔兰", "v": "爱尔兰"
  198. }, {
  199. "n": "瑞典", "v": "瑞典"
  200. }, {
  201. "n": "巴西", "v": "巴西"
  202. }, {
  203. "n": "丹麦", "v": "丹麦"
  204. }]
  205. }, {
  206. "key": "sort", "name": "排序", "value": [{
  207. "n": "近期热度", "v": "T"
  208. }, {
  209. "n": "首映时间", "v": "R"
  210. }, {
  211. "n": "高分优先", "v": "S"
  212. }]
  213. }, {
  214. "key": "年代", "name": "年代", "value": [{
  215. "n": "全部年代", "v": ""
  216. }, {
  217. "n": "2023", "v": "2023"
  218. }, {
  219. "n": "2022", "v": "2022"
  220. }, {
  221. "n": "2021", "v": "2021"
  222. }, {
  223. "n": "2020", "v": "2020"
  224. }, {
  225. "n": "2019", "v": "2019"
  226. }, {
  227. "n": "2010年代", "v": "2010年代"
  228. }, {
  229. "n": "2000年代", "v": "2000年代"
  230. }, {
  231. "n": "90年代", "v": "90年代"
  232. }, {
  233. "n": "80年代", "v": "80年代"
  234. }, {
  235. "n": "70年代", "v": "70年代"
  236. }, {
  237. "n": "60年代", "v": "60年代"
  238. }, {
  239. "n": "更早", "v": "更早"
  240. }]
  241. }], "tv": [{
  242. "key": "类型", "name": "类型", "value": [{
  243. "n": "不限", "v": ""
  244. }, {
  245. "n": "电视剧", "v": "电视剧"
  246. }, {
  247. "n": "综艺", "v": "综艺"
  248. }]
  249. }, {
  250. "key": "电视剧形式", "name": "电视剧形式", "value": [{
  251. "n": "不限", "v": ""
  252. }, {
  253. "n": "喜剧", "v": "喜剧"
  254. }, {
  255. "n": "爱情", "v": "爱情"
  256. }, {
  257. "n": "悬疑", "v": "悬疑"
  258. }, {
  259. "n": "动画", "v": "动画"
  260. }, {
  261. "n": "武侠", "v": "武侠"
  262. }, {
  263. "n": "古装", "v": "古装"
  264. }, {
  265. "n": "家庭", "v": "家庭"
  266. }, {
  267. "n": "犯罪", "v": "犯罪"
  268. }, {
  269. "n": "科幻", "v": "科幻"
  270. }, {
  271. "n": "恐怖", "v": "恐怖"
  272. }, {
  273. "n": "历史", "v": "历史"
  274. }, {
  275. "n": "战争", "v": "战争"
  276. }, {
  277. "n": "动作", "v": "动作"
  278. }, {
  279. "n": "冒险", "v": "冒险"
  280. }, {
  281. "n": "传记", "v": "传记"
  282. }, {
  283. "n": "剧情", "v": "剧情"
  284. }, {
  285. "n": "奇幻", "v": "奇幻"
  286. }, {
  287. "n": "惊悚", "v": "惊悚"
  288. }, {
  289. "n": "灾难", "v": "灾难"
  290. }, {
  291. "n": "歌舞", "v": "歌舞"
  292. }, {
  293. "n": "音乐", "v": "音乐"
  294. }]
  295. }, {
  296. "key": "综艺形式", "name": "综艺形式", "value": [{
  297. "n": "不限", "v": ""
  298. }, {
  299. "n": "真人秀", "v": "真人秀"
  300. }, {
  301. "n": "脱口秀", "v": "脱口秀"
  302. }, {
  303. "n": "音乐", "v": "音乐"
  304. }, {
  305. "n": "歌舞", "v": "歌舞"
  306. }]
  307. }, {
  308. "key": "地区", "name": "地区", "value": [{
  309. "n": "全部地区", "v": ""
  310. }, {
  311. "n": "华语", "v": "华语"
  312. }, {
  313. "n": "欧美", "v": "欧美"
  314. }, {
  315. "n": "国外", "v": "国外"
  316. }, {
  317. "n": "韩国", "v": "韩国"
  318. }, {
  319. "n": "日本", "v": "日本"
  320. }, {
  321. "n": "中国大陆", "v": "中国大陆"
  322. }, {
  323. "n": "中国香港", "v": "中国香港"
  324. }, {
  325. "n": "美国", "v": "美国"
  326. }, {
  327. "n": "英国", "v": "英国"
  328. }, {
  329. "n": "泰国", "v": "泰国"
  330. }, {
  331. "n": "中国台湾", "v": "中国台湾"
  332. }, {
  333. "n": "意大利", "v": "意大利"
  334. }, {
  335. "n": "法国", "v": "法国"
  336. }, {
  337. "n": "德国", "v": "德国"
  338. }, {
  339. "n": "西班牙", "v": "西班牙"
  340. }, {
  341. "n": "俄罗斯", "v": "俄罗斯"
  342. }, {
  343. "n": "瑞典", "v": "瑞典"
  344. }, {
  345. "n": "巴西", "v": "巴西"
  346. }, {
  347. "n": "丹麦", "v": "丹麦"
  348. }, {
  349. "n": "印度", "v": "印度"
  350. }, {
  351. "n": "加拿大", "v": "加拿大"
  352. }, {
  353. "n": "爱尔兰", "v": "爱尔兰"
  354. }, {
  355. "n": "澳大利亚", "v": "澳大利亚"
  356. }]
  357. }, {
  358. "key": "sort", "name": "排序", "value": [{
  359. "n": "近期热度", "v": "T"
  360. }, {
  361. "n": "首播时间", "v": "R"
  362. }, {
  363. "n": "高分优先", "v": "S"
  364. }]
  365. }, {
  366. "key": "年代", "name": "年代", "value": [{
  367. "n": "全部", "v": ""
  368. }, {
  369. "n": "2023", "v": "2023"
  370. }, {
  371. "n": "2022", "v": "2022"
  372. }, {
  373. "n": "2021", "v": "2021"
  374. }, {
  375. "n": "2020", "v": "2020"
  376. }, {
  377. "n": "2019", "v": "2019"
  378. }, {
  379. "n": "2010年代", "v": "2010年代"
  380. }, {
  381. "n": "2000年代", "v": "2000年代"
  382. }, {
  383. "n": "90年代", "v": "90年代"
  384. }, {
  385. "n": "80年代", "v": "80年代"
  386. }, {
  387. "n": "70年代", "v": "70年代"
  388. }, {
  389. "n": "60年代", "v": "60年代"
  390. }, {
  391. "n": "更早", "v": "更早"
  392. }]
  393. }, {
  394. "key": "平台", "name": "平台", "value": [{
  395. "n": "全部", "v": ""
  396. }, {
  397. "n": "腾讯视频", "v": "腾讯视频"
  398. }, {
  399. "n": "爱奇艺", "v": "爱奇艺"
  400. }, {
  401. "n": "优酷", "v": "优酷"
  402. }, {
  403. "n": "湖南卫视", "v": "湖南卫视"
  404. }, {
  405. "n": "Netflix", "v": "Netflix"
  406. }, {
  407. "n": "HBO", "v": "HBO"
  408. }, {
  409. "n": "BBC", "v": "BBC"
  410. }, {
  411. "n": "NHK", "v": "NHK"
  412. }, {
  413. "n": "CBS", "v": "CBS"
  414. }, {
  415. "n": "NBC", "v": "NBC"
  416. }, {
  417. "n": "tvN", "v": "tvN"
  418. }]
  419. }], "rank_list_movie": [{
  420. "key": "榜单", "name": "榜单", "value": [{
  421. "n": "实时热门电影", "v": "movie_real_time_hotest"
  422. }, {
  423. "n": "一周口碑电影榜", "v": "movie_weekly_best"
  424. }, {
  425. "n": "豆瓣电影Top250", "v": "movie_top250"
  426. }]
  427. }], "rank_list_tv": [{
  428. "key": "榜单", "name": "榜单", "value": [{
  429. "n": "实时热门电视", "v": "tv_real_time_hotest"
  430. }, {
  431. "n": "华语口碑剧集榜", "v": "tv_chinese_best_weekly"
  432. }, {
  433. "n": "全球口碑剧集榜", "v": "tv_global_best_weekly"
  434. }, {
  435. "n": "国内口碑综艺榜", "v": "show_chinese_best_weekly"
  436. }, {
  437. "n": "国外口碑综艺榜", "v": "show_global_best_weekly"
  438. }]
  439. }]
  440. }
  441. }
  442. async parseVodShortListFromJson(obj) {
  443. let vod_list = []
  444. for (const item of obj) {
  445. let vod_short = new VodShort()
  446. vod_short.vod_id = "msearch:" + item["id"]
  447. if (item["title"] === undefined) {
  448. vod_short.vod_name = item["target"]["title"]
  449. } else {
  450. vod_short.vod_name = item["title"]
  451. }
  452. if (item["pic"] === undefined) {
  453. vod_short.vod_pic = item["target"]["cover_url"]
  454. } else {
  455. vod_short.vod_pic = item["pic"]["normal"]
  456. }
  457. if (item["rating"] === undefined) {
  458. vod_short.vod_remarks = "评分:" + item["target"]["rating"]["value"].toString()
  459. } else {
  460. vod_short.vod_remarks = "评分:" + item["rating"]["value"].toString()
  461. }
  462. vod_list.push(vod_short);
  463. }
  464. return vod_list
  465. }
  466. get_tags(extend) {
  467. let tag_list = []
  468. for (const key of Object.keys(extend)) {
  469. if (key !== "sort") {
  470. tag_list.push(extend[key])
  471. }
  472. }
  473. return tag_list.join(",")
  474. }
  475. sign(url, ts, method = 'GET') {
  476. let _api_secret_key = "bf7dddc7c9cfe6f7"
  477. let url_path = "%2F" + url.split("/").slice(3).join("%2F")
  478. let raw_sign = [method.toLocaleUpperCase(), url_path, ts.toString()].join("&")
  479. return Crypto.HmacSHA1(raw_sign, _api_secret_key).toString(Crypto.enc.Base64)
  480. }
  481. async setHomeVod() {
  482. let url = this.siteUrl + "/subject_collection/subject_real_time_hotest/items"
  483. let content = await this.fetch(url, {"apikey": this.apiKey}, this.getHeader())
  484. if (!_.isEmpty(content)) {
  485. let content_json = JSON.parse(content)
  486. let items = content_json["subject_collection_items"]
  487. this.homeVodList = await this.parseVodShortListFromJson(items)
  488. }
  489. }
  490. async setCategory(tid, pg, filter, extend) {
  491. let sort = extend["sort"] ?? "show_hot";
  492. let tag_str = this.get_tags(extend)
  493. this.count = 0
  494. this.limit = 20;
  495. this.total = 0;
  496. let start = 0
  497. if (parseInt(pg) > 1) {
  498. start = (parseInt(pg) - 1) * this.limit
  499. }
  500. let cateUrl = ""
  501. let params = {"start": start.toString(), "count": this.limit.toString()}
  502. let itemKey = "items"
  503. switch (tid) {
  504. case "hot_gaia":
  505. sort = extend["sort"] ?? "recommend"
  506. let area = extend["area"] ?? "全部"
  507. params["sort"] = sort
  508. params["area"] = area
  509. cateUrl = "/movie/hot_gaia"
  510. break
  511. case "tv_hot":
  512. let type = extend["type"] ?? "tv_hot"
  513. cateUrl = "/subject_collection/" + type + "/items"
  514. itemKey = "subject_collection_items"
  515. break
  516. case "show_hot":
  517. let showType = extend["type"] ?? "show_hot"
  518. cateUrl = "/subject_collection/" + showType + "/items"
  519. itemKey = "subject_collection_items";
  520. break
  521. case "movie":
  522. params["sort"] = sort
  523. params["tags"] = tag_str
  524. cateUrl = "/movie/recommend"
  525. break
  526. case "tv":
  527. params["sort"] = sort
  528. params["tags"] = tag_str
  529. cateUrl = "/tv/recommend"
  530. break
  531. case "rank_list_movie":
  532. let rankMovieType = extend["榜单"] ?? "movie_real_time_hotest"
  533. cateUrl = "/subject_collection/" + rankMovieType + "/items"
  534. itemKey = "subject_collection_items"
  535. break
  536. case "rank_list_tv":
  537. let rankTVType = extend["榜单"] ?? "tv_real_time_hotest"
  538. cateUrl = "/subject_collection/" + rankTVType + "/items"
  539. itemKey = "subject_collection_items"
  540. break
  541. default:
  542. break
  543. }
  544. params["apikey"] = this.apiKey
  545. let content = await this.fetch(this.siteUrl + cateUrl, params, this.getHeader())
  546. if (!_.isEmpty(content)) {
  547. let content_json = JSON.parse(content)
  548. let items = content_json[itemKey]
  549. this.vodList = await this.parseVodShortListFromJson(items)
  550. }
  551. }
  552. async setSearch(wd, quick, pg) {
  553. let _api_url = "https://frodo.douban.com/api/v2"
  554. let _api_key = "0dad551ec0f84ed02907ff5c42e8ec70"
  555. let url = _api_url + "/search/movie"
  556. let date = new Date()
  557. let ts = date.getFullYear().toString() + ('0' + (date.getMonth() + 1)).slice(-2).toString() + date.getDate().toString()
  558. const limit = 20
  559. let params = {
  560. '_sig': this.sign(url, ts),
  561. '_ts': ts,
  562. 'apiKey': _api_key,
  563. 'count': limit,
  564. 'os_rom': 'android',
  565. 'q': encodeURIComponent(wd),
  566. 'start': parseInt(pg)
  567. }
  568. let content = await this.fetch(url, params, this.getSearchHeader())
  569. if (!_.isEmpty(content)) {
  570. let content_json = JSON.parse(content)
  571. this.vodList = await this.parseVodShortListFromJson(content_json["items"])
  572. const page = parseInt(pg);
  573. let pageCount = page;
  574. if (this.vodList.length === limit) {
  575. pageCount = page + 1;
  576. }
  577. this.result.setPage(page, pageCount, limit, pageCount)
  578. }
  579. }
  580. }
  581. let spider = new DoubanSpider()
  582. async function init(cfg) {
  583. await spider.init(cfg)
  584. }
  585. async function home(filter) {
  586. return await spider.home(filter)
  587. }
  588. async function homeVod() {
  589. return await spider.homeVod()
  590. }
  591. async function category(tid, pg, filter, extend) {
  592. return await spider.category(tid, pg, filter, extend)
  593. }
  594. async function detail(id) {
  595. return await spider.detail(id)
  596. }
  597. async function play(flag, id, flags) {
  598. return await spider.play(flag, id, flags)
  599. }
  600. async function search(wd, quick) {
  601. return await spider.search(wd, quick)
  602. }
  603. export function __jsEvalReturn() {
  604. return {
  605. init: init, home: home, homeVod: homeVod, category: category, detail: detail, play: play, search: search,
  606. };
  607. }
  608. export {spider}