Chapter Downloader

Download up to 200 chapters on truyen.tangthuvien.vn

目前为 2025-04-21 提交的版本。查看 最新版本

// ==UserScript==
// @name         Chapter Downloader
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  Download up to 200 chapters on truyen.tangthuvien.vn
// @author       You
// @match        https://truyen.tangthuvien.vn/doc-truyen/*
// @match        https://truyen.tangthuvien.vn/doc-truyen/*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const MAX_CHAPTERS = 200;
    let downloadedCount = 0;

    const panel = document.createElement('div');
    panel.id = 'reader-panel';
    panel.style.cssText = `
        position: fixed;
        top: 10%;
        right: 10px;
        width: 300px;
        background-color: #f8f8f8;
        border: 1px solid #ccc;
        border-radius: 5px;
        padding: 10px;
        z-index: 9999;
        box-shadow: 0 0 10px rgba(0,0,0,0.2);
        font-family: Arial, sans-serif;
    `;

    const textarea = document.createElement('textarea');
    textarea.style.cssText = `width: 100%; height: 300px; margin-bottom: 10px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; resize: vertical;`;

    const startButton = document.createElement('button');
    startButton.textContent = 'Bắt đầu';
    startButton.style.cssText = `width: 100%; padding: 8px; margin-bottom: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;`;

    const getTextButton = document.createElement('button');
    getTextButton.textContent = 'Lấy Text';
    getTextButton.style.cssText = `flex: 1; padding: 8px; margin-right: 5px; background-color: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;`;

    const nextChapterButton = document.createElement('button');
    nextChapterButton.textContent = 'Chương Tiếp';
    nextChapterButton.style.cssText = `flex: 1; padding: 8px; margin-left: 5px; background-color: #ff9800; color: white; border: none; border-radius: 4px; cursor: pointer;`;

    const clearButton = document.createElement('button');
    clearButton.textContent = 'Xoá';
    clearButton.style.cssText = `flex: 1; padding: 8px; margin-right: 5px; background-color: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer;`;

    const copyButton = document.createElement('button');
    copyButton.textContent = 'Sao Chép';
    copyButton.style.cssText = `flex: 1; padding: 8px; margin-left: 5px; background-color: #9c27b0; color: white; border: none; border-radius: 4px; cursor: pointer;`;

    const buttonRow1 = document.createElement('div');
    buttonRow1.style.cssText = `display: flex; justify-content: space-between; margin-bottom: 10px;`;
    buttonRow1.appendChild(getTextButton);
    buttonRow1.appendChild(nextChapterButton);

    const buttonRow2 = document.createElement('div');
    buttonRow2.style.cssText = `display: flex; justify-content: space-between;`;
    buttonRow2.appendChild(clearButton);
    buttonRow2.appendChild(copyButton);

    panel.appendChild(textarea);
    panel.appendChild(startButton);
    panel.appendChild(buttonRow1);
    panel.appendChild(buttonRow2);
    document.body.appendChild(panel);

    const savedContent = localStorage.getItem('chapterDownloaderContent');
    if (savedContent) textarea.value = savedContent;

    function saveContent() {
        localStorage.setItem('chapterDownloaderContent', textarea.value);
    }
    textarea.addEventListener('input', saveContent);

    function extractChapterContent() {
        const titleElement = document.querySelector('h2');
        const contentElement = document.querySelector('.box-chap');
        if (titleElement && contentElement) {
            const title = titleElement.innerText.trim();
            const content = contentElement.innerText.trim();
            textarea.value += `\n\n------------\n\n${title}\n\n${content}`;
            saveContent();
            return true;
        }
        return false;
    }

    function goToNextChapter() {
        const next = document.querySelector('.bot-next_chap.bot-control') ||
                     [...document.querySelectorAll('a')].find(a => /tiếp|sau|next/i.test(a.textContent));
        if (next) {
            next.click();
            return true;
        }
        return false;
    }

    function waitForNextChapterLoaded(oldTitle) {
        return new Promise((resolve, reject) => {
            const interval = setInterval(() => {
                const newTitle = document.querySelector('h2')?.innerText.trim();
                if (newTitle && newTitle !== oldTitle) {
                    clearInterval(interval);
                    resolve();
                }
            }, 500);
            setTimeout(() => {
                clearInterval(interval);
                reject('Timeout');
            }, 10000);
        });
    }

    let isAutoDownloading = false;
    async function startAutoDownloading() {
        if (!isAutoDownloading) return;
        const titleEl = document.querySelector('h2');
        const oldTitle = titleEl?.innerText.trim();
        if (downloadedCount >= MAX_CHAPTERS) {
            isAutoDownloading = false;
            startButton.textContent = 'Bắt đầu';
            startButton.style.backgroundColor = '#4CAF50';
            return;
        }
        if (extractChapterContent()) {
            downloadedCount++;
            if (goToNextChapter()) {
                try {
                    await waitForNextChapterLoaded(oldTitle);
                    setTimeout(startAutoDownloading, 1000);
                } catch (err) {
                    isAutoDownloading = false;
                    startButton.textContent = 'Bắt đầu';
                    startButton.style.backgroundColor = '#4CAF50';
                }
            } else {
                isAutoDownloading = false;
                startButton.textContent = 'Bắt đầu';
                startButton.style.backgroundColor = '#4CAF50';
            }
        }
    }

    startButton.addEventListener('click', () => {
        isAutoDownloading = !isAutoDownloading;
        startButton.textContent = isAutoDownloading ? 'Dừng' : 'Bắt đầu';
        startButton.style.backgroundColor = isAutoDownloading ? '#f44336' : '#4CAF50';
        if (isAutoDownloading) startAutoDownloading();
    });

    getTextButton.addEventListener('click', extractChapterContent);
    nextChapterButton.addEventListener('click', goToNextChapter);
    clearButton.addEventListener('click', () => { textarea.value = ''; saveContent(); });
    copyButton.addEventListener('click', () => {
        textarea.select();
        document.execCommand('copy');
        const notification = document.createElement('div');
        notification.textContent = 'Đã sao chép!';
        notification.style.cssText = `position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #000a; color: white; padding: 10px 20px; border-radius: 4px; z-index: 10000;`;
        document.body.appendChild(notification);
        setTimeout(() => document.body.removeChild(notification), 2000);
    });
})();