NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name webnovel.com skip video ads mobile // @namespace https://openuserjs.org/scripts/shadofx // @version 4 // @run-at document-end // @match http://m.webnovel.com/book/* // @match https://m.webnovel.com/book/* // @license MIT // ==/UserScript== // ==OpenUserJS== // @author shadofx // ==/OpenUserJS== //------------------------------------------------------------------------------ // This script is released to the public domain. No attribution is required. //------------------------------------------------------------------------------ // How frequently this script should check for new chapters. // // The amount is in milliseconds. const INTERVAL_CHAPTER_CHECK = 1000; // When a token is not ready yet, this is how much time we should wait // before trying again. // // The amount is in milliseconds. const INTERVAL_TOKEN_CHECK = 1000; /** * Check for new chapters and try to remove the adwall from them. */ function main() { Array.from( // Locked chapters. document.querySelectorAll('.j_chapterWrapper') ).filter((item)=>item.querySelector('.cha-txt').classList.contains('cha-txt-hide')) .forEach((lock) => { // Element with the chapter content. const contentElement = lock.querySelector('.cha-txt'); // Remove this class so this chapter won't be processed the next time // `main` is called. contentElement.classList.remove('cha-txt-hide'); // Remove the video. const v = lock.querySelector('.cha-watch-ad'); if(v) v.remove(); contentElement.style.opacity = '0.1'; // Get the ID for the series ("book"). // // Some chapters have the `data-bid` property, but not all of them. // That's why it's better to just get this from the URL. const bid = window.location.href.split('/book/')[1].split('/')[0]; // Get the ID for the chapter. const cid = lock.dataset.chapterid; // Both ID are required. if (!bid || !cid) { return; } return fetch( `https://m.webnovel.com/ajax/chapter/GetChapterContentToken?bookId=${bid}&chapterId=${cid}` ) .then(resp => resp.json()) .then(data => data.data.token) .then(token => encodeURIComponent(token)) .then(token => new Promise((resolve) => { // The raw body of the chapter. // // It will be plain text, so we must manually build the HTML for it. let content = ''; // Try to get the content of the chapter, and fulfill the promise once // we have it. // // This function will retry until it succeeds. function tick() { const url = `https://m.webnovel.com/ajax/chapter/GetChapterContentByToken?token=${token}`; fetch(url) .then(resp => resp.json()) .then((data) => { content = data.data.content.trim(); if (content) { resolve(content); } else { setTimeout(tick, INTERVAL_TOKEN_CHECK); } }) .catch((err) => { console.error(err.stack); tick(); }); } tick(); })) .then((content) => { // Build the HTML for the chapter content. // // For now we only split on line breaks and wrap each piece // with "<p></p>" tags. const chapterHtml = content .split('\n') .map(p => p.trim()) .filter(p => !!p) .map(p => `<p>${p}</p>`) .join(''); // Update the chapter content and turn opacity back to 100%. contentElement.innerHTML = chapterHtml; contentElement.style.opacity = '1'; }) .catch((err) => { console.error(err.stack); }); }); } // Since Qidian may load new chapters without refreshing the page, we must // continuously check for new chapters in the page. setInterval(main, INTERVAL_CHAPTER_CHECK);