MediaFetchAPI

Media Database API

当前为 2019-07-21 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/387700/718766/MediaFetchAPI.js

  1. // ==UserScript==
  2. // @name MediaFetchAPI
  3. // @namespace https://greasyfork.org/users/152136
  4. // @version 0.1
  5. // @description Media Database API
  6. // @author TYT
  7. // @require https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js
  8. // @require https://unpkg.com/number-to-chinese-words@^1.0/number-to-chinese-words.min.js
  9. // @grant GM_xmlhttpRequest
  10. // ==/UserScript==
  11. /* globals $ */
  12. (function() {
  13. 'use strict';
  14. class MediaFetchAPI {
  15. constructor(){
  16. this.converter = window.index.NumberToChineseWords;
  17. this.converter.labels = Object.assign(this.converter.labels, {
  18. digits : ['零','一', '二', '三', '四', '五', '六', '七', '八', '九'],
  19. units: ['','十', '百', '千', '万', '十', '百', '千', '亿', '十', '百', '千', '兆', '十', '百', '千', '京', '十', '百', '千', '垓']
  20. });
  21. this.doubanAPIKeyB64 = 'MGIyYmRlZGE0M2I1Njg4OTIxODM5YzhlY2IyMDM5OWI=';
  22. this.timeOut = 1e4;
  23. }
  24. errorStatus(reject){
  25. return {
  26. ontimeout: ()=>{
  27. reject({
  28. status: 440,
  29. statusText: 'Time Out'
  30. })
  31. },
  32. onerror: ()=>{
  33. reject({
  34. status: 441,
  35. statusText: 'Request Error'
  36. })
  37. },
  38. onabort: ()=>{
  39. reject({
  40. status: 442,
  41. statusText: 'Abort'
  42. })
  43. }
  44. }
  45. };
  46. url2Blob(url){
  47. return new Promise((resolve, reject)=>{
  48. let request = Object.assign({
  49. method: 'GET',
  50. url: url,
  51. timeout: 10000,
  52. responseType: 'blob',
  53. onload: (res)=>{
  54. resolve(res.response);
  55. }
  56. }, this.errorStatus(reject));
  57. GM_xmlhttpRequest(request);
  58. });
  59. }
  60. blob2DataURL(blob){
  61. return new Promise((resolve, reject)=>{
  62. let fr = new FileReader();
  63. fr.onload = (e)=>{
  64. resolve(e.target.result);
  65. }
  66. Object.assign(fr, this.errorStatus(reject));
  67. fr.readAsDataURL(blob);
  68. });
  69. }
  70. url2DataURL(url){
  71. return this.url2Blob(url)
  72. .then(this.blob2DataURL);
  73. }
  74. //Douban
  75. doubanSuggest(content){
  76. return new Promise((resolve, reject)=>{
  77. let request = Object.assign({
  78. method: 'GET',
  79. url: `https://movie.douban.com/j/subject_suggest?q=${encodeURIComponent(content)}`,
  80. timeout: this.timeOut,
  81. responseType: 'json',
  82. onload: (res)=>{
  83. if(res.status >= 200 && res.status < 300){
  84. resolve(res.response)
  85. }
  86. else{
  87. reject({
  88. status: res.status,
  89. statusText: res.statusText,
  90. });
  91. }
  92. },
  93. }, this.errorStatus(reject));
  94. GM_xmlhttpRequest(request);
  95. });
  96. }
  97. doubanDetail(id, api_key = atob(this.doubanAPIKeyB64)){
  98. return new Promise((resolve, reject)=>{
  99. let request = Object.assign({
  100. method: 'GET',
  101. url: `https://api.douban.com/v2/movie/${id}?apikey=${api_key}`,
  102. timeout: this.timeOut,
  103. responseType: 'json',
  104. onload: (res)=>{
  105. if(res.status >= 200 && res.status < 300){
  106. resolve(res.response)
  107. }
  108. else{
  109. reject({
  110. status: res.status,
  111. statusText: res.statusText,
  112. });
  113. }
  114. }
  115. }, this.errorStatus(reject));
  116. GM_xmlhttpRequest(request);
  117. });
  118. }
  119. doubanPage(id){
  120. return new Promise((resolve, reject)=>{
  121. let request = Object.assign({
  122. method: 'GET',
  123. url: `https://movie.douban.com/subject/${id}`,
  124. timeout: 10000,
  125. onload: (res)=>{
  126. if(res.status >= 200 && res.status < 300){
  127. resolve(res.response)
  128. }
  129. else{
  130. reject({
  131. status: res.status,
  132. statusText: res.statusText,
  133. });
  134. }
  135. }
  136. }, this.errorStatus(reject));
  137. GM_xmlhttpRequest(request);
  138. });
  139. }
  140. doubanCard(id, type = 'png', options = {}){
  141. const type_mapper = {
  142. png: 'toPng',
  143. blob: 'toBlob',
  144. jpg: 'toJpeg',
  145. svg: 'toSvg',
  146. pixel: 'toPixelData'
  147. };
  148. if(typeof type_mapper[type] === 'undefined'){
  149. return new Promise((resolve, reject)=>{
  150. reject({status: 443, statusText: 'Wrong Type'});
  151. });
  152. }
  153. let iframe = $('<iframe/>');
  154. iframe.css({
  155. position: 'absolute',
  156. width: 0,
  157. height: 0,
  158. top: -1e4,
  159. left: -1e4
  160. });
  161. iframe.appendTo($('body'));
  162. return new Promise((resolve, reject)=>{
  163. let request = Object.assign({
  164. method: 'GET',
  165. url: `https://movie.douban.com/subject/${id}/output_card`,
  166. timeout: this.timeOut,
  167. onload: (res)=>{
  168. if(res.status >= 200 && res.status < 300){
  169. function resolveMessage(message){
  170. if(message.data.status === 444){
  171. reject(message.data);
  172. }
  173. else{
  174. resolve(message.data);
  175. }
  176. window.removeEventListener('message', resolveMessage);
  177. iframe.remove();
  178. }
  179. window.addEventListener('message', resolveMessage);
  180. let snippet = `domtoimage.${type_mapper[type]}(document.getElementById("wrapper"),${JSON.stringify(options)}).then(t=>{window.top.postMessage(t,"*")}).catch(t=>{void 0===t||void 0===t.status||void 0===t.statusText?window.top.postMessage({status:444,statusText:"DOM to Image Error"},"*"):window.top.postMessage(t,"*")});`;
  181. let response = res.response.replace(/<script[^>]+output\-card\.js"><\/script>/, `<script>${snippet}</script>`);
  182. let bg_reg = /<div class="picture\-wrapper" style="background\-image\: url\(([^\)]+)\)">/;
  183. let target = bg_reg.exec(response);
  184. this.url2DataURL(target[1]).then((durl)=>{
  185. response = `${response.slice(0, target.index)}<div class="picture-wrapper" style="background-image: url(${durl})">${response.slice(target.index + target[0].length)}`;
  186. let idoc = iframe[0].contentWindow.document;
  187. idoc.write(response);
  188. }).catch((err)=>{
  189. reject(err);
  190. });
  191. }
  192. else{
  193. reject({
  194. status: res.status,
  195. statusText: res.statusText,
  196. });
  197. }
  198. }
  199. }, this.errorStatus(reject));
  200. GM_xmlhttpRequest(request);
  201. });
  202. }
  203. doubanAwards(id){
  204. return new Promise((resolve, reject)=>{
  205. let request = Object.assign({
  206. method: 'GET',
  207. url: `https://movie.douban.com/subject/${id}/awards`,
  208. timeout: this.timeOut,
  209. onload: (res)=>{
  210. if(res.status >= 200 && res.status < 300){
  211. const vd = document.implementation.createHTMLDocument('virtual');
  212. const article = $(res.response, vd).find('#content .article');
  213. let awards = [];
  214. for (let _awards of article.children('.awards')){
  215. let award = {};
  216. let temp = $(_awards).children('.hd');
  217. award.name = temp.find('a').text().trim();
  218. award.link = temp.find('a')[0].href;
  219. award.year = temp.find('.year').text().slice(2, -1);
  220. award.cats = [];
  221. for (let _award of $(_awards).children('.award')){
  222. let item = {};
  223. let temp = $(_award).children();
  224. [item.category, item.recipients] = [temp[0].innerText, temp[1].innerText.split(' / ')];
  225. if(item.recipients[0] === ''){
  226. item.recipients = [];
  227. }
  228. award.cats.push(item);
  229. }
  230. awards.push(award);
  231. }
  232. resolve(awards);
  233. }
  234. else{
  235. reject({
  236. status: res.status,
  237. statusText: res.statusText,
  238. });
  239. }
  240. }
  241. }, this.errorStatus(reject));
  242. GM_xmlhttpRequest(request);
  243. });
  244. }
  245. doubanCrew(id){
  246. function decouple(mixed){
  247. let match = mixed.match(/(^.*\p{Unified_Ideograph}[^ ]*) ([^\p{Unified_Ideograph}]+)$/u);
  248. if(match){
  249. return [match[1], match[2]];
  250. }
  251. else{
  252. return [mixed];
  253. }
  254. }
  255. return new Promise((resolve, reject)=>{
  256. let request = Object.assign({
  257. method: 'GET',
  258. url: `https://movie.douban.com/subject/${id}/celebrities`,
  259. timeout: this.timeOut,
  260. onload: (res)=>{
  261. if(res.status >= 200 && res.status < 300){
  262. const vd = document.implementation.createHTMLDocument('virtual');
  263. const celebrities = $(res.response, vd).find('#celebrities');
  264. let crew = [];
  265. for (let _list of $(celebrities).children('.list-wrapper')){
  266. let list = {
  267. profession: {}
  268. };
  269. [list.profession.pre, list.profession.alt = list.profession.pre] = $(_list).children('h2').text().split(' ');
  270. list.people = [];
  271. for (let _person of $(_list).find('.celebrities-list>.celebrity')){
  272. let person = {
  273. name: {},
  274. title: {}
  275. };
  276. [person.name.pre,
  277. person.name.alt = person.name.pre] = decouple(
  278. $(_person).find('.info>.name>a.name').text()
  279. );
  280. [person.title.pre,
  281. person.title.alt = person.title.pre] = decouple(
  282. $(_person).find('.info>.role').text().replace(/ \([^\)]+\)$/, '')
  283. ).map((e)=>e.split('/').map((e)=>e.trim()));
  284. if(list.profession.pre === '演员'){
  285. person.role = {
  286. pre: [],
  287. alt: []
  288. };
  289. let temp = $(_person).find('.info>.role').text().match(/ \([饰配] ([^\)]+)\)$/);
  290. if(temp){
  291. temp[1].split(' / ').forEach((e)=>{
  292. let temp_1, temp_2;
  293. [temp_1, temp_2 = temp_1] = decouple(e);
  294. person.role.pre.push(temp_1);
  295. person.role.alt.push(temp_2);
  296. });
  297. }
  298. }
  299. person.link = $(_person).find('.info>.name>a.name')[0].href;
  300. list.people.push(person);
  301. }
  302. crew.push(list);
  303. }
  304. resolve(crew);
  305. }
  306. else{
  307. reject({
  308. status: res.status,
  309. statusText: res.statusText,
  310. });
  311. }
  312. }
  313. }, this.errorStatus(reject));
  314. GM_xmlhttpRequest(request);
  315. });
  316. }
  317. doubanDetailPlus(id, api_key = atob(this.doubanAPIKeyB64)){
  318. return Promise.all([this.doubanDetail(id, api_key), this.doubanPage(id)])
  319. .then(
  320. ([json, doc]) => {
  321. const vd = document.implementation.createHTMLDocument('virtual');
  322. const vdoc = $(doc, vd);
  323. json.alt_title = [vdoc.filter('title').text().replace(/\(豆瓣\)\s?$/, '').trim()];
  324. json.title = vdoc.find('h1>span[property="v:itemreviewed"]').text().replace(json.alt_title[0], '').trim() || json.alt_title[0];
  325. let temp;
  326. if((temp = vdoc.find('#info>span.pl:contains("又名:")')[0]) !== undefined){
  327. json.alt_title = json.alt_title.concat(temp.nextSibling.nodeValue.trim().split(' / '))
  328. }
  329. json.imdb_id = null;
  330. return Promise.resolve(doubanRealIMDb(doc)).then(
  331. id=>{
  332. json.imdb_id = id;
  333. return json;
  334. }
  335. )
  336. }
  337. )
  338. function doubanRealIMDb(res){
  339. let e = douban1stSeason(res);
  340. if(typeof e !== 'undefined'){
  341. return this.doubanPage(e).then((e)=>doubanIMDb(e));
  342. }
  343. return doubanIMDb(res);
  344. function douban1stSeason(res){
  345. const vd = document.implementation.createHTMLDocument('virtual');
  346. const list = $(res, vd).find('#season');
  347. if(typeof list === 'undefined' || list.find(':selected').text() === '1'){
  348. return undefined
  349. }
  350. return list.children(':first').val();
  351. }
  352. function doubanIMDb(res){
  353. const vd = document.implementation.createHTMLDocument('virtual');
  354. const link = $(res, vd).find('#info>a[href*="imdb"]')[0];
  355. let id;
  356. if(typeof link === 'undefined' || typeof link.href === 'undefined' || typeof (id = link.href.match(/\d+$/)) === 'undefined'){
  357. return undefined;
  358. }
  359. return id[0];
  360. }
  361. }
  362. }
  363. //IMDb
  364. imdbRating(id){
  365. return new Promise((resolve, reject)=>{
  366. let request = Object.assign({
  367. method: 'GET',
  368. url: `https://p.media-imdb.com/static-content/documents/v1/title/tt${id}/ratings%3Fjsonp=imdb.rating.run:imdb.api.title.ratings/data.json`,
  369. timeout: this.timeOut,
  370. onload: (res)=>{
  371. if(res.status >= 200 && res.status < 300){
  372. resolve(JSON.parse(res.response.slice(16, -1)))
  373. }
  374. else{
  375. reject({
  376. status: res.status,
  377. statusText: res.statusText,
  378. });
  379. }
  380. }
  381. }, this.errorStatus(reject));
  382. GM_xmlhttpRequest(request);
  383. });
  384. }
  385. imdbSuggest(content){
  386. return new Promise((resolve, reject)=>{
  387. let request = Object.assign({
  388. method: 'GET',
  389. url: `https://v2.sg.media-imdb.com/suggestion/${content[0].toLowerCase()}/${encodeURIComponent(content).replace(/%20/g, '+')}.json`,
  390. timeout: this.timeOut,
  391. responseType: 'json',
  392. onload: (res)=>{
  393. if(res.status >= 200 && res.status < 300){
  394. resolve(res.response)
  395. }
  396. else{
  397. reject({
  398. status: res.status,
  399. statusText: res.statusText,
  400. });
  401. }
  402. }
  403. }, this.errorStatus(reject));
  404. GM_xmlhttpRequest(request);
  405. });
  406. }
  407. //MTime
  408. mtimeStatistic(id){
  409. return new Promise((resolve, reject)=>{
  410. let request = Object.assign({
  411. method: 'GET',
  412. url: 'http://service.library.mtime.com/Movie.api'
  413. + '?Ajax_CallBack=true'
  414. + '&Ajax_CallBackType=Mtime.Library.Services'
  415. + '&Ajax_CallBackMethod=GetMovieOverviewRating'
  416. + `&Ajax_CallBackArgument0=${id}`,
  417. timeout: this.timeOut,
  418. onload: (res)=>{
  419. if(res.status >= 200 && res.status < 300){
  420. let m = res.response.match(/^[^{]+({.*});\s*$/);
  421. let val;
  422. if(m && (val = JSON.parse(m[1]).value)){
  423. resolve(val);
  424. }
  425. }
  426. else{
  427. reject({
  428. status: res.status,
  429. statusText: res.statusText,
  430. });
  431. }
  432. }
  433. }, this.errorStatus(reject));
  434. GM_xmlhttpRequest(request);
  435. });
  436. }
  437. mtimeRating(id_array){
  438. if((typeof id_array === 'string' && id_array.match(/^\d+$/)) || Number.isInteger(id_array)){
  439. id_array = [id_array];
  440. }
  441. else if(!(id_array instanceof Array)){
  442. return Promise.reject({
  443. status: 446,
  444. statusText: 'Mtime Rating Argument Wrong Format'
  445. })
  446. }
  447. return new Promise((resolve, reject)=>{
  448. let request = Object.assign({
  449. method: 'GET',
  450. url: 'http://service.mtime.com/Service/Movie.msi'
  451. + '?Ajax_CallBack=true'
  452. + '&Ajax_CallBackType=Mtime.MemberCenter.Pages.MovieService'
  453. + '&Ajax_CallBackMethod=GetRatingsByMovieIds'
  454. + `&Ajax_CallBackArgument0=${id_array.join('|')}`,
  455. timeout: this.timeOut,
  456. onload: (res)=>{
  457. if(res.status >= 200 && res.status < 300){
  458. let m = res.response.match(/^[^{]+({.*});\s*$/);
  459. let val;
  460. if(m && (val = JSON.parse(m[1]).value)){
  461. resolve(val);
  462. }
  463. }
  464. else{
  465. reject({
  466. status: res.status,
  467. statusText: res.statusText,
  468. });
  469. }
  470. }
  471. }, this.errorStatus(reject));
  472. GM_xmlhttpRequest(request);
  473. });
  474. }
  475. mtimeQuery(content, count = 20){
  476. return new Promise((resolve, reject)=>{
  477. let request = Object.assign({
  478. method: 'GET',
  479. url: 'http://my.mtime.com/Service/Movie.mc'
  480. + '?Ajax_CallBack=true'
  481. + '&Ajax_CallBackType=Mtime.MemberCenter.Pages.MovieService'
  482. + '&Ajax_CallBackMethod=GetSearchMoviesByTitle'
  483. + `&Ajax_CallBackArgument0=${encodeURIComponent(content)}`
  484. + `&Ajax_CallBackArgument1=${count}`,
  485. timeout: 10000,
  486. onload: (res)=>{
  487. if(res.status >= 200 && res.status < 300){
  488. let val;
  489. if((val = JSON.parse(res.response).value)){
  490. resolve(val);
  491. }
  492. resolve(JSON.parse(res.response));
  493. }
  494. else{
  495. reject({
  496. status: res.status,
  497. statusText: res.statusText,
  498. });
  499. }
  500. }
  501. }, this.errorStatus(reject));
  502. GM_xmlhttpRequest(request);
  503. });
  504. }
  505. mtimeSearch(content){
  506. return new Promise((resolve, reject)=>{
  507. let request = Object.assign({
  508. method: 'GET',
  509. url: 'http://service-channel.mtime.com/Search.api'
  510. + '?Ajax_CallBack=true'
  511. + '&Ajax_CallBackType=Mtime.Channel.Services'
  512. + '&Ajax_CallBackMethod=GetSearchResult'
  513. + `&Ajax_CallBackArgument0=${encodeURIComponent(content)}`
  514. + '&Ajax_CallBackArgument2=1'
  515. + '&Ajax_CallBackArgument4=10000',
  516. timeout: this.timeOut,
  517. onload: (res)=>{
  518. if(res.status >= 200 && res.status < 300){
  519. let m = res.response.match(/^[^{]+({.*});\s*$/);
  520. let val;
  521. if(m && (val = JSON.parse(m[1]).value)){
  522. resolve(val.movieResult);
  523. }
  524. else{
  525. reject({
  526. status: 445,
  527. statusText: 'Mtime Error',
  528. });
  529. }
  530. }
  531. else{
  532. reject({
  533. status: res.status,
  534. statusText: res.statusText,
  535. });
  536. }
  537. }
  538. }, this.errorStatus(reject));
  539. GM_xmlhttpRequest(request);
  540. });
  541. }
  542. mtimeBehindTheScene(id){
  543. //revealed_list
  544. //revealed_lines
  545. //revealed_news
  546. //revealed_other
  547. //revealed_album
  548. let result = [];
  549. return new Promise((resolve, reject)=>{
  550. let request = Object.assign({
  551. method: 'GET',
  552. url: `http://movie.mtime.com/${id}/behind_the_scene.html`,
  553. timeout: 10000,
  554. onload: (res)=>{
  555. if(res.status >= 200 && res.status < 300){
  556. const vd = document.implementation.createHTMLDocument('virtual');
  557. const bts = $(res.response, vd).find('.revealed_modle');
  558. bts.map((i, e)=>{
  559. result[i] = {};
  560. result[i].preferred_title = $(e).find('h3').text().trim();
  561. result[i].alternative_title = $(e).find('h4').text().trim();
  562. switch($(e).children().filter('[class^="revealed_"]').attr('class')){
  563. case 'revealed_list':
  564. result[i].content = $(e).find('dl.revealed_list>dd').toArray()
  565. .map((e, i)=>e.innerText.trim().replace(new RegExp(`^${i+1}`), ''));
  566. result[i].type = 'list';
  567. break;
  568. case 'revealed_lines':
  569. result[i].content = $(e).find('div.revealed_lines>dl>dd').toArray()
  570. .map((e, i)=>{$(e).find('br').before(document.createTextNode('\n')).remove(); return e.innerText.trim()});
  571. result[i].type = 'lines';
  572. if (result[i].content.length === 0){
  573. result[i].content = $(e).find('div.revealed_album>div.revealed_album_list li').toArray()
  574. .map((e, i)=>`${$(e).attr('title')}:${$(e).attr('imgabstract')}`);
  575. result[i].type = 'album';
  576. }
  577. break;
  578. case 'revealed_other':
  579. $(e).find('div.revealed_other>p:empty').before(document.createTextNode('\x1995')).remove();
  580. $(e).find('div.revealed_other>p>br').before(document.createTextNode('\n')).remove();
  581. result[i].content = $(e).find('div.revealed_other').text().trim().split(/\x1995|^ {2}|\n {2}/u).filter(e=>e.length).map(e=>e.trim().replace(/^\-+|\-+$|^·/g, '').trim());
  582. result[i].type = 'other';
  583. break;
  584. case 'revealed_album':
  585. result[i].content = $(e).find('div.revealed_album>div.revealed_album_list li').toArray()
  586. .map((e, i)=>`${$(e).attr('title')}:${$(e).attr('imgabstract')}`);
  587. result[i].type = 'album';
  588. break;
  589. case 'revealed_news':
  590. result[i].content = $(e).find('h3>a').attr('href');
  591. result[i].type = 'news';
  592. break;
  593. }
  594. });
  595. resolve(result);
  596. }
  597. else{
  598. reject({
  599. status: res.status,
  600. statusText: res.statusText,
  601. });
  602. }
  603. }
  604. }, this.errorStatus(reject));
  605. GM_xmlhttpRequest(request);
  606. });
  607. }
  608. //MetaCritic
  609. metacriticSuggest(content, type = 'movie'){
  610. return new Promise((resolve, reject)=>{
  611. let request = Object.assign({
  612. method: 'POST',
  613. url: 'https://www.metacritic.com/autosearch',
  614. data: `search_term=${encodeURIComponent(content).replace(/%20/g, '+')}`,
  615. headers: {
  616. 'content-type': 'application/x-www-form-urlencoded',
  617. 'x-requested-with': 'XMLHttpRequest',
  618. 'referer': `//www.metacritic.com/${type}`
  619. },
  620. responseType: 'json',
  621. timeout: this.timeOut,
  622. onload: (res)=>{
  623. if(res.status >= 200 && res.status < 300){
  624. resolve(res.response);
  625. }
  626. else{
  627. reject({
  628. status: res.status,
  629. statusText: res.statusText,
  630. });
  631. }
  632. }
  633. }, this.errorStatus(reject));
  634. GM_xmlhttpRequest(request);
  635. });
  636. }
  637. //RottenTomatoes
  638. rottentomatoesSuggest(content, limit = 10000){
  639. return new Promise((resolve, reject)=>{
  640. let request = Object.assign({
  641. method: 'GET',
  642. url: `https://www.rottentomatoes.com/napi/search/?query=${encodeURIComponent(content).replace(/%20/g, '+')}&limit=${limit}`,
  643. responseType: 'json',
  644. timeout: this.timeOut,
  645. onload: (res)=>{
  646. if(res.status >= 200 && res.status < 300){
  647. resolve(res.response);
  648. }
  649. else{
  650. reject({
  651. status: res.status,
  652. statusText: res.statusText,
  653. });
  654. }
  655. }
  656. }, this.errorStatus(reject));
  657. GM_xmlhttpRequest(request);
  658. });
  659. }
  660. rottentomatoesSearch(content, limit = 10000){
  661. return new Promise((resolve, reject)=>{
  662. let request = Object.assign({
  663. method: 'GET',
  664. url: `https://www.rottentomatoes.com/api/private/v2.0/search?q=${encodeURIComponent(content).replace(/%20/g, '+')}&limit=${limit}`,
  665. responseType: 'json',
  666. timeout: this.timeOut,
  667. onload: (res)=>{
  668. if(res.status >= 200 && res.status < 300){
  669. resolve(res.response);
  670. }
  671. else{
  672. reject({
  673. status: res.status,
  674. statusText: res.statusText,
  675. });
  676. }
  677. }
  678. }, this.errorStatus(reject));
  679. GM_xmlhttpRequest(request);
  680. });
  681. }
  682. rottentomatoesId(url){
  683. return new Promise((resolve, reject)=>{
  684. let request = Object.assign({
  685. method: 'GET',
  686. url: url,
  687. timeout: this.timeOut,
  688. onload: (res)=>{
  689. if(res.status >= 200 && res.status < 300){
  690. const vd = document.implementation.createHTMLDocument('virtual');
  691. const id = $(res.response, vd).find('#rating-root').attr('data-movie-id');
  692. const emsId = (res.response.match(/emsId":"([^"]+)"/)||[undefined])[1];
  693. resolve({id: id, emsId: emsId});
  694. }
  695. else{
  696. reject({
  697. status: res.status,
  698. statusText: res.statusText,
  699. });
  700. }
  701. }
  702. }, this.errorStatus(reject));
  703. GM_xmlhttpRequest(request);
  704. });
  705. }
  706. rottentomatoesDetail(id){
  707. return new Promise((resolve, reject)=>{
  708. let request = Object.assign({
  709. method: 'GET',
  710. url: `https://www.rottentomatoes.com/api/private/v1.0/movies/${id}`,
  711. responseType: 'json',
  712. timeout: this.timeOut,
  713. onload: (res)=>{
  714. if(res.status >= 200 && res.status < 300){
  715. resolve(res.response);
  716. }
  717. else{
  718. reject({
  719. status: res.status,
  720. statusText: res.statusText,
  721. });
  722. }
  723. }
  724. }, this.errorStatus(reject));
  725. GM_xmlhttpRequest(request);
  726. });
  727. }
  728. rottentomatoesAudienceScore(emsId){
  729. return new Promise((resolve, reject)=>{
  730. let request = Object.assign({
  731. method: 'GET',
  732. url: `https://www.rottentomatoes.com/napi/audienceScore/${emsId}`,
  733. responseType: 'json',
  734. timeout: this.timeOut,
  735. onload: (res)=>{
  736. if(res.status >= 200 && res.status < 300){
  737. resolve(res.response);
  738. }
  739. else{
  740. reject({
  741. status: res.status,
  742. statusText: res.statusText,
  743. });
  744. }
  745. }
  746. }, this.errorStatus(reject));
  747. GM_xmlhttpRequest(request);
  748. });
  749. }
  750. //Flixster
  751. flixsterDetail(id){
  752. return new Promise((resolve, reject)=>{
  753. let request = Object.assign({
  754. method: 'GET',
  755. url: `https://api.flixster.com/android/api/v2/movies/${id}`,
  756. responseType: 'json',
  757. timeout: this.timeOut,
  758. onload: (res)=>{
  759. if(res.status >= 200 && res.status < 300){
  760. resolve(res.response);
  761. }
  762. else{
  763. reject({
  764. status: res.status,
  765. statusText: res.statusText,
  766. });
  767. }
  768. }
  769. }, this.errorStatus(reject));
  770. GM_xmlhttpRequest(request);
  771. });
  772. }
  773. //
  774. parseTorrentName(name){
  775. name = name.trim();
  776. let regexp_array = [
  777. {
  778. name: 'season',//S01E05 Season01episode05
  779. pattern: /\b(?:season[\s.]?|s)(\d{1,3})(?:\b|(?:episode[\s.]?|ep?)\d{1,3}\b)/gi,
  780. fun: (temp)=>Number.parseInt(temp[1])
  781. },
  782. {
  783. name: 'year',//2018
  784. pattern: /\b([12]\d{3})\b/g,
  785. fun: (temp)=>Number.parseInt(temp[1])
  786. },
  787. {
  788. name: 'resp',//1080p
  789. pattern: /\b(\d{3,4})p\b/gi,
  790. fun: (temp)=>Number.parseInt(temp[1])
  791. },
  792. {
  793. name: 'resi',//1080i
  794. pattern: /\b(\d{3,4})i\b/gi,
  795. fun: (temp)=>Number.parseInt(temp[1])
  796. },
  797. {
  798. name: 'resk',//4k
  799. pattern: /\b(\d)k\b/gi,
  800. fun: (temp)=>Number.parseInt(temp[1])
  801. },
  802. {
  803. name: 'resd', //SD
  804. pattern: /\b((?:s|h|uh)d)\b/gi,
  805. fun: (temp)=>temp[1]
  806. },
  807. {
  808. name: 'source',//bluray web-dl webrip web hdtv hddvd dvd
  809. pattern: /\b(?:(?<bluray>blue?\-?ray|b[dr]\-?(?:rip)?)|(?<webdl>web\-?dl)|(?<webrip>web\-?rip)|(?<web>web)|(?<hdtv>hdtv)|(?<hddvd>hd-?dvd)|(?<dvd>dvd(?:rip|scr)?))\b/gi,
  810. fun: (temp)=>Object.keys(temp.groups).find(k=>temp.groups[k] !== undefined)
  811. }
  812. ];
  813. let result = {};
  814. let temp = null, start_index = -1, end_index = -1;
  815. let start_index_log = [];
  816. for(let [index, regexp] of regexp_array.entries()){
  817. while ((temp = regexp.pattern.exec(name)) !== null) {
  818. result[regexp.name] = regexp.fun(temp);
  819. start_index = temp.index;
  820. end_index = regexp.pattern.lastIndex;
  821. }
  822. if(start_index !== -1){
  823. start_index_log.push(start_index);
  824. start_index = -1;
  825. end_index = -1;
  826. }
  827. }
  828. if(start_index_log.length === 0){
  829. result.name = name;
  830. }
  831. else{
  832. result.name = name.slice(0, Math.min(...start_index_log)).trim().replace(/^\.|\.$/, '');
  833. }
  834. return result;
  835. }
  836. }
  837. window.MediaFetchAPI = MediaFetchAPI;
  838. })();