Chapter Downloader

Tải nội dung chương truyện từ TangThuVien

目前為 2025-03-09 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Chapter Downloader 
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Tải nội dung chương truyện từ TangThuVien
// @author       Your name
// @match        https://truyen.tangthuvien.net/doc-truyen/*/*
// @match        https://truyen.tangthuvien.vn/doc-truyen/*/*
// @match        https://tangthuvien.vn/doc-truyen/*/*
// @match        https://tangthuvien.com/doc-truyen/*/*
// @match        https://*.tangthuvien.*/doc-truyen/*/*
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    // Thêm CSS cho khung soạn thảo
    GM_addStyle(`
        .story-editor-container {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 400px;
            height: 80vh;
            background: white;
            border: 1px solid #ccc;
            padding: 10px;
            z-index: 9999;
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        .story-editor {
            flex: 1;
            width: 100%;
            resize: none;
            font-family: Arial, sans-serif;
            font-size: 14px;
            line-height: 1.6;
            padding: 10px;
        }
        .button-container {
            display: flex;
            gap: 10px;
        }
        button {
            padding: 8px 16px;
            cursor: pointer;
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
        }
        button:hover {
            background: #45a049;
        }
        .status-text {
            font-size: 12px;
            color: #666;
        }
    `);

    // Log tất cả các elements có thể chứa nội dung
    function logPossibleElements() {
        const elements = document.querySelectorAll('*');
        const contentElements = Array.from(elements).filter(el => {
            const text = el.textContent?.trim();
            return text && text.length > 100;
        });

        console.log('Các elements có thể chứa nội dung:', 
            contentElements.map(el => ({
                tagName: el.tagName,
                className: el.className,
                id: el.id,
                textLength: el.textContent?.trim().length,
                firstChars: el.textContent?.trim().substring(0, 50)
            }))
        );
    }

    // Lấy nội dung chương hiện tại
    async function getCurrentChapter() {
        try {
            console.log('Bắt đầu lấy nội dung chương...');
            logPossibleElements();

            // Đợi nội dung tải xong (tăng thời gian chờ lên 5 giây)
            console.log('Đợi 5 giây để nội dung tải hoàn tất...');
            await new Promise(resolve => setTimeout(resolve, 5000));

            // Tìm container chính - mở rộng bộ chọn để phù hợp với TangThuVien
            const mainContainer = document.querySelector('#article') || 
                                document.querySelector('.content-chapter') || 
                                document.querySelector('.chapter-content') ||
                                document.querySelector('.box-chap') ||
                                document.querySelector('.content') ||
                                document.querySelector('.chapter');

            console.log('Container chính:', {
                found: !!mainContainer,
                id: mainContainer?.id,
                className: mainContainer?.className
            });

            // Nếu không tìm thấy container chính, thử tìm trực tiếp trong document
            // TangThuVien có thể không tuân theo cấu trúc container>tiêu đề+nội dung
            if (!mainContainer) {
                console.log('Không tìm thấy container chính, thử tìm trực tiếp trong document');
            }

            // Tìm tiêu đề
            const titleSelectors = [
                'h2.chapter-title',
                '.chapter-title',
                '.truyen-title',
                'h2.title',
                '.content-chapter h2',
                '.chapter h1',
                '.title-chapter',
                '.chapter-title h2',
                'h1.title',
                'header h1'
            ];

            let titleElement;
            // Tìm trong container chính nếu có
            if (mainContainer) {
                for (const selector of titleSelectors) {
                    titleElement = mainContainer.querySelector(selector);
                    if (titleElement?.textContent?.trim()) {
                        console.log('Tìm thấy tiêu đề với selector trong container:', selector);
                        break;
                    }
                }
            }
            
            // Nếu không tìm thấy trong container, tìm trong toàn bộ document
            if (!titleElement || !titleElement?.textContent?.trim()) {
                for (const selector of titleSelectors) {
                    titleElement = document.querySelector(selector);
                    if (titleElement?.textContent?.trim()) {
                        console.log('Tìm thấy tiêu đề với selector trong document:', selector);
                        break;
                    }
                }
            }

            // Tìm nội dung
            const contentSelectors = [
                '.chapter-c',
                '.chapter-content',
                '.content-chapter',
                '.box-chap',
                '.content',
                '#content',
                '.chapter-detail',
                '.chapter-text',
                '.chapter__content',
                '.ttv-chapter-content'
            ];

            let contentElement;
            // Tìm trong container chính nếu có
            if (mainContainer) {
                for (const selector of contentSelectors) {
                    contentElement = mainContainer.querySelector(selector);
                    if (contentElement?.textContent?.trim()) {
                        console.log('Tìm thấy nội dung với selector trong container:', selector);
                        break;
                    }
                }
            }
            
            // Nếu không tìm thấy trong container, tìm trong toàn bộ document
            if (!contentElement || !contentElement?.textContent?.trim()) {
                for (const selector of contentSelectors) {
                    contentElement = document.querySelector(selector);
                    if (contentElement?.textContent?.trim()) {
                        console.log('Tìm thấy nội dung với selector trong document:', selector);
                        break;
                    }
                }
            }

            // Xử lý trường hợp không tìm thấy tiêu đề hoặc nội dung
            if (!titleElement && !contentElement) {
                console.error('Không tìm thấy cả tiêu đề và nội dung');
                // Thử phương pháp cuối cùng - lấy toàn bộ văn bản của trang
                const bodyText = document.body.innerText;
                if (bodyText && bodyText.length > 500) { // Giả sử trang có ít nhất 500 ký tự
                    console.log('Thử phương pháp cuối cùng - lấy toàn bộ văn bản');
                    
                    // Lấy tiêu đề từ thẻ title của trang
                    const pageTitle = document.title.trim();
                    
                    return {
                        title: pageTitle,
                        content: bodyText
                    };
                }
                throw new Error('Không tìm thấy nội dung hoặc tiêu đề');
            }
            
            if (!titleElement) {
                console.log('Không tìm thấy tiêu đề, sử dụng title của trang');
                const pageTitle = document.title.trim();
                titleElement = { textContent: pageTitle };
            }
            
            if (!contentElement) {
                console.error('Không tìm thấy nội dung:', {
                    hasTitle: !!titleElement,
                    titleHTML: titleElement?.outerHTML
                });
                throw new Error('Không tìm thấy nội dung');
            }

            console.log('Tìm thấy cả tiêu đề và nội dung:', {
                hasTitle: !!titleElement,
                hasContent: !!contentElement,
                titlePreview: titleElement?.textContent?.trim().substring(0, 50),
                contentPreview: contentElement?.textContent?.trim().substring(0, 100)
            });

            // Xử lý nội dung
            let title = titleElement.textContent.trim();
            let content = contentElement.textContent.trim();

            // Làm sạch nội dung
            content = content.replace(title, ''); // Xóa tiêu đề khỏi nội dung
            content = content.replace(/\[\s*\w+\s*\]/g, ''); // Xóa [xxx]
            content = content.replace(/\[.*?\]/g, ''); // Xóa tất cả nội dung trong ngoặc vuông
            content = content.replace(/[""]/g, '"'); // Chuẩn hóa dấu ngoặc kép
            content = content.replace(/\s+/g, ' ').trim(); // Chuẩn hóa khoảng trắng
            
            // Loại bỏ các đoạn quảng cáo phổ biến
            content = content.replace(/Truyện\s+VIP\s+\S+/gi, '');
            content = content.replace(/Đọc\s+truyện\s+tại\s+\S+/gi, '');
            content = content.replace(/Tham\s+gia\s+group\s+\S+/gi, '');
            content = content.replace(/nguồn\s*:\s*\S+/gi, '');

            console.log('Đã lấy nội dung:', {
                title,
                contentLength: content.length,
                contentPreview: content.substring(0, 100) + '...'
            });

            return { title, content };
        } catch (error) {
            console.error('Lỗi khi lấy nội dung:', error);
            return null;
        }
    }

    // Thêm nội dung chương mới vào editor
    function appendChapterContent(editor, title, content) {
        try {
            if (!editor || !title || !content) {
                console.error('Input không hợp lệ:', {
                    hasEditor: !!editor,
                    hasTitle: !!title,
                    hasContent: !!content
                });
                return false;
            }

            // Thêm dấu phân cách nếu đã có nội dung
            const separator = editor.value ? '\n\n' + '-'.repeat(50) + '\n\n' : '';
            const newContent = title + '\n\n' + content;

            // Nối tiếp nội dung vào cuối
            editor.value += separator + newContent;
            editor.scrollTop = editor.scrollHeight;

            console.log('Đã thêm nội dung:', {
                previousLength: editor.value.length - (separator + newContent).length,
                addedLength: newContent.length,
                newTotalLength: editor.value.length
            });

            return true;
        } catch (error) {
            console.error('Lỗi khi thêm nội dung:', error);
            return false;
        }
    }

    // Chuyển đến chương tiếp theo
    function goToNextChapter() {
        const nextButton = document.querySelector('a.next-chap');
        if (nextButton && !nextButton.classList.contains('disabled') && nextButton.style.display !== 'none') {
            console.log('Chuyển đến chương tiếp theo');
            nextButton.click();
            return true;
        }
        console.log('Không tìm thấy nút next hợp lệ');
        return false;
    }

    // Tải chương tiếp theo
    async function loadNextChapter() {
        try {
            console.log('Bắt đầu tải chương tiếp...');

            const editor = document.querySelector('.story-editor');
            if (!editor) {
                throw new Error('Không tìm thấy editor');
            }

            // Đợi nội dung tải
            const chapterData = await getCurrentChapter();
            if (!chapterData) {
                throw new Error('Không lấy được nội dung chương');
            }

            // Thêm nội dung vào editor
            if (!appendChapterContent(editor, chapterData.title, chapterData.content)) {
                throw new Error('Không thêm được nội dung vào editor');
            }

            // Tự động tải chương tiếp
            setTimeout(() => {
                if (goToNextChapter()) {
                    setTimeout(loadNextChapter, 3000);
                }
            }, 1000);

        } catch (error) {
            console.error('Lỗi khi tải chương:', error);
        }
    }

    // Xuất nội dung ra file txt
    function exportToTxt() {
        try {
            const editor = document.querySelector('.story-editor');
            if (!editor?.value) {
                throw new Error('Không có nội dung để xuất');
            }

            const blob = new Blob([editor.value], { type: 'text/plain;charset=utf-8' });
            const url = window.URL.createObjectURL(blob);
            const link = document.createElement('a');

            const storyName = window.location.pathname.split('/')[2] || 'truyen';
            link.href = url;
            link.download = `${storyName}.txt`;

            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            window.URL.revokeObjectURL(url);

            console.log('Đã xuất file thành công');
        } catch (error) {
            console.error('Lỗi khi xuất file:', error);
        }
    }

    // Tạo giao diện
    function createUI() {
        console.log('Tạo giao diện...');

        const container = document.createElement('div');
        container.className = 'story-editor-container';

        const editor = document.createElement('textarea');
        editor.className = 'story-editor';
        editor.placeholder = 'Nội dung truyện sẽ hiển thị ở đây...';
        editor.readOnly = true;

        const buttonContainer = document.createElement('div');
        buttonContainer.className = 'button-container';

        const downloadBtn = document.createElement('button');
        downloadBtn.textContent = 'Tải chương tiếp';
        downloadBtn.onclick = loadNextChapter;

        const exportBtn = document.createElement('button');
        exportBtn.textContent = 'Xuất file TXT';
        exportBtn.onclick = exportToTxt;

        buttonContainer.appendChild(downloadBtn);
        buttonContainer.appendChild(exportBtn);

        container.appendChild(editor);
        container.appendChild(buttonContainer);
        document.body.appendChild(container);

        console.log('Đã tạo giao diện thành công');
    }

    // Khởi tạo script
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createUI);
    } else {
        createUI();
    }

    console.log('TangThuVien Chapter Downloader đã khởi chạy');
})();