x1080x-preview

显示影片预览图和预览影片

  1. // ==UserScript==
  2. // @name x1080x-preview
  3. // @license MIT
  4. // @namespace https://greasyfork.org/zh-CN/scripts/439204-x1080x-preview
  5. // @version 7.3
  6. // @description 显示影片预览图和预览影片
  7. // @author jasmine
  8. // @match https://*.x567x.me/forum.php?mod=viewthread&tid=*
  9. // @match https://*.x567x.me/home.php?mod=spacecp&ac=pm&from=script
  10. // @match https://x567x.me/forum.php?mod=viewthread&tid=*
  11. // @match https://x567x.me/home.php?mod=spacecp&ac=pm&from=script
  12. // @match https://*.x999x.me/forum.php?mod=viewthread&tid=*
  13. // @match https://*.x999x.me/home.php?mod=spacecp&ac=pm&from=script
  14. // @match https://x999x.me/forum.php?mod=viewthread&tid=*
  15. // @match https://x999x.me/home.php?mod=spacsecp&ac=pm&from=script
  16. // @icon https://www.google.com/s2/favicons?domain=www.x999x.me
  17. // @connect javdb.com
  18. // @connect javbus.com
  19. // @connect ec.sod.co.jp
  20. // @connect cloudfront.net
  21. // @connect r18.com
  22. // @connect www.dmm.co.jp
  23. // @connect accounts.dmm.co.jp
  24. // @connect dahlia-av.jp
  25. // @connect cdn.faleno.net
  26. // @connect faleno.jp
  27. // @connect www.mgstage.com
  28. // @connect www.afesta.tv
  29. // @connect www.bing.com
  30. // @require https://lib.baomitu.com/jquery/3.5.1/jquery.min.js
  31. // @resource fancybox_css https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css
  32. // @require https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js
  33. // @resource videojs_css https://lib.baomitu.com/video.js/5.20.5/video-js.min.css
  34. // @require https://lib.baomitu.com/video.js/5.20.5/video.min.js
  35. // @require https://cdnjs.cloudflare.com/ajax/libs/videojs-hotkeys/0.2.27/videojs.hotkeys.min.js
  36. // @resource videojs_resolution_switcher_css https://lib.baomitu.com/videojs-resolution-switcher/0.4.2/videojs-resolution-switcher.min.css
  37. // @require https://lib.baomitu.com/videojs-resolution-switcher/0.4.2/videojs-resolution-switcher.min.js
  38. // @grant GM_xmlhttpRequest
  39. // @grant GM_notification
  40. // @grant GM_registerMenuCommand
  41. // @grant GM_unregisterMenuCommand
  42. // @grant GM_getResourceText
  43. // @grant GM_addStyle
  44. // @grant GM_setValue
  45. // @grant GM_getValue
  46. // @grant GM_log
  47. // @grant GM_openInTab
  48. // @grant GM_info
  49. // ==/UserScript==
  50.  
  51. (function() {
  52. 'use strict';
  53. // 菜单列表
  54. var menu_ALL = [
  55. ['menu_showGallery', '显示预览图', '显示预览图', true],
  56. ['menu_quality', '画面优先', '画面优先', true],
  57. ['menu_jumpSOD', '视频跳转SOD', '视频跳转SOD', true],
  58. ['menu_offcial', '搜索官网', '搜索官网', true],
  59. ['menu_multi', '并发搜索', '并发搜索', false],
  60. ['menu_JavDB', '搜索JavDB', '搜索JavDB', false],
  61. ['menu_JavBus', '搜索JavBus', '搜索JavBus', false],
  62. ], menu_ID = [];
  63. for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
  64. if (GM_getValue(menu_ALL[i][0]) == null) {
  65. GM_setValue(menu_ALL[i][0], menu_ALL[i][3])
  66. };
  67. }
  68. registerMenuCommand();
  69.  
  70. // 注册脚本菜单
  71. function registerMenuCommand() {
  72. if (menu_ID.length > menu_ALL.length){ // 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
  73. for (let i=0;i<menu_ID.length;i++){
  74. GM_unregisterMenuCommand(menu_ID[i]);
  75. }
  76. }
  77. for (let i=0;i<menu_ALL.length;i++){ // 循环注册脚本菜单
  78. menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
  79. menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3]?'✅':'❌'} ${menu_ALL[i][1]}`, function(){menu_switch(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`,`${menu_ALL[i][2]}`)});
  80. }
  81. // menu_ID[menu_ID.length] = GM_registerMenuCommand(`📥 导入FanzaCookies`, function () {
  82. // let fanza_cookies = "";
  83. // let cookies = prompt("请使用EditThisCookie导出Cookies后粘贴至此");
  84. // try {
  85. // JSON.parse(cookies).forEach(item=>{
  86. // fanza_cookies += `${item.name}=${item.value};`
  87. // })
  88. // GM_setValue("FANZA_Cookies", fanza_cookies)
  89. // } catch (error) {
  90. // console.error(error);
  91. // }
  92. // })
  93. menu_ID[menu_ID.length + 1] = GM_registerMenuCommand('💬 反馈 & 建议', function () {GM_openInTab(`https://${document.location.hostname}/home.php?mod=spacecp&ac=pm&from=script`, {active: true,insert: true,setParent: true});});
  94. }
  95.  
  96. // 菜单开关
  97. function menu_switch(menu_status, Name, Tips) {
  98. if (menu_status == 'true'){
  99. GM_setValue(`${Name}`, false);
  100. GM_notification({text: `已关闭 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
  101. }else{
  102. GM_setValue(`${Name}`, true);
  103. if (['menu_JavDB', 'menu_JavBus'].includes(Name)) {
  104. GM_setValue('menu_offcial', false)
  105. }
  106. GM_notification({text: `已开启 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
  107. }
  108. registerMenuCommand(); // 重新注册脚本菜单
  109. };
  110.  
  111. // 返回菜单值
  112. function menu_value(menuName) {
  113. for (let menu of menu_ALL) {
  114. if (menu[0] == menuName) {
  115. return menu[3]
  116. }
  117. }
  118. }
  119.  
  120. // 日志记录
  121. function record_log(msg) {
  122. let log = GM_getValue("log");
  123. if (log !== "") {
  124. log = log + "\n"
  125. }
  126. GM_setValue("log", log + msg);
  127. GM_log(msg);
  128. }
  129.  
  130. // 根据xpath查找 一个结果
  131. function _xO(path, obj) {
  132. return obj.evaluate(path, obj, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  133. }
  134.  
  135. // 根据xpath查找 多个结果
  136. function _xM(path, obj) {
  137. var xresult = obj.evaluate(path, obj, null, XPathResult.ANY_TYPE, null);
  138. var xnodes = [];
  139. var xres;
  140. while (xres = xresult.iterateNext()) {
  141. xnodes.push(xres.textContent);
  142. }
  143. return xnodes;
  144. }
  145.  
  146. // 通用请求
  147. function gmFetch(obj) {
  148. return new Promise((resolve, reject) => {
  149. GM_xmlhttpRequest({
  150. method: obj.method || 'GET',
  151. // timeout in ms
  152. timeout: obj.timeout,
  153. url: obj.url,
  154. headers: obj.headers,
  155. cookie: obj.cookie,
  156. data: obj.data,
  157. revalidate: obj.revalidate ? true: false,
  158. nocache: obj.nocache ? true: false,
  159. onload: res => {
  160. if (res.status >= 200 && res.status < 400 || res.finalUrl === "http://ec.sod.co.jp/prime/") {
  161. resolve(res);
  162. } else {
  163. reject(res);
  164. }
  165. },
  166. onerror: reject,
  167. ontimeout: reject,
  168. });
  169. });
  170. }
  171.  
  172. // 请求
  173. function fetch(url, headers={"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"}){
  174. return new Promise((resolve, reject) => {
  175. GM_xmlhttpRequest({
  176. url: url,
  177. method: "GET",
  178. headers: headers,
  179. //timeout: 5000,
  180. onload: function(r){
  181. if (r.status === 200) {
  182. resolve(r.responseText)
  183. } else {
  184. reject("status error: " + r.status)
  185. }
  186. },
  187. onerror: function(e) {
  188. reject('fetch error')
  189. },
  190. ontimeout: function(e) {
  191. reject('fetch timeout')
  192. }
  193. });
  194. })
  195. }
  196.  
  197. // 检查连接
  198. function checkURL(url, headers={"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"}){
  199. return new Promise((resolve, reject) => {
  200. GM_xmlhttpRequest({
  201. url: url,
  202. method: "HEAD",
  203. headers: headers,
  204. //timeout: 5000,
  205. onload: function(r){
  206. if (r.status === 200) {
  207. resolve(true)
  208. } else {
  209. resolve(false)
  210. }
  211. },
  212. onerror: function(e) {
  213. resolve(false)
  214. },
  215. ontimeout: function(e) {
  216. resolve(false)
  217. }
  218. });
  219. })
  220. }
  221.  
  222. async function asyncForEach(array, callback) {
  223. for (let index = 0; index < array.length; index++) {
  224. await callback(array[index], index, array);
  225. }
  226. }
  227. // 格式化页面
  228. function parseResponse(html) {
  229. const parser = new DOMParser()
  230. const tree = parser.parseFromString(html, "text/html")
  231. return tree
  232. }
  233.  
  234. // DMM获取大图
  235. function preview_src(src)
  236. {
  237. if (src.match(/(p[a-z]\.)jpg/)) {
  238. return src.replace(RegExp.$1, 'pl.');
  239. } else if (src.match(/consumer_game/)) {
  240. return src.replace('js-','-');
  241. } else if (src.match(/js\-([0-9]+)\.jpg$/)) {
  242. return src.replace('js-','jp-');
  243. } else if (src.match(/ts\-([0-9]+)\.jpg$/)) {
  244. return src.replace('ts-','tl-');
  245. } else if (src.match(/(\-[0-9]+\.)jpg$/)) {
  246. return src.replace(RegExp.$1, 'jp' + RegExp.$1);
  247. } else {
  248. return src.replace('-','jp-');
  249. }
  250. }
  251.  
  252. function getBase64FromImage(url, onSuccess, onError) {
  253. var xhr = new XMLHttpRequest();
  254.  
  255. xhr.responseType = "arraybuffer";
  256. xhr.open("GET", url);
  257.  
  258. xhr.onload = function () {
  259. var base64, binary, bytes, mediaType;
  260.  
  261. bytes = new Uint8Array(xhr.response);
  262. //NOTE String.fromCharCode.apply(String, ...
  263. //may cause "Maximum call stack size exceeded"
  264. binary = [].map.call(bytes, function (byte) {
  265. return String.fromCharCode(byte);
  266. }).join('');
  267. mediaType = xhr.getResponseHeader('content-type');
  268. base64 = [
  269. 'data:',
  270. mediaType ? mediaType + ';':'',
  271. 'base64,',
  272. btoa(binary)
  273. ].join('');
  274. onSuccess(base64);
  275. };
  276. xhr.onerror = onError;
  277. xhr.send();
  278. }
  279.  
  280. // 插入预览图
  281. function insertImages(item) {
  282. let src;
  283. if (video_type === "sod" && ["sod", "javbus"].includes(source)) {
  284. src = URL.createObjectURL(item)
  285. } else {
  286. src = item
  287. }
  288. const a = document.createElement("a");
  289. const img = document.createElement("img");
  290. a.href = src;
  291. a.setAttribute("data-fancybox", "gallery");
  292. a.setAttribute("class", "x1080x-ga-box");
  293. img.setAttribute("class", "x1080x-auto-img");
  294. img.src = src;
  295. a.appendChild(img);
  296. gallery_div.appendChild(a);
  297. }
  298.  
  299. //下载预览图
  300. // async function downloadSODImages(gallery, headers) {
  301. // for (let index = 0; index < gallery.length; index++) {
  302. // try {
  303. // await download(gallery[index], gallery[index], headers);
  304. // }
  305. // catch(e){
  306. // record_log(e)
  307. // }
  308. // }
  309. // }
  310.  
  311. //访问预览图
  312. async function accessSODImages(gallery, headers) {
  313. for (let index = 0; index < gallery.length; index++) {
  314. try {
  315. await fetch(gallery[index], headers);
  316. }
  317. catch(e){
  318. record_log(e)
  319. }
  320. }
  321. }
  322.  
  323. // 浏览器版本
  324. navigator.sayswho= (function(){
  325. let ua= navigator.userAgent;
  326. let tem;
  327. let M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
  328. if(/trident/i.test(M[1])){
  329. tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
  330. return 'IE '+(tem[1] || '');
  331. }
  332. if(M[1]=== 'Chrome'){
  333. tem= ua.match(/\b(OPR|Edge)\/(\d+)/);
  334. if(tem!= null) return tem.slice(1).join(' ').replace('OPR', 'Opera');
  335. }
  336. M= M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
  337. if((tem= ua.match(/version\/(\d+)/i))!= null) M.splice(1, 1, tem[1]);
  338. return M.join(' ');
  339. })();
  340. // 入口
  341. //const res = await fetch("https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=ssis279/mtype=AhRVShI_/service=litevideo/mode=/width=560/height=360/", {"accept-language": "ja-JP,ja;q=0.9"})
  342. //log(res);
  343. //const re = /const args = ({.+});$/gm;
  344. //const hits = [];
  345. //// Iterate hits
  346. //let match = null;
  347. //do {
  348. // match = re.exec(res);
  349. // if(match) {
  350. // hits.push(match[1]);
  351. // }
  352. //} while (match);
  353. //const obj = JSON.parse(hits[0])
  354. //record_log(obj["bitrates"]); // Prints [ '#with', '#hashtags' ]
  355. //return false
  356. // 意见反馈
  357. if (window.location.href.endsWith("from=script")) {
  358. //let log = GM_getValue("log")
  359. document.querySelector("#username").value = "jpyl0423"
  360. document.querySelector("#sendmessage").value = GM_getValue("log")
  361. return
  362. }
  363.  
  364. // 基础div
  365. const node = _xO("//div[@class='t_fsz']/table/tbody", document);
  366. if (node == null || !menu_value("menu_showGallery")) {
  367. return;
  368. }
  369.  
  370. // 封面
  371. let cover = document.querySelector("td[id^=postmessage] img").src
  372. if (!cover.match(/(pics.dmm.co.jp|www.hxmmdd.com)/)) {
  373. return
  374. }
  375.  
  376. // 预览容器
  377. const tr = document.createElement("tr");
  378. const td = document.createElement("td");
  379. const gallery_div = document.createElement("div")
  380. td.appendChild(gallery_div)
  381. tr.appendChild(td)
  382. gallery_div.setAttribute("class", "x1080x-gallery");
  383. const ga_inner = document.createElement('span');
  384. gallery_div.appendChild(ga_inner)
  385. node.appendChild(tr)
  386. // 播放容器
  387. const play_box = document.createElement("a");
  388. play_box.setAttribute("class", "x1080x-ga-box x1080x-play-box");
  389. play_box.setAttribute("data-fancybox", "");
  390. gallery_div.insertBefore(play_box, gallery_div.firstChild);
  391. // 视频封面
  392. const poster = document.createElement("img");
  393. poster.setAttribute("class", "x1080x-auto-img");
  394. poster.src = cover
  395. const span = document.createElement("span");
  396. span.setAttribute("class", "x1080x-preview");
  397. span.innerHTML = '預告片'
  398. const outer_span = document.createElement("span");
  399. outer_span.setAttribute("class", "x1080x-outer");
  400. outer_span.innerHTML = '外链'
  401. play_box.appendChild(span);
  402. play_box.appendChild(outer_span);
  403. play_box.appendChild(poster);
  404. // 播放器
  405. const video_player = document.createElement("video");
  406. video_player.setAttribute("id", "x1080x-player");
  407. video_player.setAttribute("class", "video-js vjs-default-skin");
  408. play_box.append(video_player)
  409. const options = {
  410. controls: true,
  411. responsive: true,
  412. plugins: {
  413. videoJsResolutionSwitcher: {
  414. default: menu_value('menu_quality') ? `high`: `low`, // Default resolution [{Number}, 'low', 'high'],
  415. dynamicLabel: true
  416. }
  417. },
  418. };
  419. let player = videojs("x1080x-player", options, function(){});
  420. player.ready(function() {
  421. this.hotkeys({
  422. volumeStep: 0.1,
  423. seekStep: 5,
  424. enableModifiersForNumbers: false
  425. });
  426. });
  427. // 加载fancybox样式
  428. const fancybox_css = GM_getResourceText("fancybox_css");
  429. GM_addStyle(fancybox_css);
  430. // 加载video-js样式
  431. const videojs_css = GM_getResourceText("videojs_css");
  432. GM_addStyle(videojs_css);
  433. GM_addStyle(".video-js .vjs-progress-holder, .video-js .vjs-progress-holder .vjs-play-progress,.video-js .vjs-progress-holder .vjs-load-progress,.video-js .vjs-progress-holder .vjs-tooltip-progress-bar,.video-js .vjs-progress-holder .vjs-load-progress div{height:1.0em}");
  434. // 加载videojs-resolution-switcher样式
  435. const videojs_resolution_switcher_css = GM_getResourceText("videojs_resolution_switcher_css");
  436. GM_addStyle(videojs_resolution_switcher_css);
  437. // 自定义样式
  438. let btn_img = `PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA1Mi4xICg2NzA0OCkgLSBodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2ggLS0+CiAgICA8dGl0bGU+YnRuLXBsYXk8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0iYnRuLXBsYXkiIGZpbGwtcnVsZT0ibm9uemVybyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yNTYsMCBDMTE0Ljg0MzU0OSwwIDAsMTE0Ljg0MzU0OSAwLDI1NiBDMCwzOTcuMTU2NDUxIDExNC44NDM1NDksNTEyIDI1Niw1MTIgQzM5Ny4xNTY0NTEsNTEyIDUxMiwzOTcuMTU2NDUxIDUxMiwyNTYgQzUxMiwxMTQuODQzNTQ5IDM5Ny4xNTY0NTEsMCAyNTYsMCBaIiBpZD0iU2hhcGUiIGZpbGw9IiMwQTVFRTAiPjwvcGF0aD4KICAgICAgICAgICAgPHBvbHlnb24gaWQ9IlBhdGgiIGZpbGw9IiNGRkZGRkYiIHBvaW50cz0iMTkyIDM4My45OTcyODUgMTkyIDEyNy45OTkwOTUgMzg0IDI1NS45OTcyODUiPjwvcG9seWdvbj4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==`
  439. GM_addStyle(`
  440. .x1080x-gallery {
  441. display: flex;
  442. flex-direction: row;
  443. flex-wrap: wrap;
  444. margin-top: 10px;
  445. max-width: 95%;
  446. }
  447. .x1080x-ga-box {
  448. display: inline-block;
  449. width: 250px;
  450. height: auto;
  451. text-align: center;
  452. vertical-align: middle;
  453. overflow-x: hidden;
  454. text-overflow: ellipsis;
  455. font-size: .9rem;
  456. background-color: black;
  457. margin: 5px 5px 0px 0px;
  458. cursor: pointer;
  459. }
  460. .x1080x-ga-box .x1080x-auto-img {
  461. position: relative;
  462. height: 120px;
  463. }
  464. .x1080x-play-box {
  465. display: none;
  466. position: relative;
  467. }
  468. .x1080x-play-box span.x1080x-preview {
  469. font-size: .8rem;
  470. color: #fff;
  471. background-color: #fc8300;
  472. position: absolute;
  473. top: 4px;
  474. left: 4px;
  475. text-align: center;
  476. padding: 1px 2px;
  477. border-radius: 3px;
  478. z-index: 999;
  479. }
  480. .x1080x-play-box span.x1080x-outer {
  481. font-size: .8rem;
  482. color: #fff;
  483. background-color: #fc8300;
  484. position: absolute;
  485. top: 4px;
  486. right: 4px;
  487. text-align: center;
  488. padding: 1px 2px;
  489. border-radius: 3px;
  490. z-index: 999;
  491. display: none;
  492. }
  493. .x1080x-play-box:after {
  494. background: url("data:image/svg+xml;base64,${btn_img}") 50% no-repeat;
  495. background-color: rgba(0,0,0,.2);
  496. background-size: 40px 40px;
  497. bottom: 0;
  498. content: "";
  499. display: block;
  500. left: 0;
  501. position: absolute;
  502. right: 0;
  503. top: 0;
  504. height: 100%;
  505. }
  506. .x1080x-play-box:hover::after {
  507. background-color: rgba(33,156,239,0);;
  508. }
  509. #x1080x-player {
  510. display:none;
  511. position: absolute;
  512. width: 50%;
  513. height: 50%;
  514. top: 50%;
  515. left: 50%;
  516. transform: translate(-50%, -50%);
  517. overflow: hidden;
  518. }
  519. `);
  520.  
  521. GM_setValue("log", "");
  522.  
  523. let title_pattern = /\)([^\(\)].*)</g;
  524. let subject = document.querySelector("#thread_subject").innerText
  525. let video_type;
  526. if (subject.match(/\(SOD\)|\(SODVR\)/) && menu_value("menu_offcial")) {
  527. video_type = "sod"
  528. } else if ([subject.match(/\(Prestige\)|\(MAXING\)/), document.body.innerHTML.indexOf("Prestige") != -1].some((e)=>e) && menu_value("menu_offcial")) {
  529. video_type = "mgs"
  530. } else if (subject.match(/\(AfestaVR\)/) && menu_value("menu_offcial")) {
  531. video_type = "afesta"
  532. } else {
  533. video_type = "sod"
  534. }
  535. let is_vr = (subject.indexOf("【VR】") !== -1 || subject.indexOf("【8K VR】")!== -1) ? true: false
  536. let pid = cover.replace("pl.jpg", ".jpg").replace("plzm.jpg", ".jpg").split("/").slice(-1)[0].split(".")[0]
  537. let vid = subject.match(/\w+-\d+/)[0]
  538. // SOD影片有可能在DMM, 番号格式类似: 1mogi00030
  539. if (pid != vid && video_type == "sod") {
  540. video_type = "dmm"
  541. }
  542.  
  543. if (menu_value("menu_offcial") && pid.startsWith(`1`) == false) {
  544. if (subject.match(/\(DAHLIA\)/)) {
  545. video_type = "dahlia"
  546. pid = pid.replace("-", "")
  547. }
  548. if (subject.match(/\(FALENO\)/) && document.body.innerHTML.indexOf("Prestige") === -1) {
  549. video_type = "faleno"
  550. pid = pid.replace("-", "")
  551. }
  552. }
  553.  
  554. if (is_vr & video_type == "sod" & vid.startsWith('3') == false) {
  555. vid = `3${vid}`
  556. }
  557.  
  558. if (pid.match(/\d{3}[A-Z]+-\d{3}/)) {
  559. video_type = "mgs"
  560. }
  561.  
  562. // MXGS-1341
  563. const regex = /^MXGS-(\d{3,4})$/i;
  564. const match = vid.match(regex);
  565. if (match) {
  566. const numberPart = match[1];
  567. const paddedNumber = numberPart.padStart(5, '0');
  568. vid = `mxgs${paddedNumber}`;
  569. video_type = "dmm"
  570. }
  571.  
  572. let multi_search = menu_value("menu_multi")
  573. record_log(`浏览器: ${navigator.sayswho}`)
  574. record_log(`插件版本: ${GM_info.script.version}`)
  575. record_log(`标题: ${subject}`)
  576. record_log(`地址: ${window.location.href}`)
  577. record_log(`番号: ${pid}`)
  578. record_log(`视频ID: ${vid}`)
  579.  
  580. let source;
  581. let dmm = async() => {
  582. if (!multi_search && (video_type !== "dmm" && !(video_type == "sod" && pid.includes("-"))) || !menu_value("menu_offcial")) {
  583. record_log(`DMM Search: 跳过`)
  584. throw new Error('DMM Search: 跳过');
  585. }
  586. let search_id = pid
  587. if (pid.includes("-")) {
  588. search_id = pid.split("-").join("00").toLowerCase()
  589. }
  590. record_log(`DMM Search: ${search_id}`)
  591. let dmm_headers = {"accept-language": "ja-JP,ja;q=0.9"}
  592. let dmm_cookie = "age_check_done=1"
  593. //let dmm_cookie = GM_getValue("FANZA_Cookies") ? GM_getValue("FANZA_Cookies"): "age_check_done=1";
  594. let search_request = {url: `https://www.dmm.co.jp/search/=/searchstr=${search_id}`, headers: dmm_headers, cookie: dmm_cookie}
  595. let search_res = await gmFetch(search_request).catch(err => {record_log(err); return;})
  596. let search_html = parseResponse(search_res.responseText)
  597. let ele_detail = search_html.querySelector(`.tmb a[href*="${search_id}"][href^="https://www.dmm.co.jp"]`)
  598. if (!ele_detail) {
  599. record_log(`DMM Search: 未找到`);
  600. throw new Error('DMM Search: 未找到');
  601. }
  602. let detail_url = ele_detail.getAttribute("href").split("?")[0]
  603. record_log(`DMM Search: 详情页 ${detail_url}`)
  604. let cid_pattern = /cid=(.*?\w)\//
  605. let cid = cid_pattern.exec(detail_url)[1]
  606. let detail_request = {url: detail_url, headers: dmm_headers, cookie: dmm_cookie}
  607. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  608. let detail_html = parseResponse(detail_res.responseText)
  609. // 预览图
  610. let imgs = [...detail_html.querySelectorAll("#sample-image-block img")].map( item => item.getAttribute("src")).map( item => preview_src(item))
  611. // 预览视频
  612. let video = []
  613. if (is_vr) {
  614. let video_request = {url: `https://www.dmm.co.jp/digital/-/vr-sample-player/=/cid=${cid}/`, headers: dmm_headers, cookie: dmm_cookie}
  615. let video_res = await gmFetch(video_request).catch(err => {record_log(err); return;});
  616. let video_pattern = /var sampleUrl = "(.*)";/g;
  617. let video_url = video_pattern.exec(video_res.responseText)[1]
  618. if (video_url !== '') {
  619. video.push({
  620. src: video_url,
  621. type: 'video/mp4',
  622. label: `VR`,
  623. res: `100`
  624. })
  625. }
  626. } else {
  627. // let video_request = {url: `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${cid}/mtype=AhRVShI_/service=litevideo/mode=/width=560/height=360/`, headers: dmm_headers, cookie: dmm_cookie};
  628. let video_request = {url: `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${cid}`, headers: dmm_headers, cookie: dmm_cookie};
  629. const video_label = {300: "流畅", 1000: "清晰", 1500: "高清", 2000: "高清", 3000: "HD", "4K (2160p)": "4K", "4K (2160p60)": "4K", "FullHD (1080p)": "1080p", "FullHD (1080p60)": "1080p", "HD (720p)": "720p", "HD (720p60)": "720p","高画質 (576p)": "576p", "中画質 (432p)": "432p", "中画質 (288p)": "288p", "低画質 (144p)": "144p"}
  630. let video_res = await gmFetch(video_request).catch(err => {record_log(err); return;});
  631. let video_pattern = /(const args = |const params = )({.+});$/gm;
  632. if (video_res.responseText) {
  633. let bitrates = JSON.parse(video_pattern.exec(video_res.responseText)[2]).bitrates
  634. video = bitrates.map( function(item) {
  635. return {
  636. src: item.src,
  637. type: 'video/mp4',
  638. label: video_label[item.bitrate],
  639. res: item.bitrate
  640. }
  641. })
  642. }
  643. }
  644. return {imgs: imgs, video: video, source: "dmm"}
  645. }
  646.  
  647. let dahlia = async() => {
  648. if (!multi_search && video_type !== "dahlia" || !menu_value("menu_offcial")) {
  649. record_log(`DAHLIA Search: 跳过`)
  650. throw new Error('DAHLIA Search: 跳过');
  651. }
  652. let dahlia_cookie = "max-age=0"
  653. let detail_url = `https://dahlia-av.jp/works/${pid}/`
  654. record_log(`DAHLIA Search: 详情页 ${detail_url}`)
  655. let detail_request = {url: detail_url, cookie: dahlia_cookie}
  656. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  657. let detail_html = parseResponse(detail_res.responseText)
  658. // 预览图
  659. let imgs = [...detail_html.querySelectorAll(".box_works01_ga a")].map( item => item.getAttribute("href"))
  660. // 预览视频
  661. let video = []
  662. let video_url = detail_html.querySelector(".overoll_box .pop_sample").getAttribute("href");
  663. if (video_url !== '') {
  664. video.push({
  665. src: video_url,
  666. type: 'video/mp4',
  667. label: `DAHLIA`,
  668. res: `100`
  669. })
  670. }
  671. return {imgs: imgs, video: video, source: "dmm"}
  672. }
  673.  
  674. let faleno = async() => {
  675. if (!multi_search && video_type !== "faleno" || !menu_value("menu_offcial")) {
  676. record_log(`FALENO Search: 跳过`)
  677. throw new Error('FALENO Search: 跳过');
  678. }
  679. let faleno_cookie = "modal=off"
  680. let detail_url = `https://faleno.jp/top/works/${pid}/`
  681. record_log(`FALENO Search: 详情页 ${detail_url}`)
  682. let detail_request = {url: detail_url, cookie: faleno_cookie}
  683. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  684. let detail_html = parseResponse(detail_res.responseText)
  685. // 预览图
  686. let imgs = [...detail_html.querySelectorAll(".box_works01_ga a")].map( item => item.getAttribute("href"))
  687. // 预览视频
  688. let video = []
  689. let video_url = detail_html.querySelector(".pop_sample").getAttribute("href");
  690. if (video_url !== '') {
  691. video.push({
  692. src: video_url,
  693. type: 'video/mp4',
  694. label: `FALENO`,
  695. res: `100`
  696. })
  697. }
  698. return {imgs: imgs, video: video, source: "dmm"}
  699. }
  700.  
  701. let mgstage = async() => {
  702. if (!multi_search && video_type !== "mgs" || !menu_value("menu_offcial")) {
  703. record_log(`MGSTAGE Search: 跳过`)
  704. throw new Error('MGSTAGE Search: 跳过');
  705. }
  706. record_log(`MGSTAGE Search: ${pid}`)
  707. let cookie = `adc=1`;
  708. let detail_request = {url: `https://www.mgstage.com/product/product_detail/${pid}/`, cookie: cookie}
  709. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  710. let detail_html = parseResponse(detail_res.responseText)
  711. // 预览图
  712. let imgs = [...detail_html.querySelectorAll(".sample_image")].map( item => item.getAttribute("href"))
  713. // 预览视频
  714. let video = []
  715. let ele = detail_html.querySelector('div.detail_photo p.sample_movie_btn a:not([disabled])');
  716. if (ele) {
  717. let href = ele.getAttribute('href');
  718. let pid = href.split('/').pop();
  719. record_log(`MGSTAGE Search: 视频ID ${pid}`);
  720. let video_request = {url: `https://www.mgstage.com/sampleplayer/sampleRespons.php?pid=${pid}`, cookie: cookie}
  721. let video_res = await gmFetch(video_request).catch(err => {record_log(err); return;})
  722. let re = /https.*?ism/
  723. let json_obj = JSON.parse(video_res.responseText);
  724. let video_url = json_obj.url;
  725. video_url = re.exec(video_url)[0].replace('ism', 'mp4');
  726. video.push({
  727. src: video_url,
  728. type: 'video/mp4',
  729. label: `MGS`,
  730. res: `100`
  731. })
  732. }
  733. return {imgs: imgs, video: video, source: "mgs"}
  734. }
  735. let sod = async() => {
  736. if (!multi_search && video_type !== "sod" || !menu_value("menu_offcial")) {
  737. record_log(`SOD Search: 跳过`)
  738. throw new Error('SOD Search: 跳过');
  739. }
  740. record_log(`SOD Search: ${vid}`)
  741. let detail_request = {url: `https://ec.sod.co.jp/prime/videos/?id=${vid}`}
  742. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  743. let detail_html = parseResponse(detail_res.responseText)
  744. let age_check = detail_html.querySelector(".pkg_age")
  745. if (age_check) {
  746. await gmFetch({url: `https://ec.sod.co.jp/prime/_ontime.php`}).catch(err => {record_log(err); return;})
  747. detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  748. detail_html = parseResponse(detail_res.responseText)
  749. }
  750. // 预览图
  751. let imgs = [...detail_html.querySelectorAll(".img-gallery a")].map( item => item.getAttribute("href"))
  752. let imgs_blob = []
  753. await asyncForEach(imgs, async (img) => {
  754. const response = await new Promise((resolve, reject) => {
  755. GM_xmlhttpRequest({
  756. method: "get",
  757. url: img,
  758. headers: {"referer": `https://ec.sod.co.jp/prime/videos/?id=${vid}`},
  759. responseType: "blob",
  760. onload: response => {resolve(response)},
  761. onerror: response => {reject(response)},
  762. });
  763. });
  764. //console.log("response:", response);
  765. const {response: blob} = response;
  766. console.log(blob);
  767. imgs_blob.push(blob)
  768. })
  769. imgs = imgs_blob
  770. // 预览视频
  771. let video = []
  772. let ele_video = detail_html.querySelector(".videos_sampb a");
  773. if (ele_video) {
  774. let video_url = `https://ec.sod.co.jp/prime/videos/${ele_video.getAttribute('href')}`;
  775. video.push({
  776. src: video_url,
  777. type: 'video/mp4',
  778. label: `SOD`,
  779. res: `100`
  780. })
  781. let video_request = {url: video_url}
  782. let video_res = await gmFetch(video_request).catch(err => {record_log(err); return;})
  783. let video_html = parseResponse(video_res.responseText)
  784. let video_src = video_html.querySelector("#moviebox source").getAttribute("src")
  785. video.push({
  786. src: `${video_src}?from=script`,
  787. type: 'video/mp4',
  788. label: `SOD`,
  789. res: `100`
  790. })
  791. }
  792. return {imgs: imgs, video: video, source: "sod"}
  793. }
  794.  
  795. let afesta = async() => {
  796. if (!multi_search && video_type !== "afesta" || !menu_value("menu_offcial")) {
  797. record_log(`Afesta Search: 跳过`)
  798. throw new Error('Afesta Search: 跳过');
  799. }
  800. record_log(`Afesta Search: ${pid}`)
  801. const params = new URLSearchParams();
  802. params.append("keyword", `${pid}`);
  803. params.append("header", "search");
  804. let search_request = {url: `https://www.afesta.tv/vr/search.php`, method: `POST`, data: `keyword=${pid}&header=search`, headers: {"Content-Type": "application/x-www-form-urlencoded"}}
  805. let search_res = await gmFetch(search_request).catch(err => {record_log(err); return;})
  806. let search_html = parseResponse(search_res.responseText)
  807.  
  808. let ele_detail = Array.from(search_html.querySelectorAll('.faces-list a')).find(el => el.querySelector(`img`).getAttribute(`data-src`).indexOf(pid) != -1)
  809. if (!ele_detail) {
  810. record_log(`Afesta Search: 未找到`);
  811. throw new Error('Afesta Search: 未找到');
  812. }
  813. let detail_url = ele_detail.getAttribute("href")
  814. record_log(`Afesta Search: 详情页 ${detail_url}`)
  815. let detail_request = {url: detail_url}
  816. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  817. let detail_html = parseResponse(detail_res.responseText)
  818. // 预览图
  819. let imgs = [...detail_html.querySelectorAll(`.thumbs-grid li a`)]
  820. .map( item => item.getAttribute("href"))
  821. .map( item => {
  822. if (!item.startsWith(`http`)) {
  823. return new URL(item, detail_url).href
  824. } else {
  825. return item
  826. }})
  827. // 预览视频
  828. let video = []
  829. return {imgs: imgs, video: video, source: "afesta"}
  830. }
  831.  
  832. let javdb = async() => {
  833. if (!menu_value("menu_JavDB") || menu_value("menu_offcial")) {
  834. record_log(`JavDB Search: 跳过`)
  835. throw new Error('JavDB Search: 跳过');
  836. }
  837. record_log(`JavDB Search: ${vid}`)
  838. let cookie = `over18=1`;
  839. let search_request = {url: `https://javdb.com/search?q=${vid}&f=all`, cookie: cookie}
  840. let search_res = await gmFetch(search_request).catch(err => {record_log(err); return;})
  841. let search_html = parseResponse(search_res.responseText)
  842. let ele_detail = Array.from(search_html.querySelectorAll('.video-title strong')).find(el => el.textContent === vid)
  843. if (!ele_detail) {
  844. record_log(`JavDB Search: 未找到`);
  845. throw new Error('JavDB Search: 未找到');
  846. }
  847. let detail_url = `https://javdb.com${ele_detail.closest("a").getAttribute("href")}`
  848. let detail_request = {url: detail_url, cookie: cookie}
  849. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  850. let detail_html = parseResponse(detail_res.responseText)
  851. // 预览图
  852. let imgs = [...detail_html.querySelectorAll(".preview-images a[class=tile-item]")].map( item => item.getAttribute("href"))
  853. // 预览视频
  854. let video = []
  855. let ele_video = detail_html.querySelector(".preview-video-container")
  856. if (ele_video) {
  857. let video_src = ele_video.nextElementSibling.querySelector("source").getAttribute("src")
  858. video.push({
  859. src: video_src,
  860. type: 'video/mp4',
  861. label: `JavDB`,
  862. res: `100`
  863. })
  864. }
  865. return {imgs: imgs, video: video, source: "javdb"}
  866. }
  867.  
  868. let javbus = async() => {
  869. if (!menu_value("menu_JavBus") || menu_value("menu_offcial")) {
  870. record_log(`JavBus Search: 跳过`)
  871. throw new Error('JavBus Search: 跳过');
  872. }
  873. record_log(`JavBus Search: ${vid}`)
  874. let detail_request = {url: `https://www.javbus.com/${vid}`}
  875. let detail_res = await gmFetch(detail_request).catch(err => {record_log(err); return;})
  876. let detail_html = parseResponse(detail_res.responseText)
  877. // 预览图
  878. let imgs = [...detail_html.querySelectorAll("#sample-waterfall a")]
  879. .map( item => item.getAttribute("href"))
  880. .map(function(item) {
  881. if(item.startsWith("http")) {
  882. return item
  883. } else {
  884. video_type="sod"
  885. return `https://www.javbus.com${item}`
  886. }
  887. })
  888. if (video_type==="sod") {
  889. let imgs_blob = []
  890. await asyncForEach(imgs, async (img) => {
  891. const response = await new Promise((resolve, reject) => {
  892. GM_xmlhttpRequest({
  893. method: "get",
  894. url: img,
  895. headers: {"referer": `https://www.javbus.com/${vid}`},
  896. responseType: "blob",
  897. onload: response => {resolve(response)},
  898. onerror: response => {reject(response)},
  899. });
  900. });
  901. //console.log("response:", response);
  902. const {response: blob} = response;
  903. imgs_blob.push(blob)
  904. })
  905. imgs = imgs_blob
  906. }
  907. // 预览视频
  908. let video = []
  909. return {imgs: imgs, video: video, source: "javbus"}
  910. }
  911. ga_inner.innerHTML = "正在搜索预览图..."
  912. Promise.allSettled([dmm(),dahlia(),faleno(),mgstage(),sod(),afesta(),javdb(),javbus()]).then(results=>{
  913. ga_inner.innerHTML = ""
  914. results.forEach(r => {
  915. if (r.status == "rejected" && r.reason.stack.indexOf("跳过") == -1) {
  916. record_log(`Error Reason: ${r.reason.stack}`)
  917. }
  918. })
  919. let p = results.find(result => result.status==="fulfilled")
  920. if (p) {
  921. source = p.value.source
  922. let imgs = p.value.imgs
  923. let video = p.value.video
  924. if (imgs.length !== 0) {
  925. imgs.forEach(insertImages)
  926. }
  927. if (video.length !==0) {
  928. play_box.setAttribute("style", "display:inline-block");
  929. if (source === "sod") {
  930. if (menu_value("menu_jumpSOD")) {
  931. video = video.slice(0,1)
  932. outer_span.setAttribute("style", "display:block");
  933. } else {
  934. video = video.slice(1)
  935. player.updateSrc(video)
  936. }
  937. } else {
  938. player.updateSrc(video)
  939. }
  940.  
  941. // 点击弹出播放器
  942. play_box.onclick = function (e) {
  943. e.preventDefault();
  944. if (source === "sod" && menu_value("menu_jumpSOD")) {
  945. GM_openInTab(video[0].src, {active : true})
  946. } else {
  947. video_player.setAttribute("style", "display:block");
  948. $.fancybox.open({
  949. src : '#x1080x-player',
  950. type : 'inline',
  951. opts : {
  952. smallBtn: false,
  953. touch: false,
  954. afterShow : function( instance, current ) {
  955. player.play();
  956. },
  957. afterClose : function( instance, current ) {
  958. player.pause();
  959. }
  960. }
  961. });
  962. }
  963. }
  964. }
  965. if (imgs.length === 0 && video.length === 0 ){
  966. gallery_div.style.display = "none"
  967. }
  968. }
  969. })
  970. //$.fancybox.defaults.loop = "true";
  971. })();