Chapter Downloader

Add a control panel for novel reading on truyen.tangthuvien.net, auto download max 200 chapters

目前為 2025-04-21 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Chapter Downloader
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  Add a control panel for novel reading on truyen.tangthuvien.net, auto download max 200 chapters
// @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 isAutoDownloading = false;
    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.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 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;
        }
        return false;
    }

    function goToNextChapter() {
        const nextChapterLink = document.querySelector('.bot-next_chap.bot-control');
        if (nextChapterLink) {
            nextChapterLink.click();
            return true;
        } else {
            const alternativeNextLinks = document.querySelectorAll('a[href*="chuong"]');
            for (const link of alternativeNextLinks) {
                if (link.textContent.includes('tiếp') || link.textContent.includes('sau') || link.textContent.toLowerCase().includes('next')) {
                    link.click();
                    return true;
                }
            }
            return false;
        }
    }

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

        console.log(`Auto-downloading: Extracting chapter ${downloadedCount + 1}`);

        if (downloadedCount >= MAX_CHAPTERS) {
            console.log(`Auto-downloading: Đã tải ${MAX_CHAPTERS} chương, dừng.`);
            isAutoDownloading = false;
            startButton.textContent = 'Bắt đầu';
            startButton.style.backgroundColor = '#4CAF50';
            return;
        }

        if (extractChapterContent()) {
            downloadedCount++;

            setTimeout(() => {
                if (!isAutoDownloading) return;
                if (goToNextChapter()) {
                    setTimeout(() => {
                        if (isAutoDownloading) {
                            startAutoDownloading();
                        }
                    }, 2000);
                } else {
                    isAutoDownloading = false;
                    startButton.textContent = 'Bắt đầu';
                    startButton.style.backgroundColor = '#4CAF50';
                }
            }, 1000);
        } else {
            isAutoDownloading = false;
            startButton.textContent = 'Bắt đầu';
            startButton.style.backgroundColor = '#4CAF50';
        }
    }

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

    getTextButton.addEventListener('click', () => {
        if (!isAutoDownloading) {
            downloadedCount = 0;
            isAutoDownloading = true;
            startButton.textContent = 'Dừng';
            startButton.style.backgroundColor = '#f44336';
            startAutoDownloading();
        }
    });

    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-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);
    });

    let isDragging = false;
    let offsetX, offsetY;

    panel.addEventListener('mousedown', (e) => {
        if (e.target === panel) {
            isDragging = true;
            offsetX = e.clientX - panel.getBoundingClientRect().left;
            offsetY = e.clientY - panel.getBoundingClientRect().top;
        }
    });

    document.addEventListener('mousemove', (e) => {
        if (isDragging) {
            panel.style.left = (e.clientX - offsetX) + 'px';
            panel.style.top = (e.clientY - offsetY) + 'px';
            panel.style.right = 'auto';
        }
    });

    document.addEventListener('mouseup', () => {
        isDragging = false;
    });

    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);
})();