Chapter Downloader

Tự động trích xuất nội dung truyện từ truyen.tangthuvien.net

2025-03-09 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

// ==UserScript==
// @name         Chapter Downloader
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Tự động trích xuất nội dung truyện từ truyen.tangthuvien.net
// @author       TangThuVienExtractor
// @match        https://truyen.tangthuvien.net/doc-truyen/*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Biến toàn cục để theo dõi trạng thái trích xuất tự động
    let isAutomaticExtraction = localStorage.getItem('isAutoExtractionActive') === 'true';
    
    // Biến đếm số chương đã trích xuất
    let chaptersExtracted = parseInt(localStorage.getItem('chaptersExtracted') || '0');

    // Tạo panel ở bên phải trang
    function createPanel() {
        // Tạo container chính cho panel
        const panel = document.createElement('div');
        panel.style.position = 'fixed';
        panel.style.top = '100px';
        panel.style.right = '20px';
        panel.style.width = '300px';
        panel.style.backgroundColor = '#f9f9f9';
        panel.style.border = '1px solid #ddd';
        panel.style.borderRadius = '5px';
        panel.style.padding = '10px';
        panel.style.zIndex = '9999';
        panel.style.display = 'flex';
        panel.style.flexDirection = 'column';
        panel.style.gap = '10px';
        panel.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';

        // Tạo tiêu đề panel
        const title = document.createElement('h3');
        title.textContent = 'Trích xuất nội dung truyện';
        title.style.margin = '0 0 10px 0';
        title.style.textAlign = 'center';
        title.style.borderBottom = '1px solid #ddd';
        title.style.paddingBottom = '10px';
        panel.appendChild(title);

        // Tạo textarea để hiển thị nội dung đã trích xuất
        const textarea = document.createElement('textarea');
        textarea.style.width = '100%';
        textarea.style.height = '200px';
        textarea.style.padding = '8px';
        textarea.style.borderRadius = '3px';
        textarea.style.border = '1px solid #ddd';
        textarea.style.marginBottom = '10px';
        textarea.style.resize = 'vertical';
        panel.appendChild(textarea);

        // Lấy nội dung đã lưu từ localStorage (nếu có)
        const savedContent = localStorage.getItem('tangthuvien_extracted_content');
        if (savedContent) {
            textarea.value = savedContent;
        }

        // Tạo container cho các nút
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.flexDirection = 'column';
        buttonContainer.style.gap = '5px';
        panel.appendChild(buttonContainer);

        // Tạo nút bắt đầu trích xuất
        const startButton = document.createElement('button');
        startButton.textContent = 'Bắt đầu lấy text';
        startButton.style.padding = '8px';
        startButton.style.backgroundColor = '#4caf50';
        startButton.style.color = 'white';
        startButton.style.border = 'none';
        startButton.style.borderRadius = '3px';
        startButton.style.cursor = 'pointer';
        startButton.addEventListener('click', function() {
            if (!isAutomaticExtraction) {
                isAutomaticExtraction = true;
                localStorage.setItem('isAutoExtractionActive', 'true');
                startButton.textContent = 'Đang trích xuất...';
                startButton.style.backgroundColor = '#ff9800';
                
                // Đặt lại số chương khi bắt đầu lại quá trình
                if (!confirm("Tiếp tục đếm số chương đã trích xuất?")) {
                    chaptersExtracted = 0;
                    localStorage.setItem('chaptersExtracted', '0');
                }
                
                // Bắt đầu quá trình trích xuất tự động
                autoExtractAndNavigate();
            } else {
                isAutomaticExtraction = false;
                localStorage.setItem('isAutoExtractionActive', 'false');
                startButton.textContent = 'Bắt đầu lấy text';
                startButton.style.backgroundColor = '#4caf50';
            }
        });
        buttonContainer.appendChild(startButton);

        // Tạo nút xóa nội dung
        const clearButton = document.createElement('button');
        clearButton.textContent = 'Xóa nội dung';
        clearButton.style.padding = '8px';
        clearButton.style.backgroundColor = '#f44336';
        clearButton.style.color = 'white';
        clearButton.style.border = 'none';
        clearButton.style.borderRadius = '3px';
        clearButton.style.cursor = 'pointer';
        clearButton.addEventListener('click', function() {
            textarea.value = '';
            localStorage.removeItem('tangthuvien_extracted_content');
            
            // Hiển thị thông báo
            const notification = document.createElement('div');
            notification.textContent = 'Đã xóa nội dung';
            notification.style.position = 'fixed';
            notification.style.bottom = '20px';
            notification.style.right = '20px';
            notification.style.backgroundColor = '#f44336';
            notification.style.color = 'white';
            notification.style.padding = '10px';
            notification.style.borderRadius = '5px';
            notification.style.zIndex = '10000';
            document.body.appendChild(notification);
            
            // Xóa thông báo sau 3 giây
            setTimeout(() => {
                document.body.removeChild(notification);
            }, 3000);
        });
        buttonContainer.appendChild(clearButton);

        // Tạo nút sao chép nội dung
        const copyButton = document.createElement('button');
        copyButton.textContent = 'Sao chép nội dung';
        copyButton.style.padding = '8px';
        copyButton.style.backgroundColor = '#2196f3';
        copyButton.style.color = 'white';
        copyButton.style.border = 'none';
        copyButton.style.borderRadius = '3px';
        copyButton.style.cursor = 'pointer';
        copyButton.addEventListener('click', function() {
            textarea.select();
            document.execCommand('copy');
            
            // Hiển thị thông báo
            const notification = document.createElement('div');
            notification.textContent = 'Đã sao chép nội dung';
            notification.style.position = 'fixed';
            notification.style.bottom = '20px';
            notification.style.right = '20px';
            notification.style.backgroundColor = '#2196f3';
            notification.style.color = 'white';
            notification.style.padding = '10px';
            notification.style.borderRadius = '5px';
            notification.style.zIndex = '10000';
            document.body.appendChild(notification);
            
            // Xóa thông báo sau 3 giây
            setTimeout(() => {
                document.body.removeChild(notification);
            }, 3000);
        });
        buttonContainer.appendChild(copyButton);

        // Thêm panel vào body
        document.body.appendChild(panel);
        
        // Trả về các phần tử để sử dụng sau này
        return {
            panel,
            textarea,
            startButton
        };
    }

    // Hàm trích xuất nội dung chương hiện tại
    function extractCurrentChapterContent() {
        const panelElements = document.querySelector('textarea');
        const startButton = document.querySelector('button');
        
        if (!panelElements || !startButton) {
            console.error('Không tìm thấy panel hoặc textarea');
            return false;
        }
        
        // Lấy tiêu đề chương
        const chapterTitle = document.querySelector('h2');
        if (!chapterTitle) {
            console.error('Không tìm thấy tiêu đề chương');
            return false;
        }
        
        // Lấy nội dung chương
        const chapterContentSelector = '.box-chap';
        const chapterContent = document.querySelector(chapterContentSelector);
        if (!chapterContent) {
            console.error('Không tìm thấy nội dung chương');
            return false;
        }
        
        // Xử lý nội dung
        let contentText = chapterContent.innerText || chapterContent.textContent;
        contentText = contentText.trim();
        
        // Thêm nội dung vào textarea
        let currentContent = panelElements.value || '';
        
        // Thêm dấu phân cách nếu đã có nội dung trước đó
        if (currentContent !== '') {
            currentContent += '\n\n------------\n\n';
        }
        
        // Thêm tiêu đề và nội dung
        currentContent += chapterTitle.innerText + '\n\n' + contentText;
        
        // Cập nhật textarea và lưu vào localStorage
        panelElements.value = currentContent;
        localStorage.setItem('tangthuvien_extracted_content', currentContent);
        
        // Tăng số chương đã trích xuất và lưu vào localStorage
        chaptersExtracted++;
        localStorage.setItem('chaptersExtracted', chaptersExtracted);
        
        // Hiển thị thông báo
        const notification = document.createElement('div');
        notification.textContent = `Đã trích xuất: ${chapterTitle.innerText} (Chương ${chaptersExtracted})`;
        notification.style.position = 'fixed';
        notification.style.bottom = '20px';
        notification.style.right = '320px';
        notification.style.backgroundColor = '#4caf50';
        notification.style.color = 'white';
        notification.style.padding = '10px';
        notification.style.borderRadius = '5px';
        notification.style.zIndex = '10000';
        document.body.appendChild(notification);
        
        // Xóa thông báo sau 3 giây
        setTimeout(() => {
            document.body.removeChild(notification);
        }, 3000);
        
        return true;
    }

    // Biến đếm số lần thử trích xuất
    let extractionAttempts = 0;
    
    // Hàm tự động trích xuất và chuyển trang
    function autoExtractAndNavigate() {
        if (!isAutomaticExtraction) {
            return;
        }
        
        // Đặt lại biến đếm nếu quá trình tiếp tục
        extractionAttempts = 0;
        
        // Trích xuất nội dung chương hiện tại
        const extractionSuccess = extractCurrentChapterContent();
        
        if (extractionSuccess) {
            // Tìm nút chuyển đến chương tiếp theo
            const nextButton = document.querySelector('.bot-next_chap.bot-control');
            
            // Kiểm tra xem có thông báo "Bạn đã đọc đến chương mới nhất" hay không
            const pageContent = document.body.innerText || document.body.textContent;
            if (pageContent && pageContent.includes('Bạn đã đọc đến chương mới nhất')) {
                // Kết thúc quá trình khi thấy thông báo này
                console.log("Đã phát hiện thông báo: Bạn đã đọc đến chương mới nhất");
                isAutomaticExtraction = false;
                const startButton = document.querySelector('button');
                if (startButton) {
                    startButton.textContent = 'Bắt đầu lấy text';
                    startButton.style.backgroundColor = '#4caf50';
                }
                
                // Hiển thị thông báo hoàn thành
                const notification = document.createElement('div');
                notification.textContent = 'Đã hoàn thành trích xuất toàn bộ truyện!';
                notification.style.position = 'fixed';
                notification.style.bottom = '20px';
                notification.style.right = '320px';
                notification.style.backgroundColor = '#4caf50';
                notification.style.color = 'white';
                notification.style.padding = '10px';
                notification.style.borderRadius = '5px';
                notification.style.zIndex = '10000';
                document.body.appendChild(notification);
                
                // Xóa thông báo sau 5 giây
                setTimeout(() => {
                    document.body.removeChild(notification);
                }, 5000);
                return;
            }
            
            if (nextButton) {
                // Hiển thị thông báo
                const notification = document.createElement('div');
                notification.textContent = 'Đang chuyển đến chương tiếp theo...';
                notification.style.position = 'fixed';
                notification.style.bottom = '20px';
                notification.style.right = '320px';
                notification.style.backgroundColor = '#ff9800';
                notification.style.color = 'white';
                notification.style.padding = '10px';
                notification.style.borderRadius = '5px';
                notification.style.zIndex = '10000';
                document.body.appendChild(notification);
                
                // Xóa thông báo sau 2 giây
                setTimeout(() => {
                    document.body.removeChild(notification);
                }, 1000);
                
                // Nhấp vào nút chuyển trang
                nextButton.click();
                
                // Đợi 2 giây để trang mới tải xong, sau đó tiếp tục quá trình
                setTimeout(() => {
                    console.log("Trang mới đã được tải, tiếp tục quá trình...");
                    
                    // Đánh dấu rõ ràng rằng quá trình tự động vẫn đang diễn ra
                    window.localStorage.setItem('isAutoExtractionActive', 'true');
                    
                    // Tạo một MutationObserver để theo dõi khi DOM đã tải xong
                    const observer = new MutationObserver((mutations, obs) => {
                        // Kiểm tra xem trang đã tải xong chưa
                        const content = document.querySelector('.box-chap');
                        const title = document.querySelector('h2');
                        
                        if (content && title) {
                            obs.disconnect(); // Ngừng quan sát
                            console.log("Trang mới đã tải xong nội dung");
                            
                            // Đảm bảo biến isAutomaticExtraction vẫn là true để tiếp tục quá trình
                            isAutomaticExtraction = true;
                            
                            // Kiểm tra xem nút có bị ẩn hay không
                            const startButton = document.querySelector('button');
                            if (startButton) {
                                startButton.textContent = 'Đang trích xuất...';
                                startButton.style.backgroundColor = '#ff9800';
                            }
                            
                            // Hiển thị thông báo
                            const notification = document.createElement('div');
                            notification.textContent = 'Đang trích xuất chương mới...';
                            notification.style.position = 'fixed';
                            notification.style.bottom = '20px';
                            notification.style.right = '320px';
                            notification.style.backgroundColor = '#4CAF50';
                            notification.style.color = 'white';
                            notification.style.padding = '10px';
                            notification.style.borderRadius = '5px';
                            notification.style.zIndex = '10000';
                            document.body.appendChild(notification);
                            
                            // Xóa thông báo sau 2 giây và tiếp tục quy trình
                            setTimeout(() => {
                                try {
                                    document.body.removeChild(notification);
                                } catch (error) {
                                    console.log("Thông báo đã bị xóa");
                                }
                                
                                // Gọi lại hàm trích xuất để tiếp tục quá trình
                                window.setTimeout(function() {
                                    autoExtractAndNavigate();
                                }, 500);
                            }, 2000);
                        }
                    });
                    
                    // Bắt đầu quan sát thay đổi trên toàn bộ tài liệu
                    observer.observe(document, { 
                        childList: true, 
                        subtree: true 
                    });
                    
                    // Thiết lập timeout để dừng observer nếu trang không tải trong 10 giây
                    setTimeout(() => {
                        if (observer) {
                            observer.disconnect();
                            console.log("Dừng observer sau 10 giây");
                            
                            // Thử gọi trực tiếp
                            isAutomaticExtraction = true;
                            autoExtractAndNavigate();
                        }
                    }, 10000);
                }, 3000);
            } else {
                // Nếu không tìm thấy nút chuyển trang
                isAutomaticExtraction = false;
                const startButton = document.querySelector('button');
                if (startButton) {
                    startButton.textContent = 'Bắt đầu lấy text';
                    startButton.style.backgroundColor = '#4caf50';
                }
                
                // Hiển thị thông báo
                const notification = document.createElement('div');
                notification.textContent = 'Không tìm thấy nút chuyển trang';
                notification.style.position = 'fixed';
                notification.style.bottom = '20px';
                notification.style.right = '320px';
                notification.style.backgroundColor = '#f44336';
                notification.style.color = 'white';
                notification.style.padding = '10px';
                notification.style.borderRadius = '5px';
                notification.style.zIndex = '10000';
                document.body.appendChild(notification);
                
                // Xóa thông báo sau 5 giây
                setTimeout(() => {
                    document.body.removeChild(notification);
                }, 5000);
            }
        } else {
            // Nếu trích xuất thất bại
            extractionAttempts++;
            
            // Thử lại tối đa 3 lần nếu trích xuất thất bại
            if (extractionAttempts < 3) {
                console.log(`Thử trích xuất lần ${extractionAttempts}`);
                
                // Hiển thị thông báo
                const notification = document.createElement('div');
                notification.textContent = `Đang thử lại lần ${extractionAttempts}...`;
                notification.style.position = 'fixed';
                notification.style.bottom = '20px';
                notification.style.right = '320px';
                notification.style.backgroundColor = '#ff9800';
                notification.style.color = 'white';
                notification.style.padding = '10px';
                notification.style.borderRadius = '5px';
                notification.style.zIndex = '10000';
                document.body.appendChild(notification);
                
                // Xóa thông báo và thử lại sau 2 giây
                setTimeout(() => {
                    document.body.removeChild(notification);
                    autoExtractAndNavigate();
                }, 2000);
                return;
            }
            
            // Sau 3 lần thử vẫn thất bại
            isAutomaticExtraction = false;
            const startButton = document.querySelector('button');
            if (startButton) {
                startButton.textContent = 'Bắt đầu lấy text';
                startButton.style.backgroundColor = '#4caf50';
            }
            
            // Hiển thị thông báo
            const notification = document.createElement('div');
            notification.textContent = 'Trích xuất thất bại';
            notification.style.position = 'fixed';
            notification.style.bottom = '20px';
            notification.style.right = '320px';
            notification.style.backgroundColor = '#f44336';
            notification.style.color = 'white';
            notification.style.padding = '10px';
            notification.style.borderRadius = '5px';
            notification.style.zIndex = '10000';
            document.body.appendChild(notification);
            
            // Xóa thông báo sau 5 giây
            setTimeout(() => {
                document.body.removeChild(notification);
            }, 5000);
        }
    }

    // Khởi tạo panel khi trang đã tải xong
    window.addEventListener('load', function() {
        // Đợi 1 giây để đảm bảo trang đã tải hoàn toàn
        setTimeout(() => {
            createPanel();
            
            // Kiểm tra xem có đang trong quá trình tự động không
            if (localStorage.getItem('isAutoExtractionActive') === 'true') {
                console.log("Phát hiện quá trình tự động đang diễn ra, tiếp tục...");
                isAutomaticExtraction = true;
                
                // Đặt lại nút trạng thái
                const startButton = document.querySelector('button');
                if (startButton) {
                    startButton.textContent = 'Đang trích xuất...';
                    startButton.style.backgroundColor = '#ff9800';
                }
                
                // Tiếp tục quá trình sau khi trang đã tải
                setTimeout(() => {
                    autoExtractAndNavigate();
                }, 2000);
            }
        }, 1000);
    });
    
    // Thêm trình xử lý sự kiện cho việc tải trang mới
    window.addEventListener('popstate', function() {
        if (localStorage.getItem('isAutoExtractionActive') === 'true') {
            console.log("Phát hiện thay đổi URL, tiếp tục quá trình tự động...");
            isAutomaticExtraction = true;
        }
    });
})();