webnovel.com skip video ads

skip video ads

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name            webnovel.com skip video ads
// @description:en   skip video ads
// @namespace       http://forum.novelupdates.com/
// @version         4
// @run-at          document-end
// @match           http://webnovel.com/book/*
// @match           https://webnovel.com/book/*
// @match           http://www.webnovel.com/book/*
// @match           https://www.webnovel.com/book/*
// @description skip video ads
// ==/UserScript==

//------------------------------------------------------------------------------
// 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('.cha-content._lock')
  ).forEach((lock) => {
    // Remove this class so this chapter won't be processed the next time
    // `main` is called.
    lock.classList.remove('_lock');

    // Remove the video.
    const v = lock.closest('.chapter_content').querySelector('.lock-video');
    if (v) {
      v.remove();
    }

    // Element with the chapter content.
    const contentElement = lock.querySelector('.cha-words');

    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.querySelector('[data-cid]').dataset;

    // Both ID are required.
    if (!bid || !cid) {
      return;
    }

    return fetch(
      `https://www.webnovel.com/apiajax/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://www.webnovel.com/apiajax/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);