sexstories.com stories downloader and searcher

from any author's profile on sexstories.com, can select stories to download in a zip or as a series in one aggregated .html file, can also search stories and display frequency of search terms for each story

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         sexstories.com stories downloader and searcher
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  from any author's profile on sexstories.com, can select stories to download in a zip or as a series in one aggregated .html file, can also search stories and display frequency of search terms for each story
// @author       DUVish
// @match        https://www.sexstories.com/profile*/*
// @grant        none
// ==/UserScript==

let totalScripts = 3;
let scriptsLoaded = 0;

let scriptTag = document.createElement("script");
scriptTag.src = "https://code.jquery.com/jquery-3.3.1.min.js";
scriptTag.async = false;
scriptTag.type = "text/javascript";
scriptTag.onload = function() {
    scriptsLoaded++;
    //changing title of page
    document.querySelector("title").innerText = document.querySelector("h3").children[0].innerText + "'s profile on sexstories.com";

    //adding button for asyncStatus and search input itself
    let asyncStatus = true;
    document.getElementsByTagName("h3")[1].innerHTML += "<input class='searchStories' placeholder='Search stories'></input><button class='syncButton' title='Switch synchronicity status'>Asynchronous</button><button class='reset'>Reset Search Results</button>";
    document.querySelector("head").innerHTML += `<style>
      .searchStories {
        margin-left: 10px;
      }
      .syncButton {
        margin-left: 10px;
      }
      .tableDataRow {
        font-size: 11px;
        margin-left: 5px;
        margin-right: 5px;
      }
    </style>`;

    $(".syncButton").on("click", function(e) {
      //console.log("async status", asyncStatus);
      asyncStatus = !asyncStatus;
      let status = asyncStatus ? "Asynchronous" : "Synchronous";
      $(".syncButton")[0].innerText = status;
    });

    $(".reset").on("click", function(e) {
      Array.from($(".tableDataRow")).forEach(el => el.remove());
    });

    $(".searchStories").on("keydown", function(e) {
      if (e.key === "Enter") {
        let searchTerm = $(".searchStories").val();
        //console.log("starting event with search term and asyncStatus", searchTerm, asyncStatus);
        Array.prototype.forEach.call(document.getElementsByTagName("tr"), function(node, i) {
          if (i === 0) return;
          let link = node.children[0].children[0].href;
          $.ajax({
            type: "GET",
            async: asyncStatus,
            url: link,
            success: function(data) {
              //console.log("success in ajax call");
              let reg = new RegExp(searchTerm, "gi");
              let idx = $(data).find(".block_panel").length === 2 ? 0 : 1;
              let wordCount = $(data).find(".block_panel")[idx].innerText.match(/\S\s\S/g).length;
              let searchCount = $(data).find(".block_panel")[idx].innerText.match(reg).length;
              //console.log("reg, wordCount, and searchCount", reg, wordCount, searchCount);
              if (node.children.length === 5) node.innerHTML += `<td class='tableDataRow'>${wordCount} words</td>`;
              node.children[0].innerHTML += `<span class='tableDataRow'>(${searchCount} results for ${searchTerm})</span>`;
            },
            error: function(error) {
              //console.log("error in ajax call", error);
            }
          });
        });
      }
    });
};
document.querySelector("head").appendChild(scriptTag);

let zipScr = document.createElement("script");
zipScr.src = "https://cdn.jsdelivr.net/gh/Stuk/jszip/dist/jszip.min.js";
zipScr.async = false;
zipScr.type = "text/javascript";
zipScr.onload = function() {
  scriptsLoaded++;
  if (scriptsLoaded === totalScripts) scriptFunc();
};
document.querySelector("head").appendChild(zipScr);

let fileScr = document.createElement("script");
fileScr.src = "https://cdn.jsdelivr.net/gh/eligrey/FileSaver.js/src/FileSaver.js";
fileScr.async = false;
fileScr.type = "text/javascript";
fileScr.onload = function() {
  scriptsLoaded++;
  if (scriptsLoaded === totalScripts) scriptFunc();
};
document.querySelector("head").appendChild(fileScr);

let currentNumberGlobal = 1;
const author = document.querySelector(".notice").innerText.match(/[^\n]+/)[0];

document.querySelector("head").innerHTML += `
<style>
  .checkOrder {
    width: 30px
  }
  .dlTogether {
    margin-left: 60px;
  }
  .dlSeperately {
    margin-left: 4px;
  }
  .resetSelections {
    margin-left: 6px;
    margin-right: 60px
  }
</style>
`;

function scriptFunc() {
  Array.from(document.querySelector("tbody").children).forEach(el => el.innerHTML += `<div style="display: flex; position: relative; top: 10px;"><input class="checkOrder" type="checkbox"><span class="selectionorder"></span></div>`);

  Array.from(document.getElementsByClassName("checkOrder")).forEach(el => el.addEventListener("click", function(e) {
      if (e.target.checked) e.target.parentNode.children[1].innerText = currentNumberGlobal++;
      else e.preventDefault();
  }));

  document.querySelectorAll(".notice")[1].innerHTML += `<button class="dlTogether">Download Selected chapters as one story (in selection order)</button> <button class="dlSeparately">Download selected stories individually</button> <button class="resetSelections">Reset Selections</button>`;

  //console.log("current dom nodes", document.querySelector(".resetSelections"), document.querySelector(".dlSeparately"));

  document.getElementsByClassName("resetSelections")[0].addEventListener("click", function(e) {
    Array.from(document.getElementsByClassName("checkOrder")).forEach(el => {
      el.checked = false;
      el.parentNode.children[1].innerText = "";
      currentNumberGlobal = 1;
    });
  });

  document.getElementsByClassName("dlSeparately")[0].addEventListener("click", function(e) {
    //console.log("regostered downloading all separately");
    var zip = new JSZip();
    Array.from(document.querySelectorAll(".checkOrder:checked")).forEach(checkedBox => {
      let link = checkedBox.parentNode.parentNode.children[0].children[0].href;
      let htmlFile = "";
      let bodyStr = "";
      let descStr = "";
      let titleStr = "";
      let tagsStr = "";
      $.ajax({
        type: "GET",
        url: link,
        async: false,
        success: (data) => {
          bodyStr = $(data).find(".block_panel")[1].innerHTML.replace(/<!-- VOTES -->[\s\S]+>/, "").trim();
          descStr = $(data).find(".block_panel")[0].innerText.trim().match(/Introduction:[\s\n]+(.+)/i)[1];
          titleStr = $($(data).find(".story_info")[0]).find("h2")[0].innerText.trim().match(/[^\n]+/)[0].trim();
          tagsStr = $($(data).find(".story_info")[0]).find(".top_info")[0].innerText.trim();
        }
      });
      htmlFile = `
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${titleStr}</title>
</head>
<body>
<div style="text-align:center;font-size: 22px;">
${titleStr}
</div>
<br>
<div style="text-align:center;font-size: 18px;font-style: italic">
${descStr}
</div>
<br>
<div style="text-align:center;font-size: 12px;">
${tagsStr}
</div>
<br>
<br>
${bodyStr}
</body>
`;
      zip.file(titleStr + ".html", htmlFile);
    });
    zip.generateAsync({type:"blob"})
        .then(function (blob) {
        saveAs(blob, `${author}'s Stories`);
    });
  });

    document.getElementsByClassName("dlTogether")[0].addEventListener("click", function(e) {
      let htmlDoc = "";
      let bodyStr = "";
      let lastTitle = "";
      let firstTitle = "";
      let anchorsStr = "";
      Array.from(document.querySelectorAll(".checkOrder:checked")).sort((a, b) => Number(a.parentNode.children[1].innerText) - Number(b.parentNode.children[1].innerText)).forEach((el, idx) => {
        let link = el.parentNode.parentNode.children[0].children[0].href;
        $.ajax({
          type: "GET",
          url: link,
          async: false,
          success: (data) => {
            localBodyStr = $(data).find(".block_panel")[1].innerHTML.replace(/<!-- VOTES -->[\s\S]+>/, "").trim();
            localDescStr = $(data).find(".block_panel")[0].innerText.trim().match(/Introduction:[\s\n]+(.+)/i)[1];
            localTitleStr = $($(data).find(".story_info")[0]).find("h2")[0].innerText.trim().match(/[^\n]+/)[0].trim();
            localTagsStr = $($(data).find(".story_info")[0]).find(".top_info")[0].innerText.trim();
            if (idx === 0) firstTitle = localTitleStr;
            anchorsStr += `<a href="#${idx}" style="text-align:center;">${localTitleStr}</a><br>`;
            bodyStr += `
<div style="text-align:center;font-size: 22px;" id="${idx}">
${localTitleStr}
</div>
<br>
<div style="text-align:center;font-size: 18px;font-style: italic;">
${localDescStr}
</div>
<br>
<div style="text-align:center;font-size: 12px;">
${localTagsStr}
</div>
<br>
<br>
${localBodyStr}
<br>
<br>
`;
            lastTitle = localTitleStr;
          }
        });
      });
      htmlDoc = `
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${firstTitle} - ${lastTitle}</title>
</head>
<body>
<div style="text-align:center;font-size: 24px;">Table of Contents</div>
<br>
<div style="font-size: 15px; width: 100%; text-align: center; line-height: 1.6">${anchorsStr}</div>
<br>
<br>
${bodyStr}
</body>
`;
      let blob = new Blob([htmlDoc], {type: "text/plain;charset=utf8"});
      saveAs(blob, `${firstTitle} - ${lastTitle} (${currentNumberGlobal - 1} Chapters).html`);
    });
}