您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tự động điền form đăng chương trên tangthuvien.net với tính năng nâng cao
当前为
// ==UserScript== // @name TTV Auto Upload // @namespace http://tampermonkey.net/ // @version 2.8 // @description Tự động điền form đăng chương trên tangthuvien.net với tính năng nâng cao // @author HA // @match https://tangthuvien.net/dang-chuong/story/* // @grant none // ==/UserScript== (function() { 'use strict'; // CSS cho thông báo và control panel const style = document.createElement('style'); style.textContent = ` .ttv-notification { position: fixed; top: 20px; right: 20px; padding: 10px 20px; background: #4CAF50; color: white; border-radius: 4px; z-index: 9999; display: none; } .ttv-error { background: #f44336; } .ttv-control-panel { position: fixed; top: 50px; right: 20px; background: white; padding: 25px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); z-index: 9998; width: 500px; margin-bottom: 20px; transition: all 0.3s ease; } .ttv-control-panel.minimized { width: auto; height: auto; padding: 10px; opacity: 0.8; transform: translateX(calc(100% - 40px)); transition: all 0.3s ease; } .ttv-control-panel.minimized:hover { opacity: 1; transform: translateX(0); } .ttv-control-panel.minimized .ttv-button-group, .ttv-control-panel.minimized .ttv-header { display: none; } .ttv-button-group { display: flex; flex-direction: column; gap: 15px; } .ttv-content-editor { width: 100%; height: 100px; margin: 6px 0; padding: 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 13px; line-height: 1.4; resize: vertical; transition: all 0.2s ease; } .ttv-content-editor:focus { border-color: #5bc0de; outline: none; box-shadow: 0 0 8px rgba(91,192,222,0.2); } .ttv-preview { display: none; width: 100%; height: 100px; margin: 6px 0; padding: 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 13px; line-height: 1.4; overflow-y: auto; background: #f9f9f9; transition: all 0.2s ease; } .ttv-heading { font-size: 15px; color: #555; margin: 12px 0; display: flex; justify-content: space-between; align-items: center; } .ttv-word-count { font-size: 12px; color: #888; padding: 3px 8px; background: #f5f5f5; border-radius: 4px; transition: all 0.2s ease; } .ttv-chapter-list { width: 100%; margin: 10px 0; max-height: 200px; overflow-y: auto; border: 1px solid #eee; border-radius: 6px; padding: 8px; } .ttv-chapter-item { padding: 8px; border-bottom: 1px solid #eee; cursor: pointer; transition: all 0.2s ease; line-height: 1.4; font-size: 12px; color: #666; } .ttv-chapter-item .chapter-title { font-weight: bold; margin-bottom: 4px; } .ttv-chapter-item .chapter-name { color: #888; padding-left: 10px; border-left: 2px solid #ddd; margin: 4px 0; } .ttv-chapter-item .chapter-stats { font-size: 11px; color: #999; } .ttv-chapter-item:last-child { border-bottom: none; } .ttv-chapter-item.selected { background: #f0f8ff; border-left: 2px solid #5bc0de; } .ttv-control-panel.fullscreen { position: fixed; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%; border-radius: 0; z-index: 9999; padding: 15px; display: flex; flex-direction: column; } .ttv-control-panel.fullscreen .ttv-content-editor, .ttv-control-panel.fullscreen .ttv-preview { height: calc(100vh - 250px); margin: 15px 0; font-size: 16px; } .ttv-header { margin-bottom: 20px; padding-bottom: 15px; border-bottom: 2px solid #eee; font-weight: bold; font-size: 16px; color: #444; display: flex; justify-content: space-between; align-items: center; } button.btn-warning { background: #f0ad4e; color: white; border: none; padding: 12px; border-radius: 6px; width: 100%; font-size: 14px; transition: all 0.2s ease; } button.btn-warning:hover { background: #ec971f; transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,0.1); } button.ttv-minimize { padding: 2px 8px; background: none; border: none; cursor: pointer; font-size: 16px; color: #666; transition: all 0.2s ease; } button.ttv-minimize:hover { color: #333; } .chapter-form { margin-bottom: 20px; padding: 15px; border: 1px solid #ddd; border-radius: 8px; } .chapter-form h3 { margin: 0 0 10px; font-size: 16px; color: #333; } .form-row { display: flex; gap: 10px; margin-bottom: 10px; } .form-row input { flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } .chapter-form textarea { width: 100%; min-height: 100px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; resize: vertical; } .ttv-forms-container { max-height: 600px; overflow-y: auto; padding: 10px; margin: 10px 0; border: 1px solid #eee; border-radius: 8px; } `; document.head.appendChild(style); // Tạo div thông báo const notification = document.createElement('div'); notification.className = 'ttv-notification'; document.body.appendChild(notification); // Hiển thị thông báo function showNotification(message, isError = false) { notification.textContent = message; notification.className = 'ttv-notification' + (isError ? ' ttv-error' : ''); notification.style.display = 'block'; setTimeout(() => { notification.style.display = 'none'; }, 3000); } // Parse chapters function update function parseChapters(content) { const lines = content.split('\n'); const chapters = []; let currentChapter = { title: '', name: '', content: [] }; const chapterPattern = /^\s*Chương\s+\d+:/; let previousChapterTitle = ''; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (chapterPattern.test(line)) { // Kiểm tra xem có phải là tiêu đề trùng lặp không if (line !== previousChapterTitle) { // Log để debug console.log(`Tìm thấy chương mới: ${line}`); // Nếu đã có chương trước đó, lưu lại if (currentChapter.title && currentChapter.content.length > 0) { chapters.push({...currentChapter}); console.log(`Đã lưu chương: ${currentChapter.title}\nTên: ${currentChapter.name}\nSố dòng: ${currentChapter.content.length}`); currentChapter = {title: '', name: '', content: []}; } // Lưu tiêu đề chương hiện tại currentChapter.title = line; // Lấy tên chương sau dấu : let name = line.split(':')[1]?.trim() || ''; // Xóa dấu ., , hoặc ; ở đầu và cuối tên chương name = name.replace(/^[.,;'"]+|[.,;'"]+$/g, '').trim(); currentChapter.name = name; previousChapterTitle = line; console.log(`Đang xử lý chương mới:\nTiêu đề: ${line}\nTên chương: ${name}`); } else { console.log(`Bỏ qua tiêu đề trùng lặp: ${line}`); } } else { // Lưu tất cả nội dung không phải tiêu đề chương if (line) { // Chỉ thêm dòng không trống if (currentChapter.title) { currentChapter.content.push(line); } } } } // Thêm chương cuối cùng nếu có if (currentChapter.title && currentChapter.content.length > 0) { chapters.push({...currentChapter}); } // Tự động tạo form và điền nội dung if (chapters.length > 0) { // Tạo số form bằng với số chương tìm thấy createChapterForms(chapters.length); // Hiển thị danh sách chương displayChapters(chapters); // Tự động điền form setTimeout(() => { try { transferContent(); } catch (error) { console.error('Lỗi khi tự động điền form:', error); showNotification('Có lỗi xảy ra khi tự động điền form!', true); } }, 1000); } console.log(`Tổng số chương tìm thấy: ${chapters.length}`); return chapters; } // Chọn chương để hiển thị function selectChapter(chapter, index) { const contentEditor = document.querySelector('.ttv-content-editor'); contentEditor.value = chapter.title + '\n' + chapter.content.join('\n'); // Cập nhật số từ const wordCountSpan = document.querySelector('.ttv-word-count'); if (wordCountSpan) { wordCountSpan.textContent = updateWordCount(contentEditor.value); } // Highlight selected chapter const chapterItems = document.querySelectorAll('.ttv-chapter-item'); chapterItems.forEach(item => item.classList.remove('selected')); chapterItems[index]?.classList.add('selected'); // Tự động điền form khi chọn chương try { transferContent(); } catch (error) { console.error('Lỗi khi tự động điền form:', error); showNotification('Có lỗi xảy ra khi tự động điền form!', true); } } // Hiển thị danh sách chương function displayChapters(chapters) { const chapterList = document.createElement('div'); chapterList.className = 'ttv-chapter-list'; chapters.forEach((chapter, index) => { const chapterItem = document.createElement('div'); chapterItem.className = 'ttv-chapter-item'; // Store chapter content chapterItem._content = chapter.title + '\n' + chapter.content.join('\n'); // Tạo các phần tử con với định dạng riêng const titleDiv = document.createElement('div'); titleDiv.className = 'chapter-title'; titleDiv.textContent = chapter.title; const nameDiv = document.createElement('div'); nameDiv.className = 'chapter-name'; nameDiv.textContent = `Tên chương: ${chapter.name}`; const statsDiv = document.createElement('div'); statsDiv.className = 'chapter-stats'; statsDiv.textContent = `${chapter.content.length} dòng`; // Thêm các phần tử vào item chapterItem.appendChild(titleDiv); chapterItem.appendChild(nameDiv); chapterItem.appendChild(statsDiv); chapterItem.onclick = () => selectChapter(chapter, index); chapterList.appendChild(chapterItem); // Select first chapter by default if (index === 0) { chapterItem.classList.add('selected'); } }); const existingList = document.querySelector('.ttv-chapter-list'); if (existingList) { existingList.remove(); } const contentEditor = document.querySelector('.ttv-content-editor'); contentEditor.parentNode.insertBefore(chapterList, contentEditor); // Hiển thị thông báo về số chương tìm thấy showNotification(`Đã tìm thấy ${chapters.length} chương và tự động điền vào form.${chapters.length >= 10 ? ' (Giới hạn 10 chương đầu tiên)' : ''}`); // Tự động chọn chương đầu tiên if (chapters.length > 0) { selectChapter(chapters[0], 0); } } // Đếm số từ và ký tự function updateWordCount(content) { const wordCount = content.trim().split(/\s+/).length; const charCount = content.length; return `${wordCount} từ | ${charCount} ký tự`; } // Chuyển nội dung từ khung soạn thảo sang form function transferContent() { try { // Get all chapters const chapterItems = document.querySelectorAll('.ttv-chapter-item'); // Fill each chapter's content into corresponding form chapterItems.forEach((chapterItem, index) => { const formIndex = index + 1; const formFields = { chapterName: document.querySelector(`input[name="chap_name[${formIndex}]"]`), content: document.querySelector(`textarea[name="introduce[${formIndex}]"]`), chapterNumber: document.querySelector(`input[name="chap_number[${formIndex}]"]`), chapterOrder: document.querySelector(`input[name="chap_stt[${formIndex}]"]`), volume: document.querySelector(`input[name="vol[${formIndex}]"]`) }; // Verify all form fields exist Object.entries(formFields).forEach(([fieldName, element]) => { if (!element) { throw new Error(`Không tìm thấy trường "${fieldName}" cho chương ${formIndex}`); } }); // Get chapter info const chapterTitle = chapterItem.querySelector('.chapter-title').textContent; const chapterName = chapterItem.querySelector('.chapter-name').textContent.replace('Tên chương: ', ''); const chapterNumber = chapterTitle.match(/Chương\s+(\d+)/)?.[1] || formIndex.toString(); // Fill form fields formFields.chapterName.value = chapterName; formFields.chapterNumber.value = chapterNumber; formFields.chapterOrder.value = chapterNumber; formFields.volume.value = '1'; // Get content for this chapter if (index === 0) { // If it's the first/selected chapter formFields.content.value = document.querySelector('.ttv-content-editor').value; } else { formFields.content.value = chapterItem._content || ''; // Use stored content } console.log(`Đã điền form cho chương ${formIndex}:`, { title: chapterTitle, name: chapterName, number: chapterNumber, contentLength: formFields.content.value.length }); }); showNotification(`Đã tự động điền ${chapterItems.length} chương vào form!`); console.log(`Đã điền ${chapterItems.length} chương vào form`); } catch (error) { console.error('Lỗi khi điền form:', error); showNotification('Có lỗi xảy ra khi điền form: ' + error.message, true); throw error; } } // Chuyển đổi giữa chế độ soạn thảo và xem trước function togglePreview() { const contentEditor = document.querySelector('.ttv-content-editor'); const preview = document.querySelector('.ttv-preview'); const previewBtn = document.querySelector('.ttv-preview-btn'); if (contentEditor.style.display !== 'none') { contentEditor.style.display = 'none'; preview.style.display = 'block'; preview.innerHTML = contentEditor.value.replace(/\n/g, '<br>'); previewBtn.textContent = 'Soạn thảo'; } else { contentEditor.style.display = 'block'; preview.style.display = 'none'; previewBtn.textContent = 'Xem trước'; } } // Chuyển đổi chế độ toàn màn hình function toggleFullscreen() { const panel = document.querySelector('.ttv-control-panel'); const fullscreenBtn = document.querySelector('.ttv-fullscreen-btn'); panel.classList.toggle('fullscreen'); fullscreenBtn.textContent = panel.classList.contains('fullscreen') ? 'Thu nhỏ' : 'Toàn màn hình'; } // Function to create form fields for multiple chapters using website's template function createChapterForms(totalChapters) { try { // Always create 10 forms const TOTAL_FORMS = 10; // Find the original form container const originalForm = document.querySelector('form'); if (!originalForm) { throw new Error('Không tìm thấy form gốc trên trang web'); } // Get original chapter form template const originalChapterDiv = document.querySelector('div[id^="div-chapter"]'); if (!originalChapterDiv) { throw new Error('Không tìm thấy mẫu form chương gốc'); } // Debug log template structure console.log('Template form structure:', { id: originalChapterDiv.id, fields: Array.from(originalChapterDiv.querySelectorAll('input, textarea')).map(el => ({ name: el.getAttribute('name'), type: el.tagName.toLowerCase() })) }); // Clear any existing additional chapter divs const existingChapters = document.querySelectorAll('div[id^="div-chapter"]'); console.log(`Xóa ${existingChapters.length - 1} form cũ`); existingChapters.forEach((div, index) => { if (index > 0) { // Keep the first one as template div.remove(); } }); // Create new chapter divs based on template for (let i = 1; i <= TOTAL_FORMS; i++) { const newChapterDiv = originalChapterDiv.cloneNode(true); const newId = `div-chapter-${i}`; newChapterDiv.id = newId; // Update all input fields with proper indices newChapterDiv.querySelectorAll('input, textarea').forEach(input => { const name = input.getAttribute('name'); if (name) { const oldIndex = name.match(/\[(\d+)\]/)?.[1]; const newName = name.replace(/\[\d+\]/, `[${i}]`); console.log(`Cập nhật trường ${name} -> ${newName} trong form ${i}`); // Update the index in the name attribute input.setAttribute('name', newName); // Clear any existing values input.value = ''; } }); // Update any labels or headers newChapterDiv.querySelectorAll('label, .chapter-header').forEach(el => { const oldText = el.textContent; const newText = oldText.replace(/\d+/, i); console.log(`Cập nhật label ${oldText} -> ${newText} trong form ${i}`); el.textContent = newText; }); // Append the new chapter div to the form originalForm.appendChild(newChapterDiv); console.log(`Đã tạo form chương ${i} với ID ${newId}`); } // After creating forms, make sure they're visible const allChapterDivs = document.querySelectorAll('div[id^="div-chapter"]'); allChapterDivs.forEach(div => { div.style.display = 'block'; }); showNotification(`Đã tạo ${TOTAL_FORMS} form chương, sẽ điền ${totalChapters} chương vào form`); } catch (error) { console.error('Lỗi khi tạo form chương:', error); showNotification('Có lỗi xảy ra khi tạo form chương: ' + error.message, true); throw error; } } // Thêm panel điều khiển function addControlPanel() { // Tạo panel const panel = document.createElement('div'); panel.className = 'ttv-control-panel'; // Thêm header const header = document.createElement('div'); header.className = 'ttv-header'; header.innerHTML = ` <div>Soạn Thảo Nội Dung</div> <div class="ttv-toolbar"> <button class="ttv-preview-btn" onclick="togglePreview()">Xem trước</button> <button class="ttv-fullscreen-btn" onclick="toggleFullscreen()">Toàn màn hình</button> <button class="ttv-minimize">−</button> </div> `; const buttonGroup = document.createElement('div'); buttonGroup.className = 'ttv-button-group'; // Khung soạn thảo nội dung const contentEditorLabel = document.createElement('div'); contentEditorLabel.className = 'ttv-heading'; contentEditorLabel.innerHTML = ` Nội dung chương: <span class="ttv-word-count">0 từ | 0 ký tự</span> `; const contentEditor = document.createElement('textarea'); contentEditor.className = 'ttv-content-editor'; contentEditor.placeholder = 'Nhập hoặc dán nội dung chương vào đây...'; // Khung xem trước const preview = document.createElement('div'); preview.className = 'ttv-preview'; // Cập nhật số từ khi nhập nội dung contentEditor.oninput = () => { const wordCountSpan = document.querySelector('.ttv-word-count'); if (wordCountSpan) { wordCountSpan.textContent = updateWordCount(contentEditor.value); } }; // Xử lý khi paste nội dung contentEditor.onpaste = (e) => { // Cho phép paste hoàn tất setTimeout(() => { const content = contentEditor.value; const chapters = parseChapters(content); if (chapters.length > 0) { displayChapters(chapters); // Tự động điền form sau khi hiển thị danh sách chương setTimeout(() => { try { transferContent(); } catch (error) { console.error('Lỗi khi tự động điền form:', error); showNotification('Có lỗi xảy ra khi tự động điền form!', true); } }, 1000); // Đợi 1s để đảm bảo UI đã được cập nhật hoàn toàn } }, 0); }; // Nút chuyển nội dung const transferBtn = document.createElement('button'); transferBtn.type = 'button'; transferBtn.className = 'btn btn-warning'; transferBtn.innerHTML = '<span>Chuyển nội dung sang form</span>'; transferBtn.onclick = transferContent; // Thêm các phần tử vào panel panel.appendChild(header); buttonGroup.appendChild(contentEditorLabel); buttonGroup.appendChild(contentEditor); buttonGroup.appendChild(preview); buttonGroup.appendChild(transferBtn); panel.appendChild(buttonGroup); document.body.appendChild(panel); // Thêm xử lý sự kiện cho các nút trong toolbar const minimizeBtn = panel.querySelector('.ttv-minimize'); minimizeBtn.onclick = () => { panel.classList.toggle('minimized'); minimizeBtn.innerHTML = panel.classList.contains('minimized') ? '+' : '−'; }; window.togglePreview = togglePreview; window.toggleFullscreen = toggleFullscreen; } // Thêm control panel khi trang đã load window.addEventListener('load', function() { addControlPanel(); }); })();