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.1
// @description  Tải nội dung chương truyện từ TangThuVien
// @author       Your name
// @match        https://truyen.tangthuvien.net/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;
        }
    `);

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

            // Tìm container chứa nội dung
            const container = document.querySelector('#article') || document.querySelector('.content-chapter');
            if (!container) {
                throw new Error('Không tìm thấy container chương');
            }

            // Tìm tiêu đề với nhiều selector
            const titleSelectors = [
                '.chapter-title',
                'h2.chapter-title',
                '.chapter-c h2',
                'h2',
                '.content-chapter h2'
            ];

            let titleElement = null;
            for (const selector of titleSelectors) {
                const element = container.querySelector(selector);
                if (element && element.textContent.trim()) {
                    titleElement = element;
                    console.log('Tìm thấy tiêu đề với selector:', selector);
                    break;
                }
            }

            // Tìm nội dung với nhiều selector
            const contentSelectors = [
                '.chapter-c',
                '.chapter-content',
                '.content-chapter'
            ];

            let contentElement = null;
            for (const selector of contentSelectors) {
                const element = container.querySelector(selector);
                if (element && element.textContent.trim()) {
                    contentElement = element;
                    console.log('Tìm thấy nội dung với selector:', selector);
                    break;
                }
            }

            console.log('Elements tìm thấy:', {
                hasTitle: !!titleElement,
                hasContent: !!contentElement,
                titleHTML: titleElement?.outerHTML,
                contentPreview: contentElement?.textContent?.trim().substring(0, 100)
            });

            if (!titleElement || !contentElement) {
                throw new Error('Không tìm thấy nội dung chương');
            }

            // Lấy và làm sạch nội dung
            let title = titleElement.textContent.trim();
            let content = contentElement.textContent.trim();

            // Loại bỏ các phần thừa
            content = content.replace(title, ''); // Xóa tiêu đề nếu trùng
            content = content.replace(/\[\s*\w+\s*\]/g, ''); // Xóa [xxx]
            content = content.replace(/[""]/g, '"'); // Chuẩn hóa dấu ngoặc kép
            content = content.replace(/\s+/g, ' '); // Chuẩn hóa khoảng trắng
            content = content.replace(/^\s+|\s+$/g, ''); // Trim

            if (!title || !content) {
                throw new Error('Nội dung chương không hợp lệ');
            }

            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 chương:', error);
            updateStatus('Lỗi khi lấy nội dung chương: ' + error.message);
            return null;
        }
    }

    // Tạo UI elements
    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);

        const status = document.createElement('div');
        status.className = 'status-text';

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

        console.log('Đã tạo giao diện thành công');
        return { editor, downloadBtn, exportBtn, status };
    }

    // Cập nhật trạng thái
    function updateStatus(message) {
        const status = document.querySelector('.status-text');
        if (status) {
            status.textContent = message;
            console.log('Trạng thái:', message);
        }
    }

    // Thêm nội dung chương mới vào editor
    function appendChapterContent(editor, title, content) {
        try {
            console.log('Bắt đầu thêm nội dung chương mới');
            console.log('Nội dung hiện tại:', editor.value.length, 'ký tự');

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

            // Thêm nội dung mới và giữ lại nội dung cũ
            editor.value = editor.value + separator + newContent;

            // Cuộn xuống dưới để hiển thị nội dung mới
            editor.scrollTop = editor.scrollHeight;

            console.log('Đã thêm thành công:', {
                addedLength: newContent.length,
                currentLength: editor.value.length
            });
            return true;
        } catch (error) {
            console.error('Lỗi khi thêm nội dung:', error);
            return false;
        }
    }

    // Xuất nội dung ra file txt
    function exportToTxt() {
        try {
            console.log('Bắt đầu xuất file...');
            const editor = document.querySelector('.story-editor');
            if (!editor || !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');

            // Lấy tên truyện từ URL
            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);

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

    // 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('Nhấn nút chuyển chương tiếp theo');
            nextButton.click();
            return true;
        }
        console.log('Không tìm thấy nút chuyển chương hợp lệ');
        return false;
    }

    // Kiểm tra xem có phải chương cuối không
    function isLastChapter() {
        const nextButton = document.querySelector('a.next-chap');
        console.log('Kiểm tra chương cuối:', {
            buttonExists: !!nextButton,
            isDisabled: nextButton?.classList.contains('disabled'),
            isHidden: nextButton?.style.display === 'none'
        });
        return !nextButton || nextButton.classList.contains('disabled') || nextButton.style.display === 'none';
    }

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

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

            // Đợi 5 giây để nội dung được tải
            await new Promise(resolve => setTimeout(resolve, 5000));

            // Lấy nội dung chương hiện tại
            const currentChapter = getCurrentChapter();
            if (!currentChapter) {
                throw new Error('Không thể lấy nội dung chương hiện tại');
            }

            const { title, content } = currentChapter;

            // Thêm nội dung mới vào cuối
            appendChapterContent(editor, title, content);
            updateStatus('Đã tải xong chương hiện tại');

            // Kiểm tra nếu là chương cuối
            if (isLastChapter()) {
                console.log('Đã phát hiện chương cuối');
                updateStatus('Đã tải xong tất cả các chương! Đang xuất file...');
                setTimeout(exportToTxt, 1000);
                return;
            }

            // Chuyển sang chương tiếp theo sau khi lấy xong chương hiện tại
            setTimeout(() => {
                if (goToNextChapter()) {
                    setTimeout(loadNextChapter, 3000);
                }
            }, 1000);
        } catch (error) {
            console.error('Lỗi khi tải chương:', error);
            updateStatus('Lỗi khi tải chương: ' + error.message);
        }
    }

    // Khởi tạo khi DOM đã sẵn sàng
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createUI);
    } else {
        createUI();
    }

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