您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Đăng truyện nhanh TTV với nhiều tính năng mới và tự động tách chương - phiên bản tối ưu
// ==UserScript== // @name Đăng Nhanh TTV v2 // @namespace http://tampermonkey.net/ // @version 1.9 // @description Đăng truyện nhanh TTV với nhiều tính năng mới và tự động tách chương - phiên bản tối ưu // @author HA // @match https://tangthuvien.net/dang-chuong/story/* // @match https://tangthuvien.net/danh-sach-chuong/story/* // @grant none // @required https://code.jquery.com/jquery-3.2.1.min.js // @icon https://tangthuvien.net/images/icon-favico.png // @copyright 2021-2023, by HA // @license AGPL-3.0-only // @collaborator HA // ==/UserScript== (function() { 'use strict'; const HEADER_SIGN = ""; const FOOTER_SIGN = ""; const dăngnhanhTTV = { CONFIG: { YEAR_ALIVE: 2021, MAX_CHAPTER_POST: 10, MIN_CHARACTERS: 3000, MAX_CHARACTERS: 20000, SAFE_CHARACTERS: 15000 }, STATE: { CHAP_NUMBER: 1, CHAP_STT: 1, CHAP_SERIAL: 1, CHAP_NUMBER_ORIGINAL: 1, CHAP_STT_ORIGINAL: 1, CHAP_SERIAL_ORIGINAL: 1 }, ELEMENTS: { qpContent: null, qpButtonSubmit: null, qpButtonRemoveEmpty: null, qpn: null, characterCount: null }, init: function() { try { const me = this; if (this.handleRedirects()) { return; } this.initializeChapterValues(); this.createInterface(); this.addStyles(); this.cacheElements(); this.registerEvents(); this.createListAddChapter(true); jQuery('#qpNumberOfChapter').attr('max', me.CONFIG.MAX_CHAPTER_POST); this.ELEMENTS.qpContent.focus(); } catch (e) { console.error("Lỗi khởi tạo:", e); } }, handleRedirects: function() { if (window.location.href.includes('tangthuvien.net/message/successchapter')) { setTimeout(function() { const previousUrl = document.referrer; if (previousUrl.includes('/dang-chuong/story/')) { const matches = previousUrl.match(/\/dang-chuong\/story\/(\d+)/); if (matches && matches[1]) { window.location.href = 'https://tangthuvien.net/dang-chuong/story/' + matches[1]; } } }, 3000); return true; } else if (window.location.href.includes('/story/') && window.location.href.includes('/danh-sach-chuong')) { setTimeout(function() { const matches = window.location.href.match(/\/story\/(\d+)/); if (matches && matches[1]) { window.location.href = 'https://tangthuvien.net/dang-chuong/story/' + matches[1]; } }, 3000); return true; } return false; }, cacheElements: function() { this.ELEMENTS.qpContent = jQuery("#qpContent"); this.ELEMENTS.qpButtonSubmit = jQuery("#qpButtonSubmit"); this.ELEMENTS.qpButtonRemoveEmpty = jQuery("#qpButtonRemoveEmpty"); this.ELEMENTS.qpn = jQuery("#qpn"); this.ELEMENTS.characterCount = jQuery(".character-count"); }, initializeChapterValues: function() { 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.CHAP_NUMBER = this.STATE.CHAP_NUMBER_ORIGINAL = chap_number; this.STATE.CHAP_STT = this.STATE.CHAP_STT_ORIGINAL = chap_stt; this.STATE.CHAP_SERIAL = this.STATE.CHAP_SERIAL_ORIGINAL = chap_serial; }, addNewChapter: function() { const me = this; if ((me.STATE.CHAP_NUMBER + 1) <= me.CONFIG.MAX_CHAPTER_POST) { me.updateChapNumber(true); const chap_vol = parseInt(jQuery('.chap_vol').val()); const chap_vol_name = jQuery('.chap_vol_name').val(); const html = ` <div data-gen="MK_GEN" id="COUNT_CHAP_${me.STATE.CHAP_NUMBER}_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[${me.STATE.CHAP_NUMBER}]" value="${me.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="${me.STATE.CHAP_SERIAL}" required class="form-control" name="chap_number[${me.STATE.CHAP_NUMBER}]" 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[${me.STATE.CHAP_NUMBER}]" 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[${me.STATE.CHAP_NUMBER}]" 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[${me.STATE.CHAP_NUMBER}]" 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[${me.STATE.CHAP_NUMBER}]" rows="20" placeholder="Nội dung" type="text"></textarea> </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[${me.STATE.CHAP_NUMBER}]" placeholder="Quảng cáo" type="text"></textarea> </div> </div> </div>`; jQuery('#add-chap').before(html); } else { const chapterLeft = Math.max(0, me.CONFIG.MAX_CHAPTER_POST - me.STATE.CHAP_NUMBER); alert(`Bạn nên đăng tối đa ${me.CONFIG.MAX_CHAPTER_POST} chương một lần, số chương đã tạo ${me.STATE.CHAP_NUMBER} chương, bạn có thể đăng thêm ${chapterLeft} chương nữa.`); } }, createListAddChapter: function(isSilent) { const me = this; const chapterAdd = parseInt(jQuery("#qpNumberOfChapter").val()); if ((me.STATE.CHAP_NUMBER + chapterAdd) <= me.CONFIG.MAX_CHAPTER_POST) { for (let i = 0; i < chapterAdd; i++) { me.addNewChapter(); } if (!isSilent) { alert(`Đã tạo thêm ${chapterAdd} chương, hãy copy và dán nội dung cần đăng.`); } } else { alert(`Bạn nên đăng tối đa ${me.CONFIG.MAX_CHAPTER_POST} chương một lần, số chương đã tạo ${me.STATE.CHAP_NUMBER} chương, bạn có thể đăng thêm ${me.CONFIG.MAX_CHAPTER_POST - me.STATE.CHAP_NUMBER} chương nữa.`); } }, removeLastedPost: function() { const me = this; jQuery(`#COUNT_CHAP_${me.STATE.CHAP_NUMBER}_MK`).remove(); me.updateChapNumber(); }, updateChapNumber: function(isAdd) { const me = this; try { if (isAdd) { 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()); } me.STATE.CHAP_STT = chap_stt; me.STATE.CHAP_SERIAL = chap_serial; me.STATE.CHAP_NUMBER++; me.STATE.CHAP_STT++; me.STATE.CHAP_SERIAL++; } else { if (me.STATE.CHAP_NUMBER > me.STATE.CHAP_NUMBER_ORIGINAL) { me.STATE.CHAP_NUMBER--; } if (me.STATE.CHAP_STT > me.STATE.CHAP_STT_ORIGINAL) { me.STATE.CHAP_STT--; } if (me.STATE.CHAP_SERIAL > me.STATE.CHAP_SERIAL_ORIGINAL) { me.STATE.CHAP_SERIAL--; } } jQuery('#chap_number').val(me.STATE.CHAP_NUMBER); jQuery('#chap_stt').val(me.STATE.CHAP_STT); jQuery('#chap_serial').val(me.STATE.CHAP_SERIAL); jQuery('#countNumberPost').text(me.STATE.CHAP_NUMBER); } catch (e) { console.error("Lỗi cập nhật số chương:", e); } }, createInterface: function() { const html = ` <div id="HA"> <div class="form-group"></div> <center> <h3 style="color: #4285f4; margin-bottom: 15px; font-weight: 600;">📝 CÔNG CỤ ĐĂNG NHANH</h3> </center> <center> <h4><span id="short-chapter-warning" style="display:none; color:#ea4335; padding: 8px; background-color: #ffebee; border-radius: 6px;"></span></h4> </center> <div class="form-group"> <textarea placeholder="Nội dung truyện (Dán vào đây để tự động tách chương)" id="qpContent" class="form-control" rows="5"></textarea> <div class="character-count" style="text-align: right; font-size: 11px; color: #666; margin-top: 3px;">Số ký tự: 0</div> <div class="form-group" id="qpAdv" class="form-control" rows="2"></div> <div class="form-group" style="display: flex; justify-content: space-between; margin-top: 15px;"> <div> <button type="button" id="qpButtonSubmit" class="btn btn-success">📤 Đăng chương</button> </div> <div> <button type="button" id="qpButtonRemoveEmpty" class="btn btn-danger" style="display: none;">Xóa chương trống</button> <button type="button" id="qpButtonAddEmpty" class="btn btn-outline">Xóa chương trống</button> </div> </div> <div class="form-group"> <span id="qpn" style="margin:0px 15px;font-weight:500"></span> </div> </div> <div class="hidden-config-section" style="display:none;height:0;overflow:hidden;visibility:hidden;"> <h4></h4> <div class="form-group"> <input type="text" id="qpSplitValue" class="form-control" value="/[c|C]hương\\s?\\d+\\s?:?\\s?/"> </div> <div class="form-group" id="qpSplitValueReplace"></div> <div class="form-group"> <input type="number" placeholder="Thêm" value="9" id="qpNumberOfChapter" class="form-control"> </div> </div> </div>`; jQuery(".list-in-user").before(html); }, addStyles: function() { const style = document.createElement('style'); style.textContent = ` #HA { background-color: #ffffff !important; padding: 20px; color: #333333 !important; border-radius: 12px; margin-bottom: 15px; position: fixed; right: 20px; top: 50%; transform: translateY(-50%); max-width: 400px; z-index: 9999; box-shadow: 0 4px 20px rgba(0,0,0,0.15); font-family: "Segoe UI", Roboto, Arial, sans-serif; transition: all 0.3s ease; } #HA > .form-group > div { font-size: 14px; color: #333333 !important; } #HA > p { font-size: 14px; color: #333333 !important; text-align: right; } #HA .HA-option { padding: 10px; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 15px; background-color: #f8f9fa; } #HA .HA-option-label { width: 100%; background-color: #4285f4; color: white; padding: 10px; border-radius: 6px; margin: 0 0 10px 0; font-weight: 500; } #qpn { max-height: 300px; overflow-y: auto; margin-top: 15px; font-size: 13px; padding: 10px; background-color: #f5f8ff; border-radius: 8px; } .hidden-element, .hidden-config-section { display: none !important; height: 0 !important; overflow: hidden !important; visibility: hidden !important; padding: 0 !important; margin: 0 !important; position: absolute !important; left: -9999px !important; } @keyframes blink-red { 0% { box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); } 50% { box-shadow: 0 0 15px rgba(255, 0, 0, 0.8); } 100% { box-shadow: 0 0 5px rgba(255, 0, 0, 0.5); } } textarea[style*='border: 3px solid red'] { animation: blink-red 2s infinite; border: 2px solid #ea4335 !important; border-radius: 8px !important; background-color: rgba(255, 235, 238, 0.2) !important; } #short-chapter-warning { animation: blink-red 2s infinite; display: inline-block; padding: 6px 10px; border-radius: 4px; font-size: 12px; } .form-control { border: 1px solid #e0e0e0; border-radius: 6px; padding: 10px; font-size: 14px; transition: all 0.3s ease; } .form-control:focus { border-color: #4285f4; box-shadow: 0 0 5px rgba(66, 133, 244, 0.3); outline: none; } .character-count, .chapter-character-count { text-align: right; font-size: 14px; color: #666; margin-top: 5px; padding-right: 8px; font-family: "Segoe UI", Roboto, Arial, sans-serif; font-weight: 600; } .btn { padding: 8px 16px; border-radius: 6px; font-weight: 500; cursor: pointer; transition: all 0.2s ease; border: none; font-size: 14px; margin: 5px; } .btn-success { background-color: #4285f4; color: white; } .btn-success:hover { background-color: #3367d6; } .btn-danger { background-color: #f5f5f5; color: #333333; border: 1px solid #ccc; font-weight: normal; } .btn-danger:hover { background-color: #e9e9e9; color: #333333; border: 1px solid #bbb; } .btn-default { background-color: #f1f3f4; color: #333333; } .btn-default:hover { background-color: #e8eaed; } /* Styles for summary sections */ .summary-section { padding: 6px 8px; margin-bottom: 5px; border-radius: 4px; background-color: #ffffff; border-left: 3px solid #4285f4; box-shadow: 0 1px 2px rgba(0,0,0,0.05); font-size: 12px; } .summary-section.warning { border-left-color: #fbbc05; background-color: #fffde7; } .summary-section.error { border-left-color: #ea4335; background-color: #fef2f2; } .summary-label { font-weight: 600; color: #202124; display: inline-block; min-width: 110px; font-size: 12px; } .summary-value { color: #3c4043; font-weight: 500; font-size: 12px; } .summary-details { margin: 2px 0 2px 8px; padding: 2px 0 2px 6px; border-left: 2px solid #e0e0e0; font-size: 11px; } .detail-item { padding: 1px 0; color: #5f6368; font-size: 11px; } #qpn { max-height: 250px; overflow-y: auto; margin-top: 10px; font-size: 12px; padding: 8px; background-color: #f5f8ff; border-radius: 6px; } `; document.head.appendChild(style); setTimeout(function() { const elementsToHide = [ document.querySelector('.hidden-config-section'), document.getElementById('qpSplitValue'), document.getElementById('qpSplitValueReplace'), document.getElementById('qpNumberOfChapter') ]; elementsToHide.forEach(function(el) { if (el) { el.style.display = 'none'; el.style.visibility = 'hidden'; el.style.height = '0px'; el.style.overflow = 'hidden'; el.style.position = 'absolute'; el.style.left = '-9999px'; el.setAttribute('aria-hidden', 'true'); } }); }, 100); }, registerEvents: function() { const me = this; this.ELEMENTS.qpContent.on("input", function() { me.updateCharacterCount(jQuery(this).val().length, me.ELEMENTS.characterCount); }); jQuery(document).on("input", "[name^=introduce]", function() { const text = jQuery(this).val().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim(); const charCount = text.length; let charCountElement = jQuery(this).next('.chapter-character-count'); if (charCountElement.length === 0) { charCountElement = jQuery('<div class="chapter-character-count" style="text-align: right; font-size: 11px; margin-top: 3px; padding-right: 5px;"></div>'); jQuery(this).after(charCountElement); } me.updateCharacterCount(charCount, charCountElement); }); this.ELEMENTS.qpContent.on("paste", function(e) { jQuery(this).val(""); me.showProcessingNotification(); setTimeout(function() { const text = me.ELEMENTS.qpContent.val(); me.updateCharacterCount(text.length, me.ELEMENTS.characterCount); }, 100); setTimeout(function() { me.splitChapters(); jQuery("#processing-notification").fadeOut(300, function() { jQuery(this).remove(); }); me.showCompletionNotification(); }, 500); }); jQuery('#qpButtonRemoveEmpty').on('click', function(e) { e.preventDefault(); me.removeEmptyList(); }); jQuery('#qpButtonAddEmpty').on('click', function(e) { e.preventDefault(); me.addNewChapter(); }); this.ELEMENTS.qpButtonSubmit.on('click', function(e) { e.preventDefault(); const titles = jQuery("[name^=chap_name]"); const contents = jQuery("[name^=introduce]"); const advs = jQuery("[name^=adv]"); const advContent = jQuery("#qpAdv").val(); const st = jQuery('#qpSplitValueReplace').val().trim(); me.fillEmptyForms(titles, contents, advs, advContent, st); jQuery('form button[type=submit]')[0].click(); }); }, showProcessingNotification: function() { const processingMsg = jQuery("<div>", { id: "processing-notification", text: "Đang xử lý tách chương...", css: { position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)", backgroundColor: "rgba(0, 0, 0, 0.8)", color: "white", padding: "15px 25px", borderRadius: "8px", zIndex: "10000", fontSize: "16px", fontWeight: "500", boxShadow: "0 4px 15px rgba(0,0,0,0.2)" } }); jQuery("body").append(processingMsg); }, showCompletionNotification: function() { const completeMsg = jQuery("<div>", { id: "complete-notification", html: "<span style='font-size:18px;margin-right:8px;'>✅</span> Đã tách chương tự động!", css: { position: "fixed", top: "20px", right: "20px", backgroundColor: "#4CAF50", color: "white", padding: "12px 20px", borderRadius: "8px", zIndex: "10000", fontSize: "14px", fontWeight: "500", boxShadow: "0 4px 10px rgba(0,0,0,0.15)", display: "flex", alignItems: "center" } }); jQuery("body").append(completeMsg); completeMsg.fadeIn(300).delay(2000).fadeOut(500, function() { jQuery(this).remove(); }); }, updateCharacterCount: function(charCount, element) { if (element === this.ELEMENTS.characterCount) { element.html("<strong style='font-size: 14px;'>Số ký tự: " + charCount.toLocaleString('vi-VN') + "</strong>"); } else { element.html("<strong style='font-size: 14px;'>Số ký tự: " + charCount.toLocaleString('vi-VN') + "/20.000</strong>"); } if (charCount < this.CONFIG.MIN_CHARACTERS) { element.css("color", "#ea4335"); } else if (charCount > this.CONFIG.MAX_CHARACTERS) { element.css("color", "#fbbc05"); } else { element.css("color", "#34a853"); } }, ucFirst: function(str) { if (str && typeof str === 'string' && str.length > 0) { return str.charAt(0).toUpperCase() + str.slice(1); } return str; }, copyToClipboard: function(text) { navigator.clipboard.writeText(text).catch(err => { const textarea = document.createElement('textarea'); textarea.value = text; textarea.setAttribute('readonly', ''); textarea.style.position = 'absolute'; textarea.style.left = '-9999px'; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); }); }, removeEmptyList: function() { try { const me = this; const titles = jQuery("[name^=chap_name]"); const contents = jQuery("[name^=introduce]"); let count = 0; this.ELEMENTS.qpn.html(''); if (titles && titles.length) { for (let i = 0; i < titles.length; i++) { const t = titles[i]; const c = contents[i]; if (t && (!t.value || (c && !c.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) { if (t.parentElement.parentElement.parentElement.tagName != 'FORM') { t.parentElement.parentElement.parentElement.remove(); } me.updateChapNumber(); count++; } } } alert('Đã loại bỏ ' + count + ' chương trống. Đã có thể nhấn Đăng chương'); const updatedTitles = jQuery("[name^=chap_name]"); const updatedContents = jQuery("[name^=introduce]"); const updatedAdvs = jQuery("[name^=adv]"); const advContent = jQuery("#qpAdv").val(); const st = jQuery('#qpSplitValueReplace').val().trim(); let stillHasEmptyChapters = false; for (let i = 0; i < updatedTitles.length; i++) { const t = updatedTitles[i]; const c = updatedContents[i]; if (t && (!t.value || (c && !c.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) { stillHasEmptyChapters = true; break; } } if (stillHasEmptyChapters) { this.ELEMENTS.qpButtonRemoveEmpty.show(); jQuery('#qpButtonAddEmpty').hide(); } else { this.ELEMENTS.qpButtonRemoveEmpty.hide(); jQuery('#qpButtonAddEmpty').show(); } me.fillEmptyForms(updatedTitles, updatedContents, updatedAdvs, advContent, st); } catch (e) { console.error("Lỗi khi xóa chương trống:", e); } }, fillEmptyForms: function(titles, contents, advs, advContent, st) { try { const maxIterations = 3; let iteration = 0; let hasEmptyForms = true; while (hasEmptyForms && iteration < maxIterations) { hasEmptyForms = false; for (let i = 0; i < titles.length; i++) { const currentTitle = titles[i]; const currentContent = contents[i]; if (currentTitle && (!currentTitle.value || (currentContent && !currentContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) { hasEmptyForms = true; let foundNextValid = false; for (let j = i + 1; j < titles.length; j++) { const nextTitle = titles[j]; const nextContent = contents[j]; if (nextTitle && nextTitle.value && nextContent && nextContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()) { currentTitle.value = nextTitle.value; currentContent.value = nextContent.value; if (advs && advs[i]) { advs[i].value = advs[j] ? advs[j].value : advContent; } nextTitle.value = ""; nextContent.value = ""; if (advs && advs[j]) { advs[j].value = ""; } foundNextValid = true; break; } } } } iteration++; } let emptyFormCount = 0; for (let i = 0; i < titles.length; i++) { const currentTitle = titles[i]; const currentContent = contents[i]; if (currentTitle && (!currentTitle.value || (currentContent && !currentContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) { emptyFormCount++; } } if (emptyFormCount > 0) { this.ELEMENTS.qpn.html(this.ELEMENTS.qpn.html() + `<br><b style='color:red'>Cảnh báo: Vẫn còn ${emptyFormCount} chương trống.</b>`); this.ELEMENTS.qpButtonRemoveEmpty.show(); jQuery('#qpButtonAddEmpty').hide(); } else { this.ELEMENTS.qpButtonRemoveEmpty.hide(); jQuery('#qpButtonAddEmpty').show(); } } catch (e) { console.error("Lỗi khi đẩy chương lên form trống:", e); } }, splitChapters: function() { try { const me = this; window.shortChapterNotified = false; const warningEl = document.getElementById('short-chapter-warning'); if (warningEl) { warningEl.style.display = 'none'; } const mainContentText = this.ELEMENTS.qpContent.val(); const mainContentLength = mainContentText.length; this.updateCharacterCount(mainContentLength, this.ELEMENTS.characterCount); const content = this.ELEMENTS.qpContent.val(); const st = jQuery('#qpSplitValueReplace').val().trim(); const advContent = jQuery("#qpAdv").val(); const lines = content.split('\n'); const chapters = []; let currentChapter = []; const regex = /^\s*[Cc]hương\s*\d+\s*:/; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.match(regex)) { if (currentChapter.length > 0) { chapters.push(currentChapter.join('\n')); currentChapter = []; } currentChapter.push(line); } else { currentChapter.push(line); } } if (currentChapter.length > 0) { chapters.push(currentChapter.join('\n')); } const filteredChapters = chapters.filter(function(chapter) { const lines = chapter.split('\n'); return lines.some(function(line, index) { return index > 0 && line.trim().length > 0; }); }); let i = 0; const processedChapters = []; while (i < filteredChapters.length) { if (filteredChapters[i].length > this.CONFIG.MAX_CHARACTERS) { const largeChapter = filteredChapters[i]; const lines = largeChapter.split('\n'); const chapterTitle = lines.shift(); const contentToSplit = lines.join('\n'); const contentLength = contentToSplit.length; const numSubChapters = Math.ceil(contentLength / this.CONFIG.SAFE_CHARACTERS); const chunkSize = Math.floor(contentLength / numSubChapters); let startPos = 0; for (let j = 0; j < numSubChapters; j++) { let endPos = (j === numSubChapters - 1) ? contentLength : startPos + chunkSize; if (j < numSubChapters - 1) { const lookAhead = Math.min(200, contentLength - endPos); const segment = contentToSplit.substring(endPos, endPos + lookAhead); const sentenceEnd = segment.search(/[.!?][^.!?]*$/); if (sentenceEnd !== -1) { endPos += sentenceEnd + 1; } } const subContent = contentToSplit.substring(startPos, endPos); const subTitle = `${chapterTitle.trim()} (Phần ${j + 1}/${numSubChapters})`; processedChapters.push(subTitle + "\n" + subContent); startPos = endPos; } } else { processedChapters.push(filteredChapters[i]); } i++; } const titles = jQuery("[name^=chap_name]"); const contents = jQuery("[name^=introduce]"); const advs = jQuery("[name^=adv]"); let validChapters = 0; const shortChapters = []; const splitChapterGroups = []; const duplicateTitles = []; const titleMap = {}; for (let i = 0; i < Math.min(processedChapters.length, titles.length); i++) { const chapterLines = processedChapters[i].split('\n'); const title = chapterLines.shift().trim(); const content = chapterLines.join('\n'); if (content) { validChapters++; let processedTitle = title; if (title.includes(':')) { processedTitle = title.split(':').slice(1).join(':').trim(); } processedTitle = this.ucFirst(processedTitle); if (processedTitle.toLowerCase().startsWith(st.toLowerCase())) { titles[i].value = processedTitle; } else { titles[i].value = st + " " + processedTitle; } contents[i].value = HEADER_SIGN + "\r\n" + content + "\r\n" + FOOTER_SIGN; advs[i].value = advContent; const contentWithoutEmpty = content.split('\n') .filter(line => line.trim().length > 0) .join('\n'); const charCount = contentWithoutEmpty.length; let charCountElement = jQuery(contents[i]).next('.chapter-character-count'); if (charCountElement.length === 0) { charCountElement = jQuery('<div class="chapter-character-count" style="text-align: right; font-size: 11px; margin-top: 3px; padding-right: 5px;"></div>'); jQuery(contents[i]).after(charCountElement); } this.updateCharacterCount(charCount, charCountElement); if (charCount < this.CONFIG.MIN_CHARACTERS) { contents[i].style.border = '3px solid red'; shortChapters.push(title); } const titleText = titles[i].value.toLowerCase(); if (titleMap[titleText]) { duplicateTitles.push(titles[i].value); } else { titleMap[titleText] = true; } if (title.includes(" (Phần ")) { const parts = title.match(/\(Phần (\d+)\/(\d+)\)/); if (parts && parts[2] && parts[1] === "1") { const chapterName = title.replace(/\s*\(Phần \d+\/\d+\)/, ""); splitChapterGroups.push({ name: chapterName, totalParts: parseInt(parts[2]) }); } } } } const totalProcessedChapters = processedChapters.length; let splitChaptersCount = 0; let largeChapterCount = 0; let totalParts = 0; for (let i = 0; i < processedChapters.length; i++) { const firstLine = processedChapters[i].split('\n')[0]; if (firstLine && firstLine.includes(" (Phần ")) { splitChaptersCount++; const parts = firstLine.match(/\(Phần (\d+)\/(\d+)\)/); if (parts && parts[2]) { const totalPartsInChapter = parseInt(parts[2]); if (parts[1] === "1") { largeChapterCount++; totalParts += totalPartsInChapter; } } } } if (processedChapters.length > titles.length) { const remainingContent = processedChapters.slice(titles.length).join("\n\n"); this.copyToClipboard(remainingContent); } let detailedSummary = ""; detailedSummary += `<div class='summary-section' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Tổng số chương:</span> <span class='summary-value'>${totalProcessedChapters}</span></div>`; detailedSummary += `<div class='summary-section' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Đã xử lý:</span> <span class='summary-value'>${Math.min(processedChapters.length, titles.length)} chương</span></div>`; if (processedChapters.length > titles.length) { detailedSummary += `<div class='summary-section' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Đã lưu vào Clipboard:</span> <span class='summary-value'>${processedChapters.length - titles.length} chương</span></div>`; } if (largeChapterCount > 0) { detailedSummary += `<div class='summary-section warning' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Chương dài:</span> <span class='summary-value'>${largeChapterCount} chia thành ${totalParts} phần</span></div>`; if (splitChapterGroups.length > 0 && splitChapterGroups.length <= 3) { detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`; for (let i = 0; i < splitChapterGroups.length; i++) { const chapterMatch = splitChapterGroups[i].name.match(/[Cc]hương\s*(\d+):/); const chapterNum = chapterMatch ? chapterMatch[0] : "Chương"; detailedSummary += `<div class='detail-item' style='padding:1px 0'>${chapterNum} chia thành ${splitChapterGroups[i].totalParts} phần</div>`; } detailedSummary += `</div>`; } else if (splitChapterGroups.length > 3) { detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`; for (let i = 0; i < 3; i++) { const chapterMatch = splitChapterGroups[i].name.match(/[Cc]hương\s*(\d+):/); const chapterNum = chapterMatch ? chapterMatch[0] : "Chương"; detailedSummary += `<div class='detail-item' style='padding:1px 0'>${chapterNum} chia thành ${splitChapterGroups[i].totalParts} phần</div>`; } detailedSummary += `<div class='detail-item' style='padding:1px 0'>... và ${splitChapterGroups.length - 3} chương khác</div>`; detailedSummary += `</div>`; } } if (duplicateTitles.length > 0) { detailedSummary += `<div class='summary-section error' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Chương trùng lặp:</span> <span class='summary-value'>${duplicateTitles.length} chương</span></div>`; if (duplicateTitles.length <= 3) { detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`; for (let i = 0; i < duplicateTitles.length; i++) { detailedSummary += `<div class='detail-item' style='padding:1px 0'>${duplicateTitles[i]}</div>`; } detailedSummary += `</div>`; } else { detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`; for (let i = 0; i < 3; i++) { detailedSummary += `<div class='detail-item' style='padding:1px 0'>${duplicateTitles[i]}</div>`; } detailedSummary += `<div class='detail-item' style='padding:1px 0'>... và ${duplicateTitles.length - 3} chương khác</div>`; detailedSummary += `</div>`; } } if (shortChapters.length > 0) { detailedSummary += `<div class='summary-section error' style='font-size:12px;padding:6px 8px;margin-bottom:5px'><span class='summary-label'>Chương ngắn:</span> <span class='summary-value'>${shortChapters.length} chương</span></div>`; if (shortChapters.length <= 3) { detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`; for (let i = 0; i < shortChapters.length; i++) { detailedSummary += `<div class='detail-item' style='padding:1px 0'>${shortChapters[i]}</div>`; } detailedSummary += `</div>`; } else { detailedSummary += `<div class='summary-details' style='font-size:11px;margin:2px 0 2px 8px;padding:2px 0 2px 6px'>`; for (let i = 0; i < 3; i++) { detailedSummary += `<div class='detail-item' style='padding:1px 0'>${shortChapters[i]}</div>`; } detailedSummary += `<div class='detail-item' style='padding:1px 0'>... và ${shortChapters.length - 3} chương khác</div>`; detailedSummary += `</div>`; } } const mainSummary = `Đã xử lý ${totalProcessedChapters} chương\nĐã lấy ${Math.min(processedChapters.length, titles.length)} chương` + (processedChapters.length > titles.length ? `\nĐã lưu ${processedChapters.length - titles.length} chương vào Clipboard` : ''); this.ELEMENTS.qpContent.val(mainSummary); this.ELEMENTS.qpn.html(detailedSummary); this.ELEMENTS.qpButtonSubmit.removeClass("btn-disable").addClass("btn-success"); let emptyChaptersExist = false; for (let i = 0; i < titles.length; i++) { const currentTitle = titles[i]; const currentContent = contents[i]; if (currentTitle && (!currentTitle.value || (currentContent && !currentContent.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) { emptyChaptersExist = true; break; } } let hasEmptyChapters = false; for (let i = 0; i < titles.length; i++) { const t = titles[i]; const c = contents[i]; if (t && (!t.value || (c && !c.value.trim().replace(HEADER_SIGN, '').replace(FOOTER_SIGN, '').trim()))) { hasEmptyChapters = true; break; } } if (hasEmptyChapters) { me.ELEMENTS.qpButtonRemoveEmpty.show(); jQuery('#qpButtonAddEmpty').hide(); } else { me.ELEMENTS.qpButtonRemoveEmpty.hide(); jQuery('#qpButtonAddEmpty').show(); } } catch (e) { console.error("Lỗi khi tách chương:", e); this.ELEMENTS.qpn.html(`<div class='summary-section error'><span class='summary-label'>Lỗi:</span> <span class='summary-value'>${e}</span></div>`).addClass("text-danger"); } } }; dăngnhanhTTV.init(); })();