Chapter Downloader

Tự động lấy nội dung từ chương hiện tại đến chương cuối cùng trên truyen.tangthuvien.vn

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

// ==UserScript==
// @name         Chapter Downloader
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Tự động lấy nội dung từ chương hiện tại đến chương cuối cùng trên truyen.tangthuvien.vn
// @author       You
// @match        https://truyen.tangthuvien.vn/doc-truyen/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let chapterUrls = [];
    let currentChapterIndex = 0;
    let isAutoDownloading = false;

    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.id = 'content-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 buttonRow1 = document.createElement('div');
    buttonRow1.style.cssText = `display: flex; justify-content: space-between; margin-bottom: 10px;`;

    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 buttonRow2 = document.createElement('div');
    buttonRow2.style.cssText = `display: flex; justify-content: space-between;`;

    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;
    `;

    buttonRow1.appendChild(getTextButton);
    buttonRow1.appendChild(nextChapterButton);
    buttonRow2.appendChild(clearButton);
    buttonRow2.appendChild(copyButton);
    panel.appendChild(textarea);
    panel.appendChild(startButton);
    panel.appendChild(buttonRow1);
    panel.appendChild(buttonRow2);

    const shortcutsInfo = document.createElement('div');
    shortcutsInfo.style.cssText = `font-size: 11px; color: #666; margin-top: 10px; padding-top: 5px; border-top: 1px solid #ddd;`;
    shortcutsInfo.innerHTML = `<b>Phím tắt:</b> Alt+G (Lấy text), Alt+N (Chương tiếp), Alt+S (Tự động), Alt+C (Sao chép)`;
    panel.appendChild(shortcutsInfo);
    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();

            if (textarea.value) {
                textarea.value += '\n\n------------\n\n' + title + '\n\n' + content;
            } else {
                textarea.value = title + '\n\n' + content;
            }

            saveContent();
            return true;
        } else {
            return false;
        }
    }

    function startAutoDownloading() {
        if (!isAutoDownloading) return;

        if (chapterUrls.length === 0) {
            const allLinks = Array.from(document.querySelectorAll('a[href*="/doc-truyen/"]'));
            const filteredLinks = allLinks.map(a => a.href).filter((href, index, self) => href.includes('chuong') && self.indexOf(href) === index);
            chapterUrls = filteredLinks;

            const currentUrl = window.location.href.split('?')[0];
            currentChapterIndex = chapterUrls.findIndex(url => url === currentUrl);

            if (currentChapterIndex === -1) {
                console.log('Không tìm thấy chương hiện tại trong danh sách!');
                isAutoDownloading = false;
                return;
            }
        }

        if (currentChapterIndex < chapterUrls.length) {
            const currentUrl = chapterUrls[currentChapterIndex];
            console.log(`Đang tải chương ${currentChapterIndex + 1}/${chapterUrls.length}: ${currentUrl}`);

            if (window.location.href !== currentUrl) {
                window.location.href = currentUrl;
                return;
            }

            if (extractChapterContent()) {
                currentChapterIndex++;
                setTimeout(startAutoDownloading, 2000);
            } else {
                console.log('Không lấy được nội dung chương, dừng lại.');
                isAutoDownloading = false;
            }
        } else {
            console.log('Đã tải xong tất cả chương!');
            isAutoDownloading = false;
            startButton.textContent = 'Bắt đầu';
            startButton.style.backgroundColor = '#4CAF50';
        }
    }

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

    getTextButton.addEventListener('click', () => extractChapterContent());
    nextChapterButton.addEventListener('click', () => {
        const nextLink = document.querySelector('.bot-next_chap.bot-control');
        if (nextLink) nextLink.click();
    });
    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-color: rgba(0,0,0,0.8);
            color: white;
            padding: 10px 20px;
            border-radius: 4px;
            z-index: 10000;
        `;
        document.body.appendChild(notification);
        setTimeout(() => document.body.removeChild(notification), 2000);
    });

    document.addEventListener('keydown', (e) => {
        if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT') {
            if (e.altKey && e.key === 'g') {
                extractChapterContent();
                e.preventDefault();
            } else if (e.altKey && e.key === 'n') {
                const nextLink = document.querySelector('.bot-next_chap.bot-control');
                if (nextLink) nextLink.click();
                e.preventDefault();
            } else if (e.altKey && e.key === 's') {
                startButton.click();
                e.preventDefault();
            } else if (e.altKey && e.key === 'c') {
                textarea.select();
                document.execCommand('copy');
                e.preventDefault();
            }
        }
    });
})();