您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replaces Flash manga viewer with an HTML5 setup
当前为
// ==UserScript== // @name Crunchyroll HTML5 Manga Viewer // @namespace DoomTay // @description Replaces Flash manga viewer with an HTML5 setup // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vast-player.min.js // @include http://www.crunchyroll.com/comics* // @version 0.4.1 // @grant none // ==/UserScript== var mangaObject = document.querySelector("object#showmedia_videoplayer_object"); var mangaConfig = {}; var pages = []; var currentPage = 0; if(mangaObject) { var flashVars = parseFlashVars(mangaObject.querySelector("param[name='flashvars']").value); var authToken = flashVars.auth || null; var mangaDiv = document.createElement("div"); mangaDiv.style.width = mangaObject.width; mangaDiv.style.height = mangaObject.height + "px"; mangaDiv.style.position = "relative"; var playerDiv = document.createElement("div"); playerDiv.style.width = mangaObject.width; playerDiv.style.height = mangaObject.height; mangaObject.parentNode.replaceChild(playerDiv,mangaObject); var pageL = document.createElement("img"); pageL.width = "460"; pageL.height = "690"; var pageR = document.createElement("img"); pageR.width = "460"; pageR.height = "690"; var doubleSpread = document.createElement("img"); doubleSpread.width = "920"; doubleSpread.height = "690"; doubleSpread.style.display = "none"; mangaDiv.appendChild(pageL); mangaDiv.appendChild(pageR); mangaDiv.appendChild(doubleSpread); var leftButton = document.createElement("div"); leftButton.style.position = "absolute"; leftButton.style.top = "50%"; leftButton.style.cursor = "pointer"; leftButton.style.color = "white"; leftButton.style.backgroundColor = "black"; leftButton.innerHTML = "\u2b05"; leftButton.onclick = function() { if(currentPage >= 0 && mangaConfig.pages[currentPage].is_spread) turnPage(currentPage + 1); else if(currentPage + 2 < mangaConfig.pages.length) turnPage(currentPage + 2); }; mangaDiv.appendChild(leftButton); var rightButton = document.createElement("div"); rightButton.style.position = "absolute"; rightButton.style.top = "50%"; rightButton.style.right = "0"; rightButton.style.cursor = "pointer"; rightButton.style.color = "white"; rightButton.style.backgroundColor = "black"; rightButton.innerHTML = "\u27a1"; rightButton.onclick = function() { if(mangaConfig.pages[currentPage - 1].is_spread) turnPage(currentPage - 1); else if(currentPage - 1 >= 0) turnPage(currentPage - 2); }; mangaDiv.appendChild(rightButton); getAdsConfig(flashVars.config_url).then(function(config) { var ads = Array.from(config.getElementsByTagName("manga:preload")[0].getElementsByTagName("adSlots")[0].getElementsByTagName("adSlot")[0].children,child => child.getAttribute("url")); if(ads.length > 0) { var adIndex = 0; var currentAd = ads[adIndex]; var adPlayer = new window.VASTPlayer(playerDiv); adPlayer.once('AdStopped', startManga); function prepareAd() { return adPlayer.load(currentAd) .catch(function(e) { if(adIndex < ads.length) { adIndex++; currentAd = ads[adIndex]; return prepareAd(); } }); } prepareAd().then(readyPlayer => readyPlayer.startAd()); } }); function startManga() { playerDiv.parentNode.replaceChild(mangaDiv,playerDiv); authenticate(authToken,flashVars.session_id,flashVars.seriesId).then(function(data) { return getJSON("http://" + flashVars.server + "/list_chapters?series%5Fid=" + flashVars.seriesId + (data ? ("&user%5Fid=" + data.user.user_id) : "")); }).then(function(config) { var chapterID = config.chapters.find(chapter => chapter.number == flashVars.chapterNumber).chapter_id; return getJSON("http://" + flashVars.server + "/list_chapter?chapter%5Fid=" + chapterID + "&session%5Fid=" + flashVars.session_id + "&auth=" + authToken); }).then(function(config) { mangaConfig = config; turnPage(0); }); } function turnPage(newPage) { if(newPage < 0) newPage = 0; currentPage = newPage; pageL.style.display = "none"; pageR.style.display = "none"; doubleSpread.style.display = "none"; setPage(pageL,"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); setPage(pageR,"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); setPage(doubleSpread,"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); var pageData = mangaConfig.pages[newPage]; if(pageData.is_spread) { doubleSpread.style.display = ""; if(pages[newPage]) setPage(doubleSpread,pages[newPage]); else getDecryptedImage(mangaConfig.pages[newPage].locale[flashVars.locale].encrypted_composed_image_url).then(function(pageURL) { pages[newPage] = pageURL; setPage(doubleSpread,pages[newPage]); }) } else { pageL.style.display = ""; pageR.style.display = ""; if(pageData.page_number == 0 || pageData.page_number == 1) { if(pages[newPage]) setPage(pageR,pages[newPage]); else getDecryptedImage(mangaConfig.pages[newPage].locale[flashVars.locale].encrypted_composed_image_url).then(function(pageURL) { pages[newPage] = pageURL; setPage(pageR,pages[newPage]); }) if(pages[newPage + 1]) setPage(pageL,pages[newPage + 1]); else if(newPage + 1 < mangaConfig.pages.length) getDecryptedImage(mangaConfig.pages[newPage + 1].locale[flashVars.locale].encrypted_composed_image_url).then(function(pageURL) { pages[newPage + 1] = pageURL; setPage(pageL,pages[newPage + 1]); }) } else if(pageData.page_number == 2 || pageData.page_number == 6) { currentPage--; if(pages[newPage]) setPage(pageL,pages[newPage]); else getDecryptedImage(mangaConfig.pages[newPage].locale[flashVars.locale].encrypted_composed_image_url).then(function(pageURL) { pages[newPage] = pageURL; setPage(pageL,pages[newPage]); }) } } function setPage(pageElement,image) { pageElement.src = image; } } } function authenticate(token,sessionId,seriesId) { return new Promise(function(resolve) { if(!token) resolve(null); else getJSON("http://api-manga.crunchyroll.com/cr_authenticate?session%5Fid=" + sessionId + "&api%5Fver=1&device%5Ftype=com%2Ecrunchyroll%2Emanga%2Ewww&format=json&series%5Fid=" + seriesId + "&auth=" + token + "&version=0").then(data => resolve(data.data)); }) } function parseFlashVars(vars) { var splitVars = vars.split("&"); var parsed = {}; for(var s = 0; s < splitVars.length; s++) { var split = splitVars[s].split("="); parsed[split[0]] = decodeURIComponent(split[1]); } return parsed; } function getDecryptedImage(imageURL) { return new Promise(function(resolve,reject) { var config = new XMLHttpRequest(); config.onload = function() { var buffer = this.response; var viewer = new DataView(buffer); for(var i = 0; i < buffer.byteLength; i++) { var originalByte = viewer.getUint8(i); viewer.setUint8(i,originalByte ^ 0x42); } resolve(URL.createObjectURL(new Blob([buffer], {type: "image/jpeg"}))); }; config.onerror = reject; config.open("GET", imageURL, true); config.responseType = "arraybuffer"; config.send(); }); } function getAdsConfig(configURL) { return new Promise(function(resolve,reject) { var config = new XMLHttpRequest(); config.onload = function() { resolve(this.response); }; config.onerror = reject; config.open("GET", configURL, true); config.responseType = "document"; config.send(); }); } function getJSON(configURL) { return new Promise(function(resolve,reject) { var jsonRequest = new XMLHttpRequest(); jsonRequest.onload = function() { resolve(this.response); }; jsonRequest.onerror = reject; jsonRequest.open("GET", configURL, true); jsonRequest.responseType = "json"; jsonRequest.send(); }); }