Tải toàn bộ chương từ danh sách (Tangthuvien)

Tự động tải toàn bộ chương từ phần "Danh sách chương" trên Tangthuvien

当前为 2025-04-21 提交的版本,查看 最新版本

// ==UserScript==
// @name         Tải toàn bộ chương từ danh sách (Tangthuvien)
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Tự động tải toàn bộ chương từ phần "Danh sách chương" trên Tangthuvien
// @match        https://truyen.tangthuvien.vn/doc-truyen/*
// @grant        GM_xmlhttpRequest
// @connect      truyen.tangthuvien.vn
// ==/UserScript==

(function () {
    'use strict';

    if (!location.href.includes('/doc-truyen/')) return;

    // Giao diện
    const textarea = document.createElement('textarea');
    textarea.style.cssText = 'width:100%; height:300px; margin-bottom:10px; white-space: pre-wrap;';

    const container = document.createElement('div');
    container.style.cssText = `
        position: fixed; top: 10%; right: 10px; z-index: 9999;
        width: 400px; background: white; border: 1px solid #ccc;
        padding: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.3);
    `;

    const startBtn = document.createElement('button');
    startBtn.textContent = 'Bắt đầu tải';
    startBtn.style.cssText = 'width: 100%; padding: 8px; margin-bottom: 10px;';

    const copyBtn = document.createElement('button');
    copyBtn.textContent = 'Sao chép';
    copyBtn.style.cssText = 'width: 100%; padding: 8px;';

    container.appendChild(textarea);
    container.appendChild(startBtn);
    container.appendChild(copyBtn);
    document.body.appendChild(container);

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    function fetchChapter(url) {
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function (response) {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, 'text/html');
                    const title = doc.querySelector('h2')?.innerText.trim() || 'Không tiêu đề';
                    const content = doc.querySelector('.box-chap')?.innerText.trim() || 'Không có nội dung';
                    resolve(`\n\n------------\n\n${title}\n\n${content}`);
                },
                onerror: () => resolve(`\n\n[Không tải được chương: ${url}]`)
            });
        });
    }

    startBtn.addEventListener('click', async () => {
        startBtn.disabled = true;
        startBtn.textContent = 'Đang tải...';

        // Chỉ lấy từ khu vực danh sách chương
        const chapterSection = [...document.querySelectorAll('.story-detail > .list-chapter')];
        let links = [];

        if (chapterSection.length > 0) {
            links = [...chapterSection[0].querySelectorAll('a')].map(a => a.href);
        } else {
            // Dự phòng: lấy tất cả link từ vùng content chính chứa "chuong-"
            links = [...document.querySelectorAll('a')]
                .filter(a => a.href.includes('/chuong-') && a.closest('.list-chapter'));
        }

        // Sắp xếp theo số chương
        links = links
            .filter((v, i, a) => a.indexOf(v) === i) // remove duplicates
            .sort((a, b) => {
                const n1 = parseInt(a.match(/chuong-(\d+)/)?.[1] || 0);
                const n2 = parseInt(b.match(/chuong-(\d+)/)?.[1] || 0);
                return n1 - n2;
            });

        if (links.length === 0) {
            textarea.value = 'Không tìm thấy danh sách chương!';
            startBtn.disabled = false;
            startBtn.textContent = 'Bắt đầu tải';
            return;
        }

        for (const url of links) {
            textarea.value += `\nĐang tải: ${url}`;
            const data = await fetchChapter(url);
            textarea.value += data;
            await sleep(1500);
        }

        startBtn.textContent = 'Đã xong!';
        startBtn.disabled = false;
    });

    copyBtn.addEventListener('click', () => {
        textarea.select();
        document.execCommand('copy');
        alert('Đã sao chép nội dung vào clipboard!');
    });
})();