tsadult-dl

tsadult post saver

// ==UserScript==
// @name         tsadult-dl
// @namespace    http://tampermonkey.net/
// @version      0.3.1
// @description  tsadult post saver
// @author       You
// @match        https://*.tsadult.net/*/res/*.html
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tsadult.net
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.8.0/jszip.min.js
// @grant        GM_addStyle
// @license MIT
// ==/UserScript==


const CSS = `
  #dl-btn {
    position: fixed;
    bottom: 64px;
    right: 32px;
    font-size: 16pt;
    text-align: right;
  }
  #dl-msg {
   font-size: 12pt;
   color: black;
  }
`

function addDownloadButton() {
  var count = 0;
  const post = getPost();
  const imageTask = post.filter(it => it.image != null);

  const container = document.createElement('div');
  container.id = 'dl-btn';

  const message = document.createElement('div');
  message.id = 'dl-msg';
  const printMessage = (msg) => { message.innerText = msg };
  printMessage(`image: (0/${imageTask.length})`);

  const button = document.createElement('button');
  button.innerText = '💾';
  button.onclick = () => {
    if (downloading) {
      return;
    }
    var downloading = true;
    console.log('get post', post);

    var zip = new JSZip();
    var tasks = imageTask.map(it => {
        const name = it.image.name;
        const url = it.image.url;
        console.log('download image', it);
        return fetch(url)
          .then(resp => resp.blob())
          .then(blob => {
            zip.file(name, blob);
            count++;
            printMessage(`image: (${count}/${imageTask.length})`);
            return Promise.resolve();
          })
      });

    const article = post.map(it => it.content)
      .filter(it => it!=null && it.length > 0)
      .join('\n\r---\n\r');
    Promise.all(tasks)
      .then(() => zip.file('article.md', article))
      .then(() => zip.generateAsync({ type: 'blob' }))
      .then(blob => {
        const download = document.createElement('a');
        document.body.appendChild(download);
        download.style.display = 'none';

        const fileUrl = window.URL.createObjectURL(blob);
        download.href = fileUrl;
        download.download = 'tsadult_' + new Date().getTime();
        download.click();
        window.URL.revokeObjectURL(fileUrl);
        download.remove();
        downloading = false;
      })
  }
  container.appendChild(message);
  container.appendChild(button);
  return container;
}

const parser = {
  old: function () {
    const convertTo = function (fragment) {
      const message = fragment.querySelector('.message');
      var content = message?.innerText?.replace(/^#/gm, '> ');

      var image = null;
      const img = fragment.querySelector('img');
      if (img) {
        const url = new URL(img.parentElement.href, location.href).href.toString();
        const suffixIndex = url.lastIndexOf('/');
        image = { url, name: url.slice(suffixIndex + 1) }
        content = `![](${image.name})\n\n` + content;
      }

      return { image, content }
    };
    const post = document.querySelector('.op');
    const tables = document.querySelectorAll('#posts > table');
    const contents = Array.from(tables).map(it => convertTo(it));
    return [convertTo(post), ...contents]
  },
  v2024: function () {
    const convertTo = function (fragment) {
      const imagesEle = fragment.querySelectorAll('.files .fileinfo > a');

      const images = Array.from(imagesEle)
        .map(e => {
          const url = new URL(e.href, location.href).href.toString();
          const name = e.innerText;

          const image = { url, name };
          const content = `![](${name})\n\n`;
          return { image, content }
        });

      const text = fragment.querySelector('.body')?.innerText?.replace(/^#/gm, '> ') ?? '';
      return [...images, { image: null, content: text }]
    };

    const imagesEle = document.querySelectorAll('.thread > .files .fileinfo > a');
    const images = Array.from(imagesEle)
      .map(e => {
        const url = new URL(e.href, location.href).href.toString();
        const name = e.innerText;

        const image = { url, name };
        const content = `![](${name})`;
        return { image, content }
      });
    const posts = Array.from(document.querySelectorAll('.thread > .post'))
      .map(e => convertTo(e))
      .flat();

    return [...images, ...posts]
  }
};


function getPost() {
  const url = location.host;
  if (url.includes('2021')) {
    return parser.old()
  } else {
    return parser.v2024()
  }
}

(function () {
  'use strict';
  GM_addStyle(CSS);
  const button = addDownloadButton();
  document.body.appendChild(button);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址