Jav小司机(修改)

Jav小司机。简单轻量速度快!感谢wddd,此脚本为wddd创作,版权属原作者所有,我只是修改了一些不能用的网址和加了免梯子网址自用,如有兴趣者可自行下载。

  1. // ==UserScript==
  2. // @name Jav小司机(修改)
  3. // @namespace wddd
  4. // @version 1.1.401
  5. // @author testqdg
  6. // @license MIT
  7. // @include http*://*javlibrary.com/*
  8. // @include http*://*javlib.com/*
  9. // @include http*://www.o58c.com/*
  10. // @include http*://*j41g.com/*
  11. // @include http*://www.e59f.com/*
  12. // @description Jav小司机。简单轻量速度快!感谢wddd,此脚本为wddd创作,版权属原作者所有,我只是修改了一些不能用的网址和加了免梯子网址自用,如有兴趣者可自行下载。
  13. // @grant GM_xmlhttpRequest
  14. // @grant GM_addStyle
  15. // @grant GM_setValue
  16. // @grant GM_getValue
  17. // @homepage https://github.com/wdwind/JavMiniDriver
  18. // ==/UserScript==
  19.  
  20. // Credit to
  21. // * https://greasyfork.org/zh-CN/scripts/25781
  22. // * https://greasyfork.org/zh-CN/scripts/37122
  23.  
  24. // Change log
  25. // 1.1.4
  26. /**
  27. * Add support for j41g.com and e59f.com
  28. * Block ad
  29. * Only load the full screenshot until user clicks the thumbnail
  30. */
  31. // 1.1.3
  32. /**
  33. * Issue: https://github.com/wdwind/JavMiniDriver/issues/1#issuecomment-521836751
  34. *
  35. * Update browser history when clicking "load more" button in video list page
  36. * Store the configuration of whether to show the page selector in local storage instead of cookies
  37. * Fix a screenshot bug to handle non-existing images gracefully
  38. * Temporarily remove video from sod.co.jp since it requires a Referer in http request header
  39. * ~~Add a iframe to bypass adult check and DDoS check of sod.co.jp~~
  40. * Other technical refactoring
  41. */
  42. // 1.1.2
  43. /**
  44. * Issue: https://greasyfork.org/zh-CN/forum/discussion/61213/x
  45. *
  46. * Minor updates
  47. * Add javbus torrent search
  48. * Add support for javlib.com and o58c.com
  49. */
  50. // 1.1.1
  51. /**
  52. * Issue: https://github.com/wdwind/JavMiniDriver/issues/1
  53. *
  54. * Change thumbnail font
  55. * Add page selector
  56. * Add japanese-bukkake as backup image screenshot source
  57. * Change image width to max-width when clicking the screenshot to prevent image being over zoomed
  58. * Add more data sources for the screenshots in reviews/comments
  59. */
  60. // 1.1.0
  61. /**
  62. * Simplify code by merging the functions for get more comments/reviews
  63. * Process screenshots in reviews/comments
  64. * Remove redirection
  65. * Get full image url
  66. * Add mouse click effect
  67. */
  68.  
  69. // Utils
  70.  
  71. function setCookie(cookieName, cookieValue, expireDays) {
  72. let expireDate =new Date();
  73. expireDate.setDate(expireDate.getDate() + expireDays);
  74. let expires = "expires=" + ((expireDays == null) ? '' : expireDate.toUTCString());
  75. document.cookie = cookieName + "=" + cookieValue + ";" + expires + ";path=/";
  76. }
  77.  
  78. // Not used
  79. // function getCookie(cookieName) {
  80. // let value = "; " + document.cookie;
  81. // let parts = value.split("; " + cookieName + "=");
  82. // if (parts.length == 2) {
  83. // return parts.pop().split(";").shift();
  84. // }
  85. // }
  86.  
  87. function insertAfter(newNode, referenceNode) {
  88. referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  89. }
  90.  
  91. function insertBefore(newNode, referenceNode) {
  92. referenceNode.parentNode.insertBefore(newNode, referenceNode);
  93. }
  94.  
  95. function removeElementIfPresent(element) {
  96. if (element) {
  97. return element.parentNode.removeChild(element);
  98. }
  99. }
  100.  
  101. function parseHTMLText(text) {
  102. try {
  103. let doc = document.implementation.createHTMLDocument('');
  104. doc.documentElement.innerHTML = text;
  105. return doc;
  106. } catch (e) {
  107. console.error('Parse error');
  108. }
  109. }
  110.  
  111. // https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro
  112. function createElementFromHTML(html) {
  113. var template = document.createElement('template');
  114. html = html.trim(); // Never return a text node of whitespace as the result
  115. template.innerHTML = html;
  116. return template.content.firstChild;
  117. }
  118.  
  119. function getLoadMoreButton(buttonId, callback) {
  120. let loadMoreButton = createElementFromHTML('<input type="button" class="largebutton" value="加载更多 &or;">');
  121. loadMoreButton.addEventListener('click', callback);
  122.  
  123. buttonId = (buttonId != null) ? buttonId : 'load_more';
  124. let loadMoreDiv = createElementFromHTML(`<div id="${buttonId}" class="load_more"></div>`);
  125. loadMoreDiv.appendChild(loadMoreButton);
  126. return loadMoreDiv;
  127. }
  128.  
  129. // For the requests in different domains
  130. // GM_xmlhttpRequest is made from the background page, and as a result, it
  131. // doesn't have cookies in the request header
  132. function gmFetch(obj) {
  133. return new Promise((resolve, reject) => {
  134. GM_xmlhttpRequest({
  135. method: obj.method || 'GET',
  136. // timeout in ms
  137. timeout: obj.timeout,
  138. url: obj.url,
  139. headers: obj.headers,
  140. data: obj.data,
  141. onload: (result) => {
  142. if (result.status >= 200 && result.status < 300) {
  143. resolve(result);
  144. } else {
  145. reject(result);
  146. }
  147. },
  148. onerror: reject,
  149. ontimeout: reject,
  150. });
  151. });
  152. }
  153.  
  154. // For the requests in the same domain
  155. // XMLHttpRequest is made within the page, so it will send the cookies
  156. function xhrFetch(obj) {
  157. return new Promise((resolve, reject) => {
  158. let xhr = new XMLHttpRequest();
  159. xhr.open(obj.method || 'GET', obj.url);
  160. // timeout in ms
  161. xhr.timeout = obj.timeout;
  162. if (obj.headers) {
  163. Object.keys(obj.headers).forEach(key => {
  164. xhr.setRequestHeader(key, obj.headers[key]);
  165. });
  166. }
  167. xhr.withCredentials = true;
  168. xhr.onload = () => {
  169. if (xhr.status >= 200 && xhr.status < 300) {
  170. resolve(xhr);
  171. } else {
  172. reject(xhr);
  173. }
  174. };
  175. xhr.onerror = () => reject(xhr);
  176. xhr.ontimeout = () => reject(xhr);
  177. xhr.send(obj.data);
  178. });
  179. };
  180.  
  181. // Style
  182.  
  183. function addStyle() {
  184. // social media
  185. GM_addStyle(`
  186. #toplogo {
  187. height: 55px;
  188. }
  189. .socialmedia {
  190. display: none !important;
  191. width: 0% !important;
  192. height: 0% !important;
  193. }
  194. .videothumblist .videos .video {
  195. height: 290px;
  196. }
  197. .thumbnailDetail {
  198. font-size: 14px;
  199. margin-top: 2.5em;
  200. color: #666666;
  201. }
  202. .page_selector {
  203. display: none;
  204. margin-bottom: 15px;
  205. }
  206. .load_more {
  207. text-align: center;
  208. }
  209. #load_next_page {
  210. margin-bottom: 10px;
  211. }
  212. #load_next_page_button {
  213. display: inline;
  214. }
  215. #togglePageSelector {
  216. margin-left: 20px;
  217. font-size: 14px;
  218. vertical-align: text-top;
  219. display: inline;
  220. }
  221. .toggle {
  222. cursor: pointer;
  223. color: blue;
  224. }
  225. .bottombanner2 {
  226. display: none !important;
  227. }
  228. `);
  229.  
  230. // Homepage
  231. if (!window.location.href.includes('.php')) {
  232. GM_addStyle(`
  233. .videothumblist {
  234. height: 645px !important;
  235. }
  236. `);
  237. }
  238. }
  239.  
  240. // Thumbnail
  241. class MiniDriverThumbnail {
  242.  
  243. constructor() {
  244. this.loadMoreDivId = 'load_next_page';
  245. this.loadMoreButtonId = 'load_next_page_button';
  246.  
  247. let showPageSelector = GM_getValue('showPageSelector', 'none') != 'block' ? 'none' : 'block';
  248. let pageSelector = document.getElementsByClassName('page_selector')[0];
  249. if (pageSelector) {
  250. pageSelector.style.display = showPageSelector;
  251. }
  252.  
  253. let toggleMessage = GM_getValue('showPageSelector', 'none') != 'block' ? '显示页数' : '隐藏页数';
  254. this.togglePageSelector = createElementFromHTML(
  255. `<div id='togglePageSelector' class='toggle'>
  256. ${toggleMessage}
  257. </div>`);
  258. this.togglePageSelector.addEventListener('click', () => this.toggle());
  259. }
  260.  
  261. execute() {
  262. if (window) {
  263.  
  264. }
  265. let videos = document.getElementsByClassName('videos')[0];
  266. document.getElementsByClassName('videothumblist')[0].innerHTML = `<div class="videothumblist">
  267. <div class="videos"></div>
  268. </div>`;
  269. let pageSelector = document.getElementsByClassName('page_selector')[0];
  270. let nextPage = document.getElementsByClassName('page next')[0];
  271. this.updatePageContent(videos, pageSelector, nextPage);
  272. }
  273.  
  274. updatePageContent(videos, pageSelector, nextPage) {
  275. // Add videos to the page
  276. let currentVideos = document.getElementsByClassName('videos')[0];
  277. if (videos) {
  278. Array.from(videos.children).forEach(video => {
  279. currentVideos.appendChild(video);
  280. this.updateVideoDetail(video);
  281. this.updateVideoEvents(video);
  282. });
  283. }
  284.  
  285. // Remove current "load more" div
  286. removeElementIfPresent(document.getElementById(this.loadMoreDivId));
  287.  
  288. // Replace page selector content
  289. document.getElementsByClassName('page_selector')[0].innerHTML = pageSelector.innerHTML;
  290.  
  291. // Add "load more" div
  292. let loadMoreDiv = createElementFromHTML(`<div id='${this.loadMoreDivId}' class='load_more'></div>`);
  293. if (nextPage) {
  294. let nextPageUrl = nextPage.href;
  295. let loadMoreButton = getLoadMoreButton(this.loadMoreButtonId, async () => this.getNextPage(nextPageUrl));
  296. loadMoreDiv.appendChild(loadMoreButton);
  297. loadMoreDiv.appendChild(this.togglePageSelector);
  298. document.getElementById('rightcolumn').appendChild(loadMoreDiv);
  299. }
  300. }
  301.  
  302. async updateVideoDetail(video) {
  303. if (video.id.includes('vid_')) {
  304. let request = {url: `/cn/?v=${video.id.substring(4)}`};
  305. let result = await xhrFetch(request).catch(err => {console.log(err); return;});
  306. let videoDetailsDoc = parseHTMLText(result.responseText);
  307.  
  308. // Video date
  309. let videoDate = '';
  310. if (videoDetailsDoc.getElementById('video_date')) {
  311. videoDate = videoDetailsDoc.getElementById('video_date').getElementsByClassName('text')[0].innerText;
  312. }
  313.  
  314. // Video score
  315. let videoScore = '';
  316. if (videoDetailsDoc.getElementById('video_review')) {
  317. let videoScoreStr = videoDetailsDoc.getElementById('video_review').getElementsByClassName('score')[0].innerText;
  318. videoScore = videoScoreStr.substring(1, videoScoreStr.length - 1);
  319. }
  320.  
  321. // Video watched
  322. let videoWatched = '0';
  323. if (videoDetailsDoc.getElementById('watched')) {
  324. videoWatched = videoDetailsDoc.getElementById('watched').getElementsByTagName('a')[0].innerText;
  325. }
  326.  
  327. let videoDetailsHtml = `
  328. <div class="thumbnailDetail">
  329. <span>${videoDate}</span>&nbsp;&nbsp;<span style='color:red;'>${videoScore}</span>
  330. <br/>
  331. <span>${videoWatched} 人看过</span>
  332. </div>
  333. `;
  334. let videoDetails = createElementFromHTML(videoDetailsHtml);
  335. video.insertBefore(videoDetails, video.getElementsByClassName('toolbar')[0]);
  336. }
  337. }
  338.  
  339. updateVideoEvents(video) {
  340. if (video) {
  341. // Prevent existing listeners https://stackoverflow.com/a/46986927/4214478
  342. video.addEventListener('mouseout', (event) => {
  343. event.stopImmediatePropagation();
  344. video.getElementsByClassName('toolbar')[0].style.display = 'none';
  345. }, true);
  346. video.addEventListener('mouseover', (event) => {
  347. event.stopImmediatePropagation();
  348. video.getElementsByClassName('toolbar')[0].style.display = 'block';
  349. }, true);
  350. }
  351. }
  352.  
  353. async getNextPage(url) {
  354. // Update page URL and history
  355. history.pushState(history.state, window.document.title, url);
  356.  
  357. // Fetch next page
  358. let result = await xhrFetch({url: url}).catch(err => {console.log(err); return;});
  359. let nextPageDoc = parseHTMLText(result.responseText);
  360.  
  361. // Update page content
  362. let videos = nextPageDoc.getElementsByClassName('videos')[0];
  363. let pageSelector = nextPageDoc.getElementsByClassName('page_selector')[0];
  364. let nextPage = nextPageDoc.getElementsByClassName('page next')[0];
  365. this.updatePageContent(videos, pageSelector, nextPage);
  366. }
  367.  
  368. toggle() {
  369. let pageSelector = document.getElementsByClassName('page_selector')[0];
  370. if (pageSelector.style.display === 'none') {
  371. pageSelector.style.display = 'block';
  372. this.togglePageSelector.innerText = '隐藏页数';
  373. GM_setValue('showPageSelector', 'block');
  374. } else {
  375. pageSelector.style.display = 'none';
  376. this.togglePageSelector.innerText = '显示页数';
  377. GM_setValue('showPageSelector', 'none');
  378. }
  379. }
  380. }
  381.  
  382. class MiniDriver {
  383.  
  384. execute() {
  385. let javUrl = new URL(window.location.href);
  386. this.javVideoId = javUrl.searchParams.get('v');
  387.  
  388. // Video page
  389. if (this.javVideoId != null) {
  390. this.addStyle();
  391. this.setEditionNumber();
  392. this.updateTitle();
  393. this.addScreenshot();
  394. this.addTorrentLinks();
  395. this.updateReviews();
  396. this.updateComments();
  397. this.getPreview();
  398. }
  399. }
  400.  
  401. addStyle() {
  402. // left menu
  403. GM_addStyle(`
  404. #leftmenu {
  405. display: none;
  406. width: 0%;
  407. }
  408. #rightcolumn {
  409. margin-left: 10px;
  410. }
  411. /*
  412. #video_title .post-title:hover {
  413. text-decoration: underline;
  414. text-decoration-color: #CCCCCC;
  415. }
  416. */
  417. #video_id .text {
  418. color: red;
  419. }
  420. #torrents > table {
  421. width:100%;
  422. text-align: center;
  423. border: 2px solid grey;
  424. }
  425. #torrents tr td + td {
  426. border-left: 2px solid grey;
  427. }
  428. #video_favorite_edit {
  429. margin-bottom: 20px;
  430. }
  431. #torrents {
  432. margin-bottom: 20px;
  433. }
  434. #preview {
  435. margin-bottom: 20px;
  436. }
  437. #preview video {
  438. max-width: 100%;
  439. }
  440. .screenshot {
  441. cursor: pointer;
  442. max-width: 25%;
  443. display: block;
  444. }
  445. .clickToCopy {
  446. cursor: pointer;
  447. }
  448. `);
  449. }
  450.  
  451. setEditionNumber() {
  452. let edition = document.getElementById('video_id').getElementsByClassName('text')[0];
  453. this.editionNumber = edition.innerText;
  454. }
  455.  
  456. async updateTitle() {
  457. let videoTitle = document.getElementById('video_title');
  458. let postTitle = videoTitle.getElementsByClassName('post-title')[0];
  459. postTitle.innerText = postTitle.getElementsByTagName('a')[0].innerText;
  460.  
  461. // Add English title
  462. if (!window.location.href.includes('/en/')) {
  463. let request = {url: `/en/?v=${this.javVideoId}`};
  464. let result = await xhrFetch(request).catch(err => {console.log(err); return;});
  465. let videoDetailsDoc = parseHTMLText(result.responseText);
  466. let englishTitle = videoDetailsDoc.getElementById('video_title')
  467. .getElementsByClassName('post-title')[0]
  468. .getElementsByTagName('a')[0].innerText;
  469. postTitle.innerHTML = `${postTitle.innerText}<br/>${englishTitle}`;
  470. }
  471. }
  472.  
  473. scrollToTop(element) {
  474. let distanceToTop = element.getBoundingClientRect().top;
  475. if (distanceToTop < 0) {
  476.  
  477. window.scrollBy(0, distanceToTop);
  478. }
  479. }
  480.  
  481. screenShotOnclick(element) {
  482. if (element.style['max-width'] != '100%') {
  483. element.style['max-width'] = '100%';
  484. } else {
  485. element.style['max-width'] = '25%';
  486. }
  487. this.scrollToTop(element);
  488. }
  489.  
  490. lazyScreenShotOnclick(element) {
  491. let currentSrc = element.src;
  492. element.src = element.dataset.src;
  493. element.dataset.src = currentSrc;
  494. element.style['max-width'] = '100%';
  495. this.scrollToTop(element);
  496. }
  497.  
  498. async addScreenshot() {
  499. let javscreensUrl = `http://javscreens.com/images/${this.editionNumber}.jpg`;
  500. let videoDates = document.getElementById('video_date').getElementsByClassName('text')[0].innerText.split('-');
  501. let jbUrl = `http://img.japanese-bukkake.net/${videoDates[0]}/${videoDates[1]}/${this.editionNumber}_s.jpg`;
  502.  
  503. for (let url of [javscreensUrl, jbUrl]) {
  504. let img = await this.loadImg(url).catch((img) => {return img;});
  505. if (img && img.naturalHeight > 200) {
  506. // Valid screenshot loaded, break the loop
  507. break;
  508. }
  509. removeElementIfPresent(img);
  510. }
  511. }
  512.  
  513. loadImg(url) {
  514. console.log('Get screenshot ' + url);
  515. return new Promise((resolve, reject) => {
  516. let img = createElementFromHTML(`<img src="${url}" class="screenshot" title="">`);
  517. insertBefore(img, document.getElementById('rightcolumn').getElementsByClassName('socialmedia')[0]);
  518. img.addEventListener('click', () => this.screenShotOnclick(img));
  519.  
  520. img.onload = () => resolve(img);
  521. img.onerror = () => reject(img);
  522. });
  523. }
  524.  
  525. addTorrentLinks() {
  526. let sukebei = `https://sukebei.nyaa.si/?f=0&c=0_0&q=${this.editionNumber}`;
  527. let btsow = `https://btsow.rest/search/${this.editionNumber}`;
  528. let javbus = `https://www.seejav.blog/${this.editionNumber}`;
  529. let torrentKitty = `https://www.torrentkitty.tv/search/${this.editionNumber}`;
  530. let tokyotosho = `https://www.tokyotosho.info/search.php?terms=${this.editionNumber}`;
  531. let javdb = `https://javdb39.com/search?q=${this.editionNumber}`;
  532. let btDigg = `http://btdig.com/search?q=${this.editionNumber}`;
  533. let idope = `https://idope.se/torrent-list/${this.editionNumber}/`;
  534.  
  535. let torrentsHTML = `
  536. <div id="torrents">
  537. <!--
  538. <form id="form-btkitty" method="post" target="_blank" action="http://btkittyba.co/">
  539. <input type="hidden" name="keyword" value="${this.editionNumber}">
  540. <input type="hidden" name="hidden" value="true">
  541. </form>
  542. <form id="form-btdiggs" method="post" target="_blank" action="http://btdiggba.me/">
  543. <input type="hidden" name="keyword" value="${this.editionNumber}">
  544. </form>
  545. -->
  546. <table>
  547. <tr>
  548. <td><strong>种子:</strong></td>
  549. <td><a href="${sukebei}" target="_blank">sukebei</a></td>
  550. <td><a href="${btsow}" target="_blank">btsow</a></td>
  551. <td><a href="${javbus}" target="_blank">javbus</a></td>
  552. <td><a href="${torrentKitty}" target="_blank">torrentKitty</a></td>
  553. <td><a href="${tokyotosho}" target="_blank">tokyotosho</a></td>
  554. <td><a href="${javdb}" target="_blank">javdb</a></td>
  555. <td><a href="${btDigg}" target="_blank">btDigg</a></td>
  556. <td><a href="${idope}" target="_blank">idope</a></td>
  557. <!--
  558. <td><a id="btkitty" href="JavaScript:Void(0);" onclick="document.getElementById('form-btkitty').submit();">btkitty</a></td>
  559. <td><a id="btdiggs" href="JavaScript:Void(0);" onclick="document.getElementById('form-btdiggs').submit();">btdiggs</a></td>
  560. -->
  561.  
  562. </tr>
  563. </table>
  564. </div>
  565. `;
  566.  
  567. let torrents = createElementFromHTML(torrentsHTML);
  568. insertAfter(torrents, document.getElementById('video_favorite_edit'));
  569. }
  570.  
  571. updateReviews() {
  572. // Remove existing reviews
  573. let videoReviews = document.getElementById('video_reviews');
  574. Array.from(videoReviews.children).forEach(child => {
  575. if (child.id.includes('review')) {
  576. child.parentNode.removeChild(child);
  577. }
  578. });
  579.  
  580. // Add all reviews
  581. this.getNextPage(1, 'reviews');
  582. }
  583.  
  584. async getNextPage(page, pageType) {
  585. let loadMoreId = 'load_more_' + pageType;
  586. let urlPath = 'video' + pageType;
  587. let elementsId = 'video_' + pageType;
  588.  
  589. // Load more reviews
  590. let request = {url: `/cn/${urlPath}.php?v=${this.javVideoId}&mode=2&page=${page}`};
  591. let result = await xhrFetch(request).catch(err => {console.log(err); return;});
  592. let doc = parseHTMLText(result.responseText);
  593.  
  594. // Remove the load more div in current page
  595. let loadMoreDiv = document.getElementById(loadMoreId);
  596. if (loadMoreDiv != null) {
  597. loadMoreDiv.parentNode.removeChild(loadMoreDiv);
  598. }
  599.  
  600. // Get comments/reviews in the next page
  601. let elements = doc.getElementById(elementsId);
  602. if (!elements.getElementsByClassName('t')[0] || !doc.getElementsByClassName('page_selector')[0]) {
  603. return;
  604. }
  605.  
  606. // Set element texts
  607. Array.from(elements.getElementsByClassName('t')).forEach(element => {
  608. let elementText = parseBBCode(escapeHtml(element.getElementsByTagName('textarea')[0].innerText));
  609. let elementHtml = createElementFromHTML(`<div>${parseHTMLText(elementText).body.innerHTML}</div>`);
  610. element.getElementsByClassName('text')[0].replaceWith(this.processScreenshot(elementHtml));
  611. });
  612.  
  613. // Append elements to the page
  614. let currentElements = document.getElementById(elementsId);
  615. let bottomLine = currentElements.getElementsByClassName('grey')[0];
  616. Array.from(elements.children).forEach(element => {
  617. if (element.tagName == 'TABLE') {
  618. currentElements.insertBefore(element, bottomLine);
  619. }
  620. });
  621.  
  622. // Append load more if next page exists
  623. let nextPage = doc.getElementsByClassName('page next')[0];
  624. if (nextPage) {
  625. let loadMoreButton = getLoadMoreButton(loadMoreId, async () => this.getNextPage(page + 1, pageType));
  626. insertAfter(loadMoreButton, currentElements);
  627. }
  628. }
  629.  
  630. updateComments() {
  631. // Remove existing comments
  632. let videoComments = document.getElementById('video_comments');
  633. Array.from(videoComments.children).forEach(child => {
  634. if (child.id.includes('comment')) {
  635. child.parentNode.removeChild(child);
  636. }
  637. });
  638.  
  639. // Add all comments
  640. this.getNextPage(1, 'comments');
  641. }
  642.  
  643. processScreenshot(content) {
  644. let sources = [
  645. {regex: /imgspice/, process: (input) => input.replace(/_s|_t/, '')},
  646. {regex: /t[\d]+\.pixhost/, process: (input) => input.replace(/t([\d]+\.)/, 'img$1').replace('/thumbs/', '/images/')},
  647. {regex: /img[\d]+\.pixhost/, process: (input) => input},
  648. {regex: /imagetwist/, process: (input) => input.replace('/th/', '/i/')},
  649. {regex: /oloadcdn/, process: (input) => input},
  650. {regex: /subyshare/, process: (input) => input},
  651. {regex: /verystream/, process: (input) => input},
  652. {regex: /iceimg/, process: (input) => input.replace('/ssd/small/', '/uploads3/pixsense/big/').replace('/small-', '/')},
  653. {regex: /imgfrost/, process: (input) => input.replace('/small/small_', '/big/')},
  654. {regex: /japanese\-bukkake/, process: (input) => input},
  655. {regex: /picz\.in\.th/, process: (input) => input.replace('.md', '')},
  656. {regex: /photosex/, process: (input) => input},
  657. {regex: /imgtaxi/, process: (input) => input.replace('/small/', '/big/').replace('/small-medium/', '/big/')},
  658. {regex: /imgdrive/, process: (input) => input.replace('/small/', '/big/').replace('/small-medium/', '/big/')},
  659. {regex: /sehuatuchuang/, process: (input) => input},
  660. {regex: /900file/, process: (input) => input},
  661. {regex: /avcensdownload/, process: (input) => input},
  662. {regex: /filesor/, process: (input) => input.replace('_s', '')},
  663. ];
  664.  
  665. // Get full img url
  666. Array.from(content.getElementsByTagName('img')).forEach(img => {
  667. if (img.src != null) {
  668. for (let source of sources) {
  669. if (img.src.match(source.regex)) {
  670. let rawImgUrl = source.process(img.src);
  671. let screenshot = createElementFromHTML(`
  672. <img class="screenshot processed"
  673. referrerpolicy="no-referrer"
  674. src="${img.src}"
  675. data-src="${rawImgUrl}">
  676. `);
  677. screenshot.addEventListener('click', () => this.lazyScreenShotOnclick(screenshot));
  678. img.replaceWith(screenshot);
  679. break;
  680. }
  681. }
  682. }
  683. });
  684.  
  685. // Remove the redirection
  686. Array.from(content.getElementsByTagName('a')).forEach(element => {
  687. let imgs = element.getElementsByTagName('img');
  688. if (imgs.length == 1 && imgs[0].className.includes('processed')) {
  689. element.replaceWith(imgs[0]);
  690. }
  691. });
  692.  
  693. return content;
  694. }
  695.  
  696. getPreview() {
  697. let includesEditionNumber = (str) => {
  698. return str != null
  699. // && str.includes(this.editionNumber.toLowerCase().split('-')[0])
  700. && str.includes(this.editionNumber.toLowerCase().split('-')[1]);
  701. }
  702.  
  703. let r18 = async () => {
  704. let request = {url: `https://www.r18.com/common/search/order=match/searchword=${this.editionNumber}/`};
  705. let result = await gmFetch(request).catch(err => {console.log(err); return;});
  706. let video_tag = parseHTMLText(result.responseText).querySelector('.js-view-sample');
  707. let src = ['high', 'med', 'low']
  708. .map(i => video_tag.getAttribute('data-video-' + i))
  709. .find(i => i);
  710. return src;
  711. }
  712.  
  713. let dmm = async () => {
  714. // Find dmm cid
  715. let bingRequest = {url: `https://www.bing.com/search?q=${this.editionNumber.toLowerCase()}+site%3awww.dmm.co.jp`}
  716. let bingResult = await gmFetch(bingRequest).catch(err => {console.log(err); return;});
  717. let bingDoc = parseHTMLText(bingResult.responseText);
  718. let pattern = /(cid=[\w]+|pid=[\w]+)/g;
  719. let dmmCid = '';
  720. for (let match of bingDoc.body.innerHTML.match(pattern)) {
  721. if (includesEditionNumber(match)) {
  722. dmmCid = match.replace(/(cid=|pid=)/, '');
  723. break;
  724. }
  725. }
  726.  
  727. if (dmmCid == '') {
  728. return;
  729. }
  730.  
  731. let request = {url: `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${dmmCid}/mtype=AhRVShI_/service=litevideo/mode=/width=560/height=360/`};
  732. let result = await gmFetch(request).catch(err => {console.log(err); return;});
  733. let doc = parseHTMLText(result.responseText);
  734.  
  735. // Very hacky... Didn't find a way to parse the HTML with JS.
  736. for (let script of doc.getElementsByTagName('script')) {
  737. if (script.innerText != null && script.innerText.includes('.mp4')) {
  738. for (let line of script.innerText.split('\n')) {
  739. if (line.includes('var params')) {
  740. line = line.replace('var params =', '').replace(';', '');
  741. let videoSrc = JSON.parse(line).src;
  742. if (!videoSrc.startsWith('http')) {
  743. videoSrc = 'http:' + videoSrc;
  744. }
  745. return videoSrc;
  746. }
  747. }
  748. }
  749. }
  750. }
  751.  
  752. // let sod = async () => {
  753. // let request = {url: `https://ec.sod.co.jp/prime/videos/sample.php?id=${this.editionNumber}`};
  754. // let result = await gmFetch(request).catch(err => {console.log(err); return;});
  755. // let doc = parseHTMLText(result.responseText);
  756. // return doc.getElementsByTagName('source')[0].src;
  757. // }
  758.  
  759. let jav321 = async () => {
  760. let request = {
  761. url: `https://www.jav321.com/search`,
  762. method: 'POST',
  763. data: `sn=${this.editionNumber}`,
  764. headers: {
  765. referer: 'https://www.jav321.com/',
  766. 'content-type': 'application/x-www-form-urlencoded',
  767. },
  768. };
  769.  
  770. let result = await gmFetch(request).catch(err => {console.log(err); return;});
  771. let doc = parseHTMLText(result.responseText);
  772. return doc.getElementsByTagName('source')[0].src;
  773. }
  774.  
  775. let kv = async () => {
  776. if (this.editionNumber.includes('KV-')) {
  777. return `http://fskvsample.knights-visual.com/samplemov/${this.editionNumber.toLowerCase()}-samp-st.mp4`;
  778. }
  779.  
  780. return;
  781. }
  782.  
  783. // // Prepare for sod adult check and DDoS check
  784. // // iframe vs. embed vs. object https://stackoverflow.com/a/21115112/4214478
  785. // // ifrmae sandbox https://www.w3schools.com/tags/att_iframe_sandbox.asp
  786. // insertBefore(
  787. // createElementFromHTML(`<iframe src="https://ec.sod.co.jp/prime/_ontime.php"
  788. // style="display:none;" referrerpolicy="no-referrer" sandbox>
  789. // </iframe>`),
  790. // document.getElementById('topmenu'));
  791.  
  792. Promise.all(
  793. [jav321, r18, dmm, kv].map(source => source().catch(err => {console.log(err); return;}))
  794. ).then(responses => {
  795. console.log(responses);
  796.  
  797. let videoHtml = responses
  798. .filter(response => response != null
  799. && includesEditionNumber(response)
  800. && !response.includes('//_sample.mp4'))
  801. .map(response => `<source src="${response}">`)
  802. .join('');
  803. if (videoHtml != '') {
  804. let previewHtml = `
  805. <div id="preview">
  806. <video controls onloadstart="this.volume=0.5">
  807. ${videoHtml}
  808. </video>
  809. </div>
  810. `;
  811. insertAfter(createElementFromHTML(previewHtml), document.getElementById('torrents'));
  812. }
  813. });
  814. }
  815. }
  816.  
  817. function blockAds() {
  818. // Remove ad script if possible
  819. let ad = window.document.querySelector('script[src*="yuanmengbi"]');
  820. let adId = (ad && ad.src) ? (new URL(ad.src)).searchParams.get('id') : '291';
  821. removeElementIfPresent(ad);
  822.  
  823. // Add cookie to bypass ad
  824. setCookie(adId, "1");
  825.  
  826. // Not open ad url
  827. // https://stackoverflow.com/a/9172526
  828. // https://stackoverflow.com/a/4658196
  829. let scope = (typeof unsafeWindow === "undefined") ? window : unsafeWindow;
  830. scope.open = function(open) {
  831. return function(url, name, features) {
  832. if (url.includes('yuanmengbi')) {
  833. return;
  834. }
  835. return open.call(scope, url, name, features);
  836. };
  837. }(scope.open);
  838. }
  839.  
  840. // Block ad
  841. blockAds();
  842.  
  843. // Adult check
  844. setCookie('over18', 18);
  845.  
  846. // Style change
  847. addStyle();
  848. if (!window.location.href.includes('.php')
  849. && (window.location.href.includes('?v=') || window.location.href.includes('&v='))) {
  850. new MiniDriver().execute();
  851. } else {
  852. new MiniDriverThumbnail().execute();
  853. }