您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Jav小司机。简单轻量速度快!
// ==UserScript== // @name Jav小司机 // @namespace wddd // @version 1.1.5 // @author wddd // @license MIT // @include http*://*javlibrary.com/* // @include http*://*javlib.com/* // @include http*://*m34z.com/* // @include http*://*j41g.com/* // @include http*://*h28o.com/* // @description Jav小司机。简单轻量速度快! // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_log // @homepage https://github.com/wdwind/JavMiniDriver // ==/UserScript== // Credit to // * https://greasyfork.org/zh-CN/scripts/25781 // * https://greasyfork.org/zh-CN/scripts/37122 // Change log // 1.1.5 /** * Add page selector in video detail page * Support filters by score and viewers */ // 1.1.4 /** * Add support for j41g.com and h28o.com * Block ad * Only load the full screenshot until user clicks the thumbnail */ // 1.1.3 /** * Issue: https://github.com/wdwind/JavMiniDriver/issues/1#issuecomment-521836751 * * Update browser history when clicking "load more" button in video list page * Store the configuration of whether to show the page selector in local storage instead of cookies * Fix a screenshot bug to handle non-existing images gracefully * Temporarily remove video from sod.co.jp since it requires a Referer in http request header * ~~Add a iframe to bypass adult check and DDoS check of sod.co.jp~~ * Other technical refactoring */ // 1.1.2 /** * Issue: https://greasyfork.org/zh-CN/forum/discussion/61213/x * * Minor updates * Add javbus torrent search * Add support for javlib.com and m34z.com */ // 1.1.1 /** * Issue: https://github.com/wdwind/JavMiniDriver/issues/1 * * Change thumbnail font * Add page selector * Add japanese-bukkake as backup image screenshot source * Change image width to max-width when clicking the screenshot to prevent image being over zoomed * Add more data sources for the screenshots in reviews/comments */ // 1.1.0 /** * Simplify code by merging the functions for get more comments/reviews * Process screenshots in reviews/comments * Remove redirection * Get full image url * Add mouse click effect */ // Utils function setCookie(cookieName, cookieValue, expireDays) { let expireDate =new Date(); expireDate.setDate(expireDate.getDate() + expireDays); let expires = "expires=" + ((expireDays == null) ? '' : expireDate.toUTCString()); document.cookie = cookieName + "=" + cookieValue + ";" + expires + ";path=/"; } // Not used // function getCookie(cookieName) { // let value = "; " + document.cookie; // let parts = value.split("; " + cookieName + "="); // if (parts.length == 2) { // return parts.pop().split(";").shift(); // } // } function insertAfter(newNode, referenceNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } function insertBefore(newNode, referenceNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode); } function removeElementIfPresent(element) { if (element) { return element.parentNode.removeChild(element); } } function parseHTMLText(text) { try { let doc = document.implementation.createHTMLDocument(''); doc.documentElement.innerHTML = text; return doc; } catch (e) { console.error('Parse error'); } } // https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro function createElementFromHTML(html) { var template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result template.innerHTML = html; return template.content.firstChild; } // For the requests in different domains // GM_xmlhttpRequest is made from the background page, and as a result, it // doesn't have cookies in the request header function gmFetch(obj) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: obj.method || 'GET', // timeout in ms timeout: obj.timeout, url: obj.url, headers: obj.headers ? obj.headers : {}, data: obj.data, onload: (result) => { if (result.status >= 200 && result.status < 300) { resolve(result); } else { reject(result); } }, onerror: reject, ontimeout: reject, }); }); } // For the requests in the same domain // XMLHttpRequest is made within the page, so it will send the cookies function xhrFetch(obj) { return fetch(obj.url, { method: obj.method || 'GET', headers: obj.headers || {}, body: obj.data, credentials: 'include', timeout: obj.timeout, }); } function xhrFetchWithRetry(obj) { let fun = (obj) => xhrFetch(obj).then(response => { if (response.status == 429) { throw new Error('429 (Too Many Requests)'); } else { return response; } }); return retry(fun, obj); } async function retry(fun, input, max_retries = 10, retries = 0, initial = 8000) { try { return await fun(input); } catch (e) { if (retries > max_retries) { throw e; } GM_log(`Retrying ${retries}, wait ${initial + (2 ** retries) * 1000} ms`); // wait await new Promise(_ => setTimeout(_, initial + (2 ** retries) * 1000)); // retry return retry(fun, input, max_retries, retries + 1, initial); } } // Style function addStyle() { // social media GM_addStyle(` #toplogo { height: 55px; } .socialmedia { display: none !important; width: 0% !important; height: 0% !important; } .videothumblist .videos .video { height: 290px; } .thumbnailDetail { font-size: 14px; margin-top: 2.5em; color: #666666; } .page_selector { display: block; margin-bottom: 15px; } .load_more { text-align: center; } #load_next_page { margin-bottom: 10px; } #load_next_page_button { display: inline; } #togglePageSelector { margin-left: 20px; font-size: 14px; vertical-align: text-top; display: inline; } .toggle { cursor: pointer; color: blue; } .bottombanner2 { display: none !important; } table.displaymode { table-layout: fixed; } td.mid { text-align: left; font: bold 12px monospace; } input.slider { direction: rtl; height: 10px; margin-left: 10px; } .filter { display: inline-block; } .filterMinValue { display: inline-block; } `); // Homepage if (!window.location.href.includes('.php')) { GM_addStyle(` .videothumblist { height: 645px !important; } `); } } // Thumbnail class MiniDriverThumbnail { constructor() { this.filterKeys = ['minScore', 'minViewer']; this.filterConfigs = {'minScore': {'value' : GM_getValue('minScore', 0), 'max': 10}, 'minViewer': {'value' : GM_getValue('minViewer', 0), 'max': 100}}; this.videoStats = {}; } execute() { let videos = document.getElementsByClassName('videos')[0]; document.getElementsByClassName('videothumblist')[0].innerHTML = `<div class="videothumblist"> <div class="videos"></div> </div>`; let pageSelector = document.getElementsByClassName('page_selector')[0]; this.updatePageContent(videos, pageSelector); this.addFilters(); } addFilters() { let filters = createElementFromHTML( `<td class="mid"> <div class="filter"> 显示 <label for="score">评分 > </label> <div class="filterMinValue">0</div> <!--<input type="number" id="minScore" min="0">--> <input type="range" id="minScore" min="0" class="slider"> </div> <div class="filter"> <label for="viewers">观看人数 > </label> <div class="filterMinValue">0</div> <!--<input type="number" id="minViewer" min="0">--> <input type="range" id="minViewer" min="0" class="slider"> </div> </td>`); let sliders = filters.getElementsByClassName('slider'); let valueDiv = filters.getElementsByClassName('filterMinValue'); function round(num) { return Math.round(num * 100) / 100; } function getSliderValue(input, max) { return round(100 - input * (100 / max)); } function getConfigValue(input, max) { return round(max - input / (100 / max)); } for (let i = 0; i < sliders.length; i++) { let config = this.filterConfigs[this.filterKeys[i]]; sliders[i].value = getSliderValue(config['value'], config['max']); valueDiv[i].innerText = config['value']; sliders[i].addEventListener('change', () => { let updatedConfig = getConfigValue(sliders[i].value, config['max']); valueDiv[i].innerText = updatedConfig; GM_setValue(this.filterKeys[i], updatedConfig); this.applyFilters(); }); } // Insert filter to the page let mode = document.getElementsByClassName('displaymode'); if (mode.length > 0) { let leftMode = mode[0].getElementsByClassName('left'); if (leftMode.length > 0) { insertAfter(filters, leftMode[0]); } } } applyFilters() { for (let key in this.videoStats) { this.applyFilterOn(key); } } applyFilterOn(videoKey) { let video = document.getElementById(videoKey); if (video) { let show = true; for (let filter of this.filterKeys) { let config = GM_getValue(filter, 0); if (config > 0) { if (!(filter in this.videoStats[videoKey]) || !this.videoStats[videoKey][filter] || this.videoStats[videoKey][filter] == NaN || this.videoStats[videoKey][filter] < config) { show = false; if (!show) { break; } } } } video.style.display = show ? 'inline-block' : 'none'; } } updatePageContent(videos, pageSelector) { // Add videos to the page let currentVideos = document.getElementsByClassName('videos')[0]; if (videos) { Array.from(videos.children).forEach(video => { currentVideos.appendChild(video); this.updateVideoDetail(video); this.updateVideoEvents(video); }); } // Replace page selector content let pageSelectorId = 'pageSelectorThumbnail'; let showPageSelector = GM_getValue(pageSelectorId, 'none') != 'block' ? 'none' : 'block'; document.getElementsByClassName('page_selector')[0].innerHTML = pageSelector.innerHTML; document.getElementsByClassName('page_selector')[0].id = pageSelectorId; document.getElementsByClassName('page_selector')[0].style.display = showPageSelector; } async updateVideoDetail(video) { if (video.id.includes('vid_')) { let request = {url: `/cn/?v=${video.id.substring(4)}`}; let response = await xhrFetchWithRetry(request).catch(err => {GM_log(err); return;}); let responseText = await response.text().catch(err => {GM_log(err); return;}); let videoDetailsDoc = parseHTMLText(responseText); // Video date let videoDate = ''; if (videoDetailsDoc.getElementById('video_date')) { videoDate = videoDetailsDoc.getElementById('video_date').getElementsByClassName('text')[0].innerText; } // Video score let videoScore = ''; if (videoDetailsDoc.getElementById('video_review')) { let videoScoreStr = videoDetailsDoc.getElementById('video_review').getElementsByClassName('score')[0].innerText; videoScore = videoScoreStr.substring(1, videoScoreStr.length - 1); if (!(video.id in this.videoStats)) { this.videoStats[video.id] = {}; } this.videoStats[video.id]['minScore'] = parseFloat(videoScore); } // Video watched let videoWatched = '0'; if (videoDetailsDoc.getElementById('watched')) { videoWatched = videoDetailsDoc.getElementById('watched').getElementsByTagName('a')[0].innerText; if (!(video.id in this.videoStats)) { this.videoStats[video.id] = {}; } this.videoStats[video.id]['minViewer'] = parseFloat(videoWatched); } let videoDetailsHtml = ` <div class="thumbnailDetail"> <span>${videoDate}</span> <span style='color:red;'>${videoScore}</span> <br/> <span>${videoWatched} 人看过</span> </div> `; let videoDetails = createElementFromHTML(videoDetailsHtml); video.insertBefore(videoDetails, video.getElementsByClassName('toolbar')[0]); // Apply filter this.applyFilterOn(video.id); } } updateVideoEvents(video) { if (video) { // Prevent existing listeners https://stackoverflow.com/a/46986927/4214478 video.addEventListener('mouseout', (event) => { event.stopImmediatePropagation(); video.getElementsByClassName('toolbar')[0].style.display = 'none'; }, true); video.addEventListener('mouseover', (event) => { event.stopImmediatePropagation(); video.getElementsByClassName('toolbar')[0].style.display = 'block'; }, true); } } async getNextPage(url) { // Update page URL and history history.pushState(history.state, window.document.title, url); // Fetch next page let response = await xhrFetchWithRetry({url: url}).catch(err => {GM_log(err); return;}); let responseText = await response.text().catch(err => {GM_log(err); return;}); let nextPageDoc = parseHTMLText(responseText); // Update page content let videos = nextPageDoc.getElementsByClassName('videos')[0]; let pageSelector = nextPageDoc.getElementsByClassName('page_selector')[0]; this.updatePageContent(videos, pageSelector); } } class MiniDriver { execute() { let javUrl = new URL(window.location.href); this.javVideoId = javUrl.searchParams.get('v'); // Video page if (this.javVideoId != null) { this.addStyle(); this.setEditionNumber(); this.updateTitle(); this.addScreenshot(); this.addTorrentLinks(); this.updateReviews(); this.updateComments(); this.getPreview(); } } addStyle() { // left menu GM_addStyle(` #leftmenu { display: none; width: 0%; } #rightcolumn { margin-left: 10px; } /* #video_title .post-title:hover { text-decoration: underline; text-decoration-color: #CCCCCC; } */ #video_id .text { color: red; } #torrents > table { width:100%; text-align: center; border: 2px solid grey; } #torrents tr td + td { border-left: 2px solid grey; } #video_favorite_edit { margin-bottom: 20px; } #torrents { margin-bottom: 20px; } #preview { margin-bottom: 20px; } #preview video { max-width: 100%; max-height: 80vh; } .screenshot { cursor: pointer; max-width: 25%; display: block; } .clickToCopy { cursor: pointer; } `); } setEditionNumber() { let edition = document.getElementById('video_id').getElementsByClassName('text')[0]; this.editionNumber = edition.innerText; } async updateTitle() { let videoTitle = document.getElementById('video_title'); let postTitle = videoTitle.getElementsByClassName('post-title')[0]; postTitle.innerText = postTitle.getElementsByTagName('a')[0].innerText; // Add English title if (!window.location.href.includes('/en/')) { let request = {url: `/en/?v=${this.javVideoId}`}; let response = await xhrFetchWithRetry(request).catch(err => {GM_log(err); return;}); let responseText = await response.text().catch(err => {GM_log(err); return;}); let videoDetailsDoc = parseHTMLText(responseText); let englishTitle = videoDetailsDoc.getElementById('video_title') .getElementsByClassName('post-title')[0] .getElementsByTagName('a')[0].innerText; postTitle.innerHTML = `${postTitle.innerText}<br/>${englishTitle}`; } } scrollToTop(element) { let distanceToTop = element.getBoundingClientRect().top; if (distanceToTop < 0) { window.scrollBy(0, distanceToTop); } } screenShotOnclick(element) { if (element.style['max-width'] != '100%') { element.style['max-width'] = '100%'; } else { element.style['max-width'] = '25%'; } this.scrollToTop(element); } lazyScreenShotOnclick(element) { let currentSrc = element.src; element.src = element.dataset.src; element.dataset.src = currentSrc; element.style['max-width'] = '100%'; this.scrollToTop(element); } async addScreenshot() { let javscreensUrl = `http://javscreens.com/images/${this.editionNumber}.jpg`; let videoDates = document.getElementById('video_date').getElementsByClassName('text')[0].innerText.split('-'); let jbUrl = `http://img.japanese-bukkake.net/${videoDates[0]}/${videoDates[1]}/${this.editionNumber}_s.jpg`; for (let url of [javscreensUrl, jbUrl]) { let img = await this.loadImg(url).catch((img) => {return img;}); if (img && img.naturalHeight > 200) { insertBefore(img, document.getElementById('rightcolumn').getElementsByClassName('socialmedia')[0]); img.addEventListener('click', () => this.screenShotOnclick(img)); // Valid screenshot loaded, break the loop break; } removeElementIfPresent(img); } } loadImg(url) { return new Promise(function (resolve, reject) { GM_xmlhttpRequest({ method: 'GET', responseType: 'blob', url: url, onload: (result) => { if (result.status >= 200 && result.status < 300) { let img = createElementFromHTML(`<img class="screenshot" title="">`); insertBefore(img, document.getElementById('rightcolumn').getElementsByClassName('socialmedia')[0]); img.src = window.URL.createObjectURL(result.response); img.onload = () => resolve(img); img.onerror = () => reject(img); } else { reject(); } }, onerror: reject, ontimeout: reject, }); }); } addTorrentLinks() { let sukebei = `https://sukebei.nyaa.si/?f=0&c=0_0&q=${this.editionNumber}`; let btsow = `https://btos.pw/search/${this.editionNumber}`; let javbus = `https://www.javbus.com/${this.editionNumber}`; let torrentKitty = `https://www.torrentkitty.tv/search/${this.editionNumber}`; let tokyotosho = `https://www.tokyotosho.info/search.php?terms=${this.editionNumber}`; let biedian = `https://biedian.me/search?source=%E7%A7%8D%E5%AD%90%E6%90%9C&s=time&p=1&k=${this.editionNumber}`; let btDigg = `http://btdig.com/search?q=${this.editionNumber}`; let idope = `https://idope.se/torrent-list/${this.editionNumber}/`; let torrentsHTML = ` <div id="torrents"> <!-- <form id="form-btkitty" method="post" target="_blank" action="http://btkittyba.co/"> <input type="hidden" name="keyword" value="${this.editionNumber}"> <input type="hidden" name="hidden" value="true"> </form> <form id="form-btdiggs" method="post" target="_blank" action="http://btdiggba.me/"> <input type="hidden" name="keyword" value="${this.editionNumber}"> </form> --> <table> <tr> <td><strong>种子:</strong></td> <td><a href="${sukebei}" target="_blank">sukebei</a></td> <td><a href="${btsow}" target="_blank">btsow</a></td> <td><a href="${javbus}" target="_blank">javbus</a></td> <td><a href="${torrentKitty}" target="_blank">torrentKitty</a></td> <td><a href="${tokyotosho}" target="_blank">tokyotosho</a></td> <td><a href="${biedian}" target="_blank">biedian</a></td> <td><a href="${btDigg}" target="_blank">btDigg</a></td> <td><a href="${idope}" target="_blank">idope</a></td> <!-- <td><a id="btkitty" href="JavaScript:Void(0);" onclick="document.getElementById('form-btkitty').submit();">btkitty</a></td> <td><a id="btdiggs" href="JavaScript:Void(0);" onclick="document.getElementById('form-btdiggs').submit();">btdiggs</a></td> --> </tr> </table> </div> `; let torrents = createElementFromHTML(torrentsHTML); insertAfter(torrents, document.getElementById('video_favorite_edit')); } updateReviews() { // Remove existing reviews let videoReviews = document.getElementById('video_reviews'); Array.from(videoReviews.children).forEach(child => { if (child.id.includes('review')) { child.parentNode.removeChild(child); } }); // Add all reviews this.getNextPage(1, 'reviews'); } async getNextPage(page, pageType) { let pageSelectorId = 'page_selector_' + pageType; let urlPath = 'video' + pageType; let elementsId = 'video_' + pageType; // Load more reviews let request = {url: `/cn/${urlPath}.php?v=${this.javVideoId}&mode=2&page=${page}`}; let response = await xhrFetchWithRetry(request).catch(err => {GM_log(err); return;}); let responseText = await response.text().catch(err => {GM_log(err); return;}); let doc = parseHTMLText(responseText); // Remove the page selector div in current page let oldPageSelectorDiv = document.getElementById(pageSelectorId); if (oldPageSelectorDiv != null) { oldPageSelectorDiv.parentNode.removeChild(oldPageSelectorDiv); } // Get comments/reviews in the next page let elements = doc.getElementById(elementsId); if (!elements.getElementsByClassName('t')[0] || !doc.getElementsByClassName('page_selector')[0]) { return; } // Set element texts Array.from(elements.getElementsByClassName('t')).forEach(element => { let elementText = parseBBCode(escapeHtml(element.getElementsByTagName('textarea')[0].innerText)); let elementHtml = createElementFromHTML(`<div>${parseHTMLText(elementText).body.innerHTML}</div>`); element.getElementsByClassName('text')[0].replaceWith(this.processUrls(elementHtml)); }); // Append elements to the page let currentElements = document.getElementById(elementsId); let bottomLine = currentElements.getElementsByClassName('grey')[0]; Array.from(elements.children).forEach(element => { if (element.tagName == 'TABLE' || element.tagName == 'TD') { currentElements.insertBefore(element, bottomLine); } }); // Append page selector let showPageSelector = GM_getValue(pageSelectorId, 'none') != 'block' ? 'none' : 'block'; let pageSelector = doc.getElementsByClassName('page_selector')[0]; if (pageSelector) { pageSelector.style.display = showPageSelector; pageSelector.id = pageSelectorId; let as = pageSelector.getElementsByTagName('a'); for (let a of as) { let nextPage = (new URL(a.href, a.href.includes('https') ? undefined : 'https://www.javlibrary.com/')).searchParams.get('page'); a.removeAttribute('href'); a.style.cursor = 'pointer'; a.addEventListener('click', async () => this.getNextPage(nextPage ? parseInt(nextPage) : 1, pageType)); } insertAfter(pageSelector, currentElements); } } updateComments() { // Remove existing comments let videoComments = document.getElementById('video_comments'); Array.from(videoComments.children).forEach(child => { if (child.id.includes('comment')) { child.parentNode.removeChild(child); } }); // Add all comments this.getNextPage(1, 'comments'); } processUrls(content) { Array.from(content.getElementsByTagName('a')).forEach(a => { if (a.href.includes('redirect.php?url=')) { let encodedRealUrl = a.href.replace('redirect.php?url=', ''); let realUrl = decodeURIComponent(encodedRealUrl); if (realUrl.indexOf('&ver=') > 0) { realUrl = realUrl.substring(0, realUrl.indexOf('&ver=')); } a.href = realUrl; } }); return content; } getPreview() { let nativeDmm = async() => { let dmmCid = document.getElementsByClassName('btn_videoplayer')[0].getAttribute('attr-data'); // let request = {url: `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${dmmCid}/mtype=AhRVShI_/service=litevideo/mode=/width=560/height=360/`}; let request = {url: `https://www.dmm.co.jp/service/-/html5_player/=/cid=${dmmCid}/mtype=AhRVShI_/service=mono/floor=dvd/mode=/`} let result = await gmFetch(request).catch(err => {GM_log(err); return;}); let doc = parseHTMLText(result.responseText); // Very hacky... Didn't find a way to parse the HTML with JS. for (let script of doc.getElementsByTagName('script')) { if (script.innerText != null && script.innerText.includes('.mp4')) { for (let line of script.innerText.split('\n')) { if (line.includes('.mp4')) { line = line.substring(line.indexOf('{'), line.lastIndexOf(';')); let videoSrc = JSON.parse(line).src; if (!videoSrc.startsWith('http')) { videoSrc = 'http:' + videoSrc; } return videoSrc; } } } } } // r18 site is shut down // let r18 = async () => { // let request = {url: `https://www.r18.com/common/search/order=match/searchword=${this.editionNumber}/`}; // let result = await gmFetch(request).catch(err => {GM_log(err); return;}); // let videoTag = parseHTMLText(result.responseText).querySelector('.js-view-sample'); // let src = ['high', 'med', 'low'] // .map(i => videoTag.getAttribute('data-video-' + i)) // .find(i => i); // return src; // } let javTrailer = async () => { let searchRequest = { url: `https://javtrailers.com/api/autocomplete?query=${this.editionNumber}&searchtype=id&lang=en`, headers: { authorization: 'AELAbPQCh_fifd93wMvf_kxMD_fqkUAVf@BVgb2!md@TNW8bUEopFExyGCoKRcZX', // cookie: 'auth.strategy=local; user-country=US; searchterm=fset-411; searchtype=id', // referer: 'https://javtrailers.com/', } }; let searchResult = await gmFetch(searchRequest).catch(err => {GM_log(err); return;}); let results = JSON.parse(searchResult.responseText).results; for (let result of results) { if (this.editionNumber === result.dvdId) { let videoRequest = { url : `https://javtrailers.com/api/video/${result.contentId}`, headers: { authorization: 'AELAbPQCh_fifd93wMvf_kxMD_fqkUAVf@BVgb2!md@TNW8bUEopFExyGCoKRcZX', // cookie: 'auth.strategy=local; user-country=US; searchterm=fset-411; searchtype=id', // referer: 'https://javtrailers.com/video/1fset00411', } }; let videoResult = await gmFetch(videoRequest).catch(err => {GM_log(err); return;}); let trailerUrl = JSON.parse(videoResult.responseText).video.trailer; if (trailerUrl.includes('.m3u8')) { GM_log(trailerUrl); GM_log('.m3u8 is not supported by HTML video tag on some browsers.'); return; } else { return trailerUrl; } } } } let dmm = async () => { let dmmCid = await this.getDmmCid(); if (dmmCid == null || dmmCid == '') { return; } // let request = {url: `https://www.dmm.co.jp/service/digitalapi/-/html5_player/=/cid=${dmmCid}/mtype=AhRVShI_/service=litevideo/mode=/width=560/height=360/`}; let request = {url: `https://www.dmm.co.jp/service/-/html5_player/=/cid=${dmmCid}/mtype=AhRVShI_/service=mono/floor=dvd/mode=/`} let result = await gmFetch(request).catch(err => {GM_log(err); return;}); let doc = parseHTMLText(result.responseText); // Very hacky... Didn't find a way to parse the HTML with JS. for (let script of doc.getElementsByTagName('script')) { if (script.innerText != null && script.innerText.includes('.mp4')) { for (let line of script.innerText.split('\n')) { if (line.includes('.mp4')) { line = line.substring(line.indexOf('{'), line.lastIndexOf(';')); let videoSrc = JSON.parse(line).src; if (!videoSrc.startsWith('http')) { videoSrc = 'http:' + videoSrc; } return videoSrc; } } } } } // let sod = async () => { // let request = {url: `https://ec.sod.co.jp/prime/videos/sample.php?id=${this.editionNumber}`}; // let result = await gmFetch(request).catch(err => {GM_log(err); return;}); // let doc = parseHTMLText(result.responseText); // return doc.getElementsByTagName('source')[0].src; // } // Site closed? let jav321 = async () => { let request = { url: `https://www.jav321.com/search`, method: 'POST', data: `sn=${this.editionNumber}`, headers: { referer: 'https://www.jav321.com/', 'content-type': 'application/x-www-form-urlencoded', }, }; let result = await gmFetch(request).catch(err => {GM_log(err); return;}); let doc = parseHTMLText(result.responseText); return doc.getElementsByTagName('source')[0].src; } let kv = async () => { if (this.editionNumber.includes('KV-')) { return `http://fskvsample.knights-visual.com/samplemov/${this.editionNumber.toLowerCase()}-samp-st.mp4`; } return; } // // Prepare for sod adult check and DDoS check // // iframe vs. embed vs. object https://stackoverflow.com/a/21115112/4214478 // // ifrmae sandbox https://www.w3schools.com/tags/att_iframe_sandbox.asp // insertBefore( // createElementFromHTML(`<iframe src="https://ec.sod.co.jp/prime/_ontime.php" // style="display:none;" referrerpolicy="no-referrer" sandbox> // </iframe>`), // document.getElementById('topmenu')); let previewSearchSources = document.getElementsByClassName('btn_videoplayer').length > 0 ? [nativeDmm] : [javTrailer, dmm, jav321, kv]; Promise.all( previewSearchSources.map(source => source().catch(err => {GM_log(err); return;})) ).then(responses => { GM_log(responses); let videoHtml = responses .filter(response => response != null && this.includesEditionNumber(response) && !response.includes('//_sample.mp4')) .map(response => `<source src="${response}">`) .join(''); if (videoHtml != '') { let previewHtml = ` <div id="preview"> <video controls onloadstart="this.volume=0.5"> <meta name="referrer" content="no-referrer"> ${videoHtml} </video> </div> `; insertAfter(createElementFromHTML(previewHtml), document.getElementById('torrents')); } }); } includesEditionNumber(str) { return str != null // && str.includes(this.editionNumber.toLowerCase().split('-')[0]) && str.includes(this.editionNumber.toLowerCase().split('-')[1]); } async getDmmCid() { let getCidFromUrl = (url) => { if (url.includes('dmm.co.jp') && this.includesEditionNumber(url)) { let cid = url.split('/').at(-2); return cid; } } let profileImageUrl = document.getElementById('video_jacket_img').src; let cid = getCidFromUrl(profileImageUrl); if (cid !== null) { return cid; } let urlPattern = /(http|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/g; for (let url of document.body.innerHTML.match(urlPattern)) { cid = getCidFromUrl(url); if (cid != null) { return cid; } } let getCidFromSearchEngine = async (searchUrl) => { let request = {url: searchUrl}; let response = await gmFetch(request).catch(err => {GM_log(err); return;}); let pattern = /(cid=[\w]+|pid=[\w]+)/g; for (let match of response.responseText.match(pattern)) { if (this.includesEditionNumber(match)) { return match.replace(/(cid=|pid=)/, ''); } } } // Find dmm cid from search engines let bingCid = getCidFromSearchEngine(`https://www.bing.com/search?q=${this.editionNumber.toLowerCase()}+site%3awww.dmm.co.jp`); if (bingCid != null) { return bingCid; } let googleCid = await getCidFromSearchEngine(`https://www.google.com/search?q=${this.editionNumber}+site%3Awww.dmm.co.jp`); if (googleCid != null) { return googleCid; } } } // Need `// @run-at document-start` to override the default addEventListener // Check https://stackoverflow.com/a/26269087/4214478 and https://stackoverflow.com/a/57437878/4214478 // EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener; // EventTarget.prototype.addEventListener = function(type, listener) { // if (this == document && type == 'click') { // GM_log('Prevent adding click event on "document" element. Event listener: ' + listener.toString()); // return; // } // this.addEventListenerBase(type, listener); // }; function blockAds() { // Not open ad url // https://stackoverflow.com/a/9172526 // https://stackoverflow.com/a/4658196 let adSites = ['yuanmengbi', 'zhaijv', 'henanlvyi']; let scope = (typeof unsafeWindow === "undefined") ? window : unsafeWindow; scope.open = function(open) { return function(url, name, features) { if (adSites.some(site => url.includes(site))) { return; } return open.call(scope, url, name, features); }; }(scope.open); } // Block ad blockAds(); // Adult check setCookie('over18', 18); // Style change addStyle(); if (!window.location.href.includes('.php') && (window.location.href.includes('?v=') || window.location.href.includes('&v='))) { new MiniDriver().execute(); } else { new MiniDriverThumbnail().execute(); }