TTV Auto Upload

Công cụ đăng chương đơn giản cho Tàng Thư Viện

当前为 2025-03-09 提交的版本,查看 最新版本

// ==UserScript==
// @name         TTV Auto Upload
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Công cụ đăng chương đơn giản cho Tàng Thư Viện
// @author       HA
// @match        https://tangthuvien.net/dang-chuong/story/*
// @match        https://tangthuvien.net/danh-sach-chuong/story/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const style = document.createElement('style');
    style.textContent = `
        #ttv-panel {
            position: fixed;
            top: 50px;
            right: 20px;
            background: white;
            padding: 20px;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
            width: 400px;
            z-index: 9998;
            max-height: 90vh;
            overflow-y: auto;
        }
        #ttv-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 1px solid #eee;
        }
        #ttv-header h3 {
            color: #2196F3;
            margin: 0;
            font-size: 18px;
            font-weight: 600;
        }
        #ttv-stats {
            display: flex;
            gap: 10px;
            align-items: center;
            font-size: 14px;
            color: #666;
        }
        #ttv-stats .stat {
            padding: 4px 8px;
            border-radius: 6px;
            background: #f5f5f5;
        }
        #ttv-content {
            width: 100%;
            height: 120px;
            margin-bottom: 15px;
            padding: 12px;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            font-size: 14px;
            font-family: monospace;
            transition: all 0.2s ease;
            resize: vertical;
            background: #fff;
        }
        #ttv-content:focus {
            border-color: #2196F3;
            box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);
            outline: none;
        }
        .btn-group {
            display: flex;
            gap: 10px;
            margin: 15px 0;
        }
        #ttv-panel button {
            flex: 1;
            padding: 12px 15px;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-weight: 600;
            font-size: 14px;
            transition: all 0.2s;
            position: relative;
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }
        #ttv-panel button:hover {
            transform: translateY(-1px);
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        #ttv-panel button:active {
            transform: translateY(0);
        }
        #ttv-panel button:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }
        .btn-auto {
            background: linear-gradient(45deg, #4CAF50, #45a049);
        }
        .btn-manual {
            background: linear-gradient(45deg, #2196F3, #1e88e5);
        }
        #ttv-panel button.processing {
            pointer-events: none;
        }
        #ttv-panel button.processing:after {
            content: "";
            position: absolute;
            width: 20px;
            height: 20px;
            right: 10px;
            border: 2px solid rgba(255,255,255,0.3);
            border-top: 2px solid white;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }
        #ttv-chapters {
            width: 100%;
            margin-bottom: 15px;
            border: 1px solid #eee;
            border-radius: 8px;
            max-height: 300px;
            overflow-y: auto;
            background: #fafafa;
            display: none;
        }
        #ttv-chapters.has-chapters {
            display: block;
        }
        .chapter-item {
            padding: 15px;
            border-bottom: 1px solid #eee;
            background: white;
            transition: all 0.2s;
        }
        .chapter-item:hover {
            background: #f5f5f5;
        }
        .chapter-item:last-child {
            border-bottom: none;
        }
        .chapter-title {
            font-weight: 600;
            margin-bottom: 8px;
            color: #333;
            font-size: 14px;
        }
        .chapter-stats {
            font-size: 12px;
            color: #666;
            display: flex;
            gap: 10px;
            align-items: center;
        }
        .chapter-warning {
            color: #f44336;
            font-weight: 500;
            padding: 2px 6px;
            background: rgba(244, 67, 54, 0.1);
            border-radius: 4px;
        }
        .chapter-long {
            color: #ff9800;
            font-weight: 500;
            padding: 2px 6px;
            background: rgba(255, 152, 0, 0.1);
            border-radius: 4px;
        }
        #ttv-notification {
            margin-top: 10px;
            padding: 10px 15px;
            border-radius: 8px;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 8px;
            opacity: 0;
            transition: opacity 0.3s ease;
        }
        #ttv-notification.show {
            opacity: 1;
        }
        #ttv-notification.success {
            background-color: #e8f5e9;
            color: #2e7d32;
            border: 1px solid #c8e6c9;
        }
        #ttv-notification.error {
            background-color: #ffebee;
            color: #c62828;
            border: 1px solid #ffcdd2;
        }
        #ttv-notification.warning {
            background-color: #fff3e0;
            color: #ef6c00;
            border: 1px solid #ffe0b2;
        }
        #ttv-notification.info {
            background-color: #e3f2fd;
            color: #1565c0;
            border: 1px solid #bbdefb;
        }
        .loading-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999;
            opacity: 0;
            transition: opacity 0.3s ease;
        }
        .loading-overlay.show {
            opacity: 1;
        }
        .loading-content {
            background: white;
            padding: 20px;
            border-radius: 8px;
            text-align: center;
        }
        .loading-spinner {
            width: 40px;
            height: 40px;
            border: 3px solid #f3f3f3;
            border-top: 3px solid #2196F3;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin: 0 auto 10px;
        }
        .chapter-character-count {
            text-align: right;
            font-size: 12px;
            margin-top: 5px;
            color: #666;
        }
        textarea[name^="introduce"].short-chapter {
            border: 2px solid #f44336 !important;
            background-color: rgba(244, 67, 54, 0.05) !important;
            animation: shortChapterBlink 1s infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        @keyframes shortChapterBlink {
            0% { background-color: rgba(244, 67, 54, 0.05); }
            50% { background-color: rgba(244, 67, 54, 0.1); }
            100% { background: rgba(244, 67, 54, 0.05); }
        }
        .chapter-counter {
            position: absolute;
            top: -8px;
            right: -8px;
            background: #ff5722;
            color: white;
            border-radius: 12px;
            padding: 2px 6px;
            font-size: 12px;
            font-weight: bold;
        }
    `;
    document.head.appendChild(style);

    const TTVManager = {
        STATE: {
            chapterNumber: 1,
            chapterSTT: 1,
            chapterSerial: 1,
            isAuto: false,
            isProcessing: false,
            totalChapters: 0,
            processedChapters: 0
        },

        init: function() {
            console.log('[TTV-DEBUG] Initializing script...');
            this.createFormContainer = this.createFormContainer.bind(this);
            this.initializeChapterValues = this.initializeChapterValues.bind(this);
            this.createInterface = this.createInterface.bind(this);
            this.setupEventListeners = this.setupEventListeners.bind(this);
            this.setupCharacterCounter = this.setupCharacterCounter.bind(this);
            this.showNotification = this.showNotification.bind(this);
            this.showLoading = this.showLoading.bind(this);
            this.hideLoading = this.hideLoading.bind(this);
            this.createFormContainer();
            this.initializeChapterValues();
            this.createInterface();
            this.setupEventListeners();
            this.setupCharacterCounter();
            console.log('[TTV-DEBUG] Script initialized successfully');
            this.showNotification('Công cụ đã sẵn sàng', 'success');
        },

        createInterface: function() {
            const panel = document.createElement('div');
            panel.id = 'ttv-panel';
            panel.innerHTML = `
                <div id="ttv-header">
                    <h3>📝 ĐĂNG CHƯƠNG</h3>
                    <div id="ttv-stats">
                        <span class="stat" id="total-chapters">0 chương</span>
                        <span class="stat" id="processed-chapters">0/0</span>
                    </div>
                </div>
                <div id="ttv-chapters"></div>
                <textarea 
                    id="ttv-content" 
                    placeholder="Dán nội dung vào đây để tự động tách chương...
Hỗ trợ các định dạng:
- Chương 1: ...
- [Tab]Chương 2: ...
-    Chương 3: ..."
                ></textarea>
                <div class="btn-group">
                    <button class="btn-auto" id="ttv-auto">
                        <span>🔄 Tự động đăng</span>
                        <div class="chapter-counter">0/10</div>
                    </button>
                    <button class="btn-manual" id="ttv-manual">
                        <span>📝 Đăng thủ công</span>
                    </button>
                </div>
                <div id="ttv-notification"></div>
            `;
            document.body.appendChild(panel);
        },

        updateStats: function() {
            document.getElementById('total-chapters').textContent = 
                `${this.STATE.totalChapters} chương`;
            document.getElementById('processed-chapters').textContent = 
                `${this.STATE.processedChapters}/${this.STATE.totalChapters}`;
            document.querySelector('.chapter-counter').textContent = 
                `${Math.min(this.STATE.totalChapters, 10)}/10`;
        },

        createChapterHTML: function(chapNum) {
            const chap_vol = parseInt(jQuery('.chap_vol').val()) || 1;
            const chap_vol_name = jQuery('.chap_vol_name').val() || '';
            return `
            <div data-gen="MK_GEN" id="COUNT_CHAP_${chapNum}_MK">
                <div class="col-xs-12 form-group"></div>
                <div class="form-group">
                    <label class="col-sm-2" for="chap_stt">STT</label>
                    <div class="col-sm-8">
                        <input class="form-control" required name="chap_stt[${chapNum}]" value="${dăngnhanhTTV.STATE.CHAP_STT}" placeholder="Số thứ tự của chương" type="text"/>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2" for="chap_number">Chương thứ..</label>
                    <div class="col-sm-8">
                        <input value="${dăngnhanhTTV.STATE.CHAP_SERIAL}" required class="form-control" name="chap_number[${chapNum}]" placeholder="Chương thứ.. (1,2,3..)" type="text"/>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2" for="chap_name">Quyển số</label>
                    <div class="col-sm-8">
                        <input class="form-control" name="vol[${chapNum}]" placeholder="Quyển số" type="number" value="${chap_vol}" required/>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2" for="chap_name">Tên quyển</label>
                    <div class="col-sm-8">
                        <input class="form-control chap_vol_name" name="vol_name[${chapNum}]" placeholder="Tên quyển" type="text" value="${chap_vol_name}" />
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2" for="chap_name">Tên chương</label>
                    <div class="col-sm-8">
                        <input required class="form-control" name="chap_name[${chapNum}]" placeholder="Tên chương" type="text"/>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2" for="introduce">Nội dung</label>
                    <div class="col-sm-8">
                        <textarea maxlength="75000" style="color:#000;font-weight: 400;" required class="form-control" name="introduce[${chapNum}]" rows="20" placeholder="Nội dung" type="text"></textarea>
                        <div class="chapter-character-count"></div>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2" for="adv">Quảng cáo</label>
                    <div class="col-sm-8">
                        <textarea maxlength="1000" class="form-control" name="adv[${chapNum}]" placeholder="Quảng cáo" type="text"></textarea>
                    </div>
                </div>
            </div>`;
        },

        setupCharacterCounter: function() {
            jQuery(document).on("input", "[name^=introduce]", function() {
                const text = jQuery(this).val();
                const charCount = text.length;
                let charCountElement = jQuery(this).next('.chapter-character-count');
                if (charCountElement.length === 0) {
                    charCountElement = jQuery('<div class="chapter-character-count"></div>');
                    jQuery(this).after(charCountElement);
                }
                if(charCount < 3000) {
                    jQuery(this).addClass('short-chapter');
                    charCountElement.html(`<span class="short-chapters-warning">${charCount.toLocaleString()}/40.000 ký tự</span>`);
                } else {
                    jQuery(this).removeClass('short-chapter');
                    if(charCount > 40000) {
                        charCountElement.html(`<span style="color: #fbbc05;">${charCount.toLocaleString()}/40.000 ký tự</span>`);
                    } else {
                        charCountElement.html(`<span style="color: #34a853;">${charCount.toLocaleString()}/40.000 ký tự</span>`);
                    }
                }
            });
        },

        validateChapterLengths: function() {
            let hasError = false;
            jQuery('form[name="postChapForm"] .chapter-detail').each(function() {
                const form = this;
                const contentTextarea = form.querySelector('textarea[name^="introduce"]');
                const content = contentTextarea.value;
                if (content.length < 3000) {
                    jQuery(contentTextarea).addClass('short-chapter');
                    let warningIcon = form.querySelector('.warning-icon');
                    if (!warningIcon) {
                        warningIcon = document.createElement('div');
                        warningIcon.className = 'warning-icon';
                        warningIcon.innerHTML = '⚠️';
                        contentTextarea.parentNode.appendChild(warningIcon);
                    }
                    hasError = true;
                } else {
                    jQuery(contentTextarea).removeClass('short-chapter');
                    const warningIcon = form.querySelector('.warning-icon');
                    if (warningIcon) {
                        warningIcon.remove();
                    }
                }
            });
            return !hasError;
        },


        initializeChapterValues: function() {
            try {
                const chap_number = parseInt(jQuery('#chap_number').val());
                let chap_stt = parseInt(jQuery('.chap_stt1').val());
                let chap_serial = parseInt(jQuery('.chap_serial').val());

                if (parseInt(jQuery('#chap_stt').val()) > chap_stt) {
                    chap_stt = parseInt(jQuery('#chap_stt').val());
                }
                if (parseInt(jQuery('#chap_serial').val()) > chap_serial) {
                    chap_serial = parseInt(jQuery('#chap_serial').val());
                }

                this.STATE.chapterNumber = this.STATE.chapterNumberOriginal = chap_number || 1;
                this.STATE.chapterSTT = this.STATE.chapterSTTOriginal = chap_stt || 1;
                this.STATE.chapterSerial = this.STATE.chapterSerialOriginal = chap_serial || 1;
            } catch (e) {
                console.error("Error initializing chapter values:", e);
            }
        },

        showNotification: function(message, type = 'info') {
            const notification = document.getElementById('ttv-notification');
            let icon = '';
            switch(type) {
                case 'success': icon = '✅'; break;
                case 'error': icon = '❌'; break;
                case 'warning': icon = '⚠️'; break;
                case 'info': icon = 'ℹ️'; break;
            }

            notification.className = type;
            notification.innerHTML = `
                <span class="notification-icon">${icon}</span>
                ${message}
            `;
            notification.classList.add('show');

            if (type === 'success' || type === 'info') {
                setTimeout(() => {
                    notification.classList.remove('show');
                }, 5000);
            }
        },

        showLoading: function(message = 'Đang xử lý...') {
            let overlay = document.querySelector('.loading-overlay');
            if (!overlay) {
                overlay = document.createElement('div');
                overlay.className = 'loading-overlay';
                overlay.innerHTML = `
                    <div class="loading-content">
                        <div class="loading-spinner"></div>
                        <div class="loading-message">${message}</div>
                    </div>
                `;
                document.body.appendChild(overlay);
                setTimeout(() => overlay.classList.add('show'), 0);
            }
        },

        hideLoading: function() {
            const overlay = document.querySelector('.loading-overlay');
            if (overlay) {
                overlay.classList.remove('show');
                setTimeout(() => overlay.remove(), 300);
            }
        },

        // ...rest of the functions remain largely unchanged...  (handlePaste, performAction, etc.)

        updateChapNumber: function(isAdd) {
            try{
                if (isAdd) {
                    let maxStt =0;
                    let maxSerial = 0;
                    jQuery('input[name^="chap_stt"]').each(function() {
                        const val = parseInt(jQuery(this).val()) || 0;
                        maxStt = Math.max(maxStt, val);
                    });
                    jQuery('input[name^="chap_number"]').each(function() {
                        const val = parseInt(jQuery(this).val()) || 0;
                        maxSerial = Math.max(maxSerial, val);
                    });
                    const chapStt = parseInt(jQuery('.chap_stt1').val()) || 0;
                    const chapSerial = parseInt(jQuery('.chap_serial').val()) || 0;
                    maxStt = Math.max(maxStt, chapStt);
                    maxSerial = Math.max(maxSerial, chapSerial);
                    this.STATE.chapterSTT = maxStt + 1;
                    this.STATE.chapterSerial = maxSerial + 1;
                    this.STATE.chapterNumber++;
                } else {
                    if (this.STATE.chapterNumber > this.STATE.chapterNumberOriginal) {
                        this.STATE.chapterNumber--;
                    }
                    if (this.STATE.chapterSTT > this.STATE.chapterSTTOriginal) {
                        this.STATE.chapterSTT--;
                    }
                    if (this.STATE.chapterSerial > this.STATE.chapterSerialOriginal) {
                        this.STATE.chapterSerial--;
                    }
                }
                jQuery('#chap_number').val(this.STATE.chapterNumber);
                jQuery('#chap_stt').val(this.STATE.chapterSTT);
                jQuery('#chap_serial').val(this.STATE.chapterSerial);
                jQuery('#countNumberPost').text(this.STATE.chapterNumber);
            } catch (e) {
                console.log("Lỗi: " + e);
            }
        },

        // ...rest of the functions remain largely unchanged... (removeEmptyChapters, toggleAutoPost, runAutoPostSequence, submitChapters, addNewChapter, resetAutoPost, toggleAutoMode)

        setupEventListeners: function() {
            jQuery('#ttv-content').on('paste', this.handlePaste.bind(this));
            jQuery('#ttv-manual').on('click', this.submitChapters.bind(this));
            jQuery('#ttv-auto').on('click', this.toggleAutoPost.bind(this));
            jQuery('#qpButtonRemoveEmpty').on('click', this.removeEmptyChapters.bind(this));
            jQuery('#qpButtonReset').on('click', this.resetAutoPost.bind(this));
            jQuery('#qpOptionLoop').on('change', this.toggleAutoMode.bind(this));
            this.setupCharacterCounter();

            if (window.location.href.includes('/dang-chuong/story/')) {
                setTimeout(() => {
                    if (this.STATE.isAuto && this.STATE.processedChapters < this.STATE.totalChapters) {
                        this.runAutoPostSequence();
                    }
                }, 2000);
            }
        },
        handlePasteButton: function() {
            this.showLoading();
            navigator.clipboard.readText()
                .then(text => {
                    jQuery('#ttv-content').val(text);
                    setTimeout(() => {
                        this.performAction();
                        this.hideLoading();
                    }, 100);
                })
                .catch(err => {
                    console.error('Không thể đọc dữ liệu từ clipboard:', err);
                    this.hideLoading();
                    this.showNotification('Không thể truy cập clipboard. Vui lòng dán trực tiếp vào ô nội dung.', 'error');
                });
        },
        handlePaste: function(e) {
            e.preventDefault();
            jQuery('#ttv-content').val("");
            this.showLoading();
            const pastedText = e.originalEvent.clipboardData.getData('text');
            jQuery('#ttv-content').val(pastedText);
            setTimeout(() => {
                this.performAction();
                this.hideLoading();
            }, 100);
        },
        performAction: function() {
            try {
                console.log("Starting performAction");
                var text = jQuery('#ttv-content').val();

                if (!text) {
                    this.showNotification('Không có nội dung để tách chương', 'error');
                    return 0;
                }
                var debugOutput = [];
                var chapters = [];
                var lines = text.split('\n');
                var currentChapter = [];
                var lastTitle = null;
                debugOutput.push("=== Processing Text ===");
                debugOutput.push(`Total lines: ${lines.length}`);
                debugOutput.push("=== Line Analysis ===");
                function visualizeWhitespace(str) {
                    return str.split('').map(c => {
                        if (c === '\t') return '\\t';
                        if (c === ' ') return '·';
                        if (c === '\n') return '\\n';
                        return c;
                    }).join('');
                }

                // Hàm lấy mã chương dựa vào tiêu đề
                function getChapterCode(title) {
                    // Lấy số chương + tên chương, bỏ qua các ký tự đặc biệt
                    const match = title.match(/[Cc]hương\s*(\d+)\s*:/);
                    if (!match) return title.trim();
                    const chapterNum = match[1];
                    return `chap_${chapterNum}`;
                }

                for (let i = 0; i < lines.length; i++) {
                    let line = lines[i];
                    let isChapterTitle = /^\t[Cc]hương\s*\d+\s*:/.test(line) || /^\s{4,}[Cc]hương\s*\d+\s*:/.test(line);
                    debugOutput.push(`Line ${i}: ${visualizeWhitespace(line.substring(0, 50))}${line.length > 50 ? '...' : ''}`);
                    debugOutput.push(`  Is chapter: ${isChapterTitle}`);

                    if (isChapterTitle) {
                        // Lấy mã chương để so sánh
                        const currentChapterCode = getChapterCode(line);
                        const lastTitleCode = lastTitle ? getChapterCode(lastTitle) : null;

                        if (currentChapter.length > 0) {
                            // Kiểm tra nếu chương hiện tại khác chương trước đó
                            if (currentChapterCode !== lastTitleCode) {
                                chapters.push(currentChapter.join('\n'));
                                currentChapter = [line];
                                lastTitle = line;
                                debugOutput.push(`  -> New chapter started: ${currentChapterCode}`);
                            } else {
                                debugOutput.push(`  -> Skipped duplicate chapter: ${currentChapterCode}`);
                                // Không cần thêm dòng này vào chapter hiện tại vì nó là tiêu đề trùng lặp
                            }
                        } else {
                            currentChapter = [line];
                            lastTitle = line;
                            debugOutput.push(`  -> First chapter started: ${currentChapterCode}`);
                        }
                    } else if (currentChapter.length > 0) {
                        currentChapter.push(line);
                    }
                }
                if (currentChapter.length > 0) {
                    chapters.push(currentChapter.join('\n'));
                    debugOutput.push("-> Added final chapter");
                }
                debugOutput.push("=== Results ===");
                debugOutput.push(`Found ${chapters.length} chapters`);
                const processedChapters = [];
                for (let i = 0; i < chapters.length; i++) {
                    const chapterLines = chapters[i].split('\n');
                    const title = chapterLines.shift().trim();
                    const chapterText = chapterLines.join('\n');
                    const charCount = chapterText.length;
                    debugOutput.push(`Chapter ${i+1} character count: ${charCount}`);
                    if (charCount > 40000) {
                        const parts = Math.ceil(charCount / 40000);
                        debugOutput.push(`Splitting into ${parts} parts`);
                        const charsPerPart = Math.ceil(charCount / parts);
                        debugOutput.push(`Characters per part: ~${charsPerPart}`);
                        let currentText = chapterText;
                        let totalProcessed = 0;
                        for (let part = 0; part < parts; part++) {
                            const isLastPart = part === parts - 1;
                            const targetSize = isLastPart ? currentText.length : charsPerPart;
                            let endPos = Math.min(targetSize, currentText.length);
                            if (!isLastPart && endPos < currentText.length) {
                                const nextParagraph = currentText.indexOf('\n\n', endPos - 500);
                                if (nextParagraph !== -1 && nextParagraph < endPos + 500) {
                                    endPos = nextParagraph + 2;
                                } else {
                                    const sentenceEnd = Math.max(
                                        currentText.lastIndexOf('. ', endPos),
                                        currentText.lastIndexOf('! ', endPos),
                                        currentText.lastIndexOf('? ', endPos)
                                    );
                                    if (sentenceEnd !== -1 && sentenceEnd > endPos - 500) {
                                        endPos = sentenceEnd + 2;
                                    }
                                }
                            }
                            const partContent = currentText.substring(0, endPos);
                            totalProcessed += partContent.length;
                            currentText = currentText.substring(endPos);
                            let chapterTitle = title;
                            if (title.includes(':')) {
                                chapterTitle = title.substring(title.indexOf(':') + 1).trim();
                            }
                            let newTitle = `${title} (Phần ${part+1}/${parts})`;
                            processedChapters.push(newTitle + '\n' + partContent);
                            debugOutput.push(`Part ${part+1}: ${partContent.length} chars`);
                        }
                        debugOutput.push(`Total processed: ${totalProcessed}/${charCount} chars`);
                    } else {
                        processedChapters.push(chapters[i]);
                    }
                }
                debugOutput.push(`After processing: ${processedChapters.length} chapters`);
                const chaptersToFill = processedChapters.slice(0, MAX_CHAPTER_POST);
                const remainingChapters = processedChapters.slice(MAX_CHAPTER_POST);
                var titles = jQuery("input[name^='chap_name']");
                var contents = jQuery("textarea[name^='introduce']");
                var advs = jQuery("textarea[name^='adv']");
                debugOutput.push(`Forms found: ${titles.length}`);
                if (processedChapters.length === 0) {
                    this.showNotification('Không tìm thấy chương nào', 'error');
                    jQuery('#debug-output').text(debugOutput.join('\n'));
                    return;
                }
                if (remainingChapters.length > 0) {
                    debugOutput.push(`${remainingChapters.length} chapters will be copied to clipboard`);
                }
                const neededForms = chaptersToFill.length - titles.length;
                if (neededForms > 0 && titles.length < MAX_CHAPTER_POST) {
                    debugOutput.push(`Need to add ${neededForms} more forms`);
                    for (let i = 0; i < neededForms && (titles.length + i) < MAX_CHAPTER_POST; i++) {
                        this.addNewChapter();
                    }
                    titles = jQuery("input[name^='chap_name']");
                    contents = jQuery("textarea[name^='introduce']");
                    advs = jQuery("textarea[name^='adv']");
                }
                debugOutput.push(`Filling ${chaptersToFill.length} chapters into forms`);
                jQuery.each(titles, function(k, v) {
                    if (k < chaptersToFill.length) {
                        var content = chaptersToFill[k].split('\n');
                        var title = content.shift().trim();
                        var chapterTitle = title;
                        if (title.includes(':')) {
                            chapterTitle = title.substring(title.indexOf(':') + 1).trim();
                        }
                        debugOutput.push(`\nFilling chapter ${k + 1}:`);
                        debugOutput.push(`Original title: ${title}`);
                        debugOutput.push(`Extracted title: ${chapterTitle}`);
                        debugOutput.push(`Content length: ${content.length} lines`);
                        if (!chapterTitle || chapterTitle.trim() === '') {
                            chapterTitle = "Vô đề";
                            debugOutput.push(`Empty title detected, using default: ${chapterTitle}`);
                        }
                        titles[k].value = chapterTitle;
                        contents[k].value = HEADER_SIGN + "\r\n" + content.join('\n') + "\r\n" + FOOTER_SIGN;
                        if (advs[k]) advs[k].value = "";
                        jQuery(contents[k]).trigger('input');
                    }
                });
                if (remainingChapters.length > 0) {
                    try {
                        const clipboardContent = remainingChapters.map(chap => {
                            const lines = chap.trim().split('\n');
                            if (lines.length > 0) {
                                if (!lines[0].startsWith('\t')) {
                                    lines[0] = '\t' + lines[0];
                                }
                            }
                            return lines.join('\n');
                        }).join('\n\n---CHAPTER_SEPARATOR---\n\n');
                        let splitChapters = 0;
                        let shortChapters = 0;
                        let shortChapterDetails = [];
                        let longChapterDetails = [];
                        for (let i = 0; i < chapters.length; i++) {
                            const chapterLines = chapters[i].split('\n');
                            const title = chapterLines.shift().trim();
                            const chapterText = chapterLines.join('\n');
                            if (chapterText.length > 40000) {
                                splitChapters++;
                                const partsCount = Math.ceil(chapterText.length / 40000);
                                longChapterDetails.push({
                                    title: title,
                                    parts: partsCount
                                });
                            }
                            if (chapterText.length < 3000) {
                                shortChapters++;
                                shortChapterDetails.push({
                                    title: title,
                                    length: chapterText.length
                                });
                            }
                        }
                        const splittedChaptersCount = processedChapters.length - (chapters.length - splitChapters);
                        let message = '';
                        message = message.concat(`📝Đã xử lý ${processedChapters.length} Chương\n`);
                        message = message.concat(`📝Đã nhập ${Math.min(processedChapters.length, MAX_CHAPTER_POST)} Chương\n`);

                        if (remainingChapters.length > 0) {
                            message = message.concat(`📋Đã lưu lại ${remainingChapters.length} Chương\n`);
                        }
                        if (splitChapters > 0) {
                            message = message.concat(`📑Có ${splitChapters} Chương đã chia thành ${splittedChaptersCount} Chương\n`);
                            longChapterDetails.forEach(chapter => {
                                let chapterName = chapter.title;
                                if (chapterName.includes(':')) {
                                    chapterName = chapterName.trim();
                                }
                                message = message.concat(` - ${chapterName}: ${chapter.parts} Chương\n`);
                            });
                        }
                        if (shortChapters > 0) {
                            message = message.concat(`⚠️<span class="short-chapters-warning">Có ${shortChapters} chương dưới 3000 ký tự</span>\n`);
                            shortChapterDetails.forEach(chapter => {
                                let chapterName = chapter.title;
                                if (chapterName.includes(':')) {
                                    chapterName = chapterName.trim();
                                }
                                message = message.concat(`<span class="short-chapters-warning"> - ${chapterName}: có ${chapter.length.toLocaleString()} ký tự</span>\n`);
                            });
                        }
                        if (navigator.clipboard && navigator.clipboard.writeText) {
                            navigator.clipboard.writeText(clipboardContent)
                                .then(() => {
                                    debugOutput.push(`📋Đã lưu lại ${remainingChapters.length} Chương\n`);
                                    this.showNotification(message, remainingChapters.length > 0 ? 'warning' : 'success');
                                })
                                .catch(err => {
                                    throw err;
                                });
                        } else {
                            const tempTextarea = document.createElement('textarea');
                            tempTextarea.style.position = 'fixed';
                            tempTextarea.style.top = '0';
                            tempTextarea.style.left = '0';
                            tempTextarea.style.width = '2em';
                            tempTextarea.style.height = '2em';
                            tempTextarea.style.opacity = '0';
                            tempTextarea.style.pointerEvents = 'none';
                            tempTextarea.value = clipboardContent;
                            document.body.appendChild(tempTextarea);
                            tempTextarea.focus();
                            tempTextarea.select();
                            const successful = document.execCommand('copy');
                            document.body.removeChild(tempTextarea);
                            if (!successful) {
                                throw new Error('Không thể sao chép vào clipboard');
                            }
                            debugOutput.push(`Đã sao chép ${remainingChapters.length} chương vào clipboard (execCommand)`);
                            this.showNotification(message, 'success');
                        }
                    } catch (err) {
                        console.error('Lỗi khi sao chép vào clipboard:', err);
                        debugOutput.push(`Lỗi khi sao chép vào clipboard: ${err.message}`);
                        this.showNotification('Không thể sao chép vào clipboard. Vui lòng thử lại.', 'error');
                        const manualCopyArea = document.createElement('div');
                        manualCopyArea.style.position = 'fixed';
                        manualCopyArea.style.top = '50%';
                        manualCopyArea.style.left = '50%';
                        manualCopyArea.style.transform = 'translate(-50%, -50%)';
                        manualCopyArea.style.backgroundColor = 'white';
                        manualCopyArea.style.padding = '20px';
                        manualCopyArea.style.borderRadius = '8px';
                        manualCopyArea.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1)';
                        manualCopyArea.style.zIndex = '10000';
                        manualCopyArea.style.maxWidth = '80%';
                        manualCopyArea.style.maxHeight = '80%';
                        manualCopyArea.style.overflow = 'auto';
                        manualCopyArea.innerHTML = `
                            <h3 style="margin-top: 0;">Sao chép thủ công</h3>
                            <p>Không thể sao chép tự động. Vui lòng chọn toàn bộ nội dung bên dưới và sao chép (Ctrl+C):</p>
                            <textarea style="width: 100%; height: 300px; padding: 10px;">${clipboardContent}</textarea>
                            <div style="text-align: right; margin-top: 10px;">
                                <button id="closeManualCopy" style="padding: 8px 16px; background: #3b82f6; color: white; border: none; border-radius: 4px; cursor: pointer;">Đóng</button>
                            </div>
                        `;
                        document.body.appendChild(manualCopyArea);
                        document.getElementById('closeManualCopy').addEventListener('click', () => {
                            document.body.removeChild(manualCopyArea);
                        });
                    }
                }
                jQuery('#qpButtonSubmit').removeClass("btn-disable").addClass("btn-success");
                jQuery('#ttv-content').val("Đã xử lý xong");
                if (remainingChapters.length === 0) {
                    let splitChapters = 0;
                    let shortChapters = 0;
                    let shortChapterDetails = [];
                    let longChapterDetails = [];
                    for (let i = 0; i < chapters.length; i++) {
                        const chapterLines = chapters[i].split('\n');
                        const title = chapterLines.shift().trim();
                        const chapterText = chapterLines.join('\n');
                        if (chapterText.length > 40000) {
                            splitChapters++;
                            const partsCount = Math.ceil(chapterText.length / 40000);
                            longChapterDetails.push({
                                title: title,
                                parts: partsCount
                            });
                        }
                        if (chapterText.length < 3000) {
                            shortChapters++;
                            shortChapterDetails.push({
                                title: title,
                                length: chapterText.length
                            });
                        }
                    }
                    const splittedChaptersCount = processedChapters.length - (chapters.length - splitChapters);
                    let message = '';
                    message = message.concat(`📝Đã xử lý ${processedChapters.length} Chương\n`);
                    message = message.concat(`📝Đã nhập ${Math.min(processedChapters.length, MAX_CHAPTER_POST)} Chương\n`);
                    if (splitChapters > 0) {
                        message = message.concat(`📑Có ${splitChapters} Chương dài chia thành ${splittedChaptersCount} Chương\n`);
                        longChapterDetails.forEach(chapter => {
                            let chapterName = chapter.title;
                            if (chapterName.includes(':')) {
                                chapterName = chapterName.trim();
                            }
                            message = message.concat(` - ${chapterName}: ${chapter.parts} Chương\n`);
                        });
                    }
                    if (shortChapters > 0) {
                        message = message.concat(`⚠️<span class="short-chapters-warning">Có ${shortChapters} chương dưới 3000 ký tự</span>\n`);
                        shortChapterDetails.forEach(chapter => {
                            let chapterName = chapter.title;
                            if (chapterName.includes(':')) {
                                chapterName = chapterName.trim();
                            }
                            message = message.concat(`<span class="short-chapters-warning"> - ${chapterName}: có ${chapter.length.toLocaleString()} ký tự</span>\n`);
                        });
                    }
                    this.showNotification(message, 'success');
                }
                jQuery('#debug-output').text(debugOutput.join('\n'));
                return processedChapters.length;
            } catch (e) {
                console.error("Error in performAction:", e);
                this.showNotification('Có lỗi khi tách chương', 'error');
                return 0;
            }
        },
        removeEmptyChapters: function() {
            const forms = document.querySelectorAll('[data-gen="MK_GEN"]');
            let removed = 0;

            forms.forEach(form => {
                const content = form.querySelector('textarea[name^="introduce"]').value.trim();
                if (!content) {
                    form.remove();
                    removed++;
                    this.updateChapNumber(false);
                }
            });
            this.showNotification(`Đã xử lý ${forms.length} chương`, 'info');
        },
        toggleAutoPost: function() {
            this.STATE.isAuto = !this.STATE.isAuto;

            if (this.STATE.isAuto) {
                this.STATE.totalChapters = parseInt(prompt("Nhập tổng số lần tự động đăng:", "10")) || 0;
                this.STATE.processedChapters = parseInt(localStorage.getItem('TTV_POSTED_CHAPTERS') || '0');

                localStorage.setItem('TTV_TOTAL_CHAPTERS', this.STATE.totalChapters.toString());

                this.updateStats();
                this.showNotification(`Đã bật tự động đăng (${this.STATE.processedChapters}/${this.STATE.totalChapters})`, 'success');

                if (window.location.href.includes('/dang-chuong/story/')) {
                    setTimeout(() => this.runAutoPostSequence(), 2000);
                }
            } else {
                localStorage.setItem('TTV_AUTO_POST', 'false');
                this.STATE.processedChapters = 0;
                localStorage.setItem('TTV_POSTED_CHAPTERS', '0');
                this.STATE.totalChapters = 0;
                localStorage.setItem('TTV_TOTAL_CHAPTERS', '0');
                this.updateStats();
                this.showNotification('Đã tắt tự động đăng và reset số lần đăng', 'info');
            }
        },

        runAutoPostSequence: function() {
            if (this.STATE.processedChapters >= this.STATE.totalChapters) {
                this.STATE.isAuto = false;
                localStorage.setItem('TTV_AUTO_POST', 'false');
                this.STATE.processedChapters = 0;
                localStorage.setItem('TTV_POSTED_CHAPTERS', '0');
                this.STATE.totalChapters = 0;
                localStorage.setItem('TTV_TOTAL_CHAPTERS', '0');
                this.updateStats();
                this.showNotification(`Đã hoàn thành tự động đăng ${this.STATE.totalChapters}/${this.STATE.totalChapters} chương và đã reset số lần đăng`, 'success');
                return;
            }

            if (!this.STATE.isAuto) {
                return;
            }

            setTimeout(() => {
                if (this.STATE.isAuto) {
                    this.handlePasteButton();
                    setTimeout(() => {
                        if (this.STATE.isAuto) {
                            this.submitChapters();
                        }
                    }, 3000);
                }
            }, 2000);
        },

        submitChapters: function() {
            if (!this.validateChapterLengths()) {
                return;
            }
            this.showLoading();
            document.querySelector('form[name="postChapForm"] button[type="submit"]').click();

            if (this.STATE.isAuto) {
                this.STATE.processedChapters++;
                localStorage.setItem('TTV_POSTED_CHAPTERS', this.STATE.processedChapters.toString());

                if (this.STATE.processedChapters >= this.STATE.totalChapters) {
                    this.STATE.isAuto = false;
                    localStorage.setItem('TTV_AUTO_POST', 'false');
                    this.STATE.processedChapters = 0;
                    localStorage.setItem('TTV_POSTED_CHAPTERS', '0');
                    this.STATE.totalChapters = 0;
                    localStorage.setItem('TTV_TOTAL_CHAPTERS', '0');
                    this.updateStats();
                    setTimeout(() => {
                        this.showNotification(`Đã hoàn thành tự động đăng ${this.STATE.totalChapters}/${this.STATE.totalChapters} chương và đã reset số lần đăng`, 'success');
                    }, 3000);
                }
            }

            setTimeout(() => this.hideLoading(), 2000);
        },
        addNewChapter: function() {
            if ((this.STATE.chapterNumber + 1) <= MAX_CHAPTER_POST) {
                this.updateChapNumber(true);
                const html = this.createChapterHTML(this.STATE.chapterNumber);
                jQuery('#add-chap').before(html);
                this.setupCharacterCounter();
            } else {
                this.showNotification(`Chỉ có thể đăng tối đa ${MAX_CHAPTER_POST} chương một lần`, 'warning');
            }
        },

        createFormContainer: function() {
            const container = document.createElement('div');
            container.id = 'ttv-chapter-forms';
            document.body.appendChild(container);
        },

        resetAutoPost: function() {
            this.STATE.totalChapters = 0;
            this.STATE.processedChapters = 0;
            localStorage.removeItem('TTV_TOTAL_CHAPTERS');
            localStorage.removeItem('TTV_POSTED_CHAPTERS');
            this.updateStats();
            this.showNotification('Đã reset số lần tự động đăng', 'info');
        },
        toggleAutoMode: function() {
            const isAutoMode = jQuery('#qpOptionLoop').is(':checked');

            if (isAutoMode) {
                jQuery('#qpButtonAutoPost').show();
                jQuery('#qpButtonReset').show();
                jQuery('#qpButtonPaste').hide();
                jQuery('#qpButtonSubmit').hide();

                this.showNotification('Đã bật chế độ tự động', 'info');
            } else {
                jQuery('#qpButtonAutoPost').hide();
                jQuery('#qpButtonReset').hide();
                jQuery('#qpButtonPaste').show();
                jQuery('#qpButtonSubmit').show();

                this.showNotification('Đã tắt chế độ tự động', 'info');
            }

            localStorage.setItem('TTV_AUTO_MODE', isAutoMode.toString());
        }
    };

    const MAX_CHAPTER_POST = 10;
    const HEADER_SIGN = "/***";
    const FOOTER_SIGN = "***/";

    TTVManager.init();
})();