您需要先安装一个扩展,例如 篡改猴、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.5 // @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; } // Tối ưu chế độ toàn màn hình .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; } `; 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); } // Phân tích và tách chương từ nội dung function parseChapters(content) { const lines = content.split('\n'); const chapters = []; let currentChapter = { title: '', name: '', content: [] }; const chapterPattern = /^\s*Chương\s+\d+:/; // Lưu lại tất cả các dòng không phải tiêu đề chương let previousChapterTitle = ''; // Đầu tiên tìm các chương được đánh dấu rõ ràng 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: []}; // Chỉ lấy 10 chương đầu tiên if (chapters.length >= 10) { // Copy phần còn lại vào clipboard const remainingContent = lines.slice(i).join('\n'); navigator.clipboard.writeText(remainingContent).then(() => { console.log('Đã copy nội dung còn lại vào clipboard'); showNotification('Đã copy các chương còn lại vào clipboard'); }).catch(err => { console.error('Không thể copy vào clipboard:', err); showNotification('Không thể copy các chương còn lại vào clipboard', true); }); break; } } // 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ó và chưa đủ 10 chương if (currentChapter.title && currentChapter.content.length > 0 && chapters.length < 10) { chapters.push({...currentChapter}); } // Sau khi parse xong, tự động điền form if (chapters.length > 0) { // Hiển thị danh sách chương displayChapters(chapters); } 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'; // 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 chương đầu tiê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 { const formFields = { chapterName: document.querySelector('input[name="chap_name[1]"]'), content: document.querySelector('textarea[name="introduce[1]"]'), chapterNumber: document.querySelector('input[name="chap_number[1]"]'), chapterOrder: document.querySelector('input[name="chap_stt[1]"]'), volume: document.querySelector('input[name="vol[1]"]') }; // Kiểm tra tất cả các trường form Object.entries(formFields).forEach(([fieldName, element]) => { if (!element) { throw new Error(`Không tìm thấy trường "${fieldName}" trong form`); } }); // Lấy chương đã chọn const selectedChapter = document.querySelector('.ttv-chapter-item.selected'); if (!selectedChapter) { throw new Error('Chưa có chương nào được chọn'); } // Lấy thông tin chương const chapterTitle = selectedChapter.querySelector('.chapter-title').textContent; const chapterName = selectedChapter.querySelector('.chapter-name').textContent.replace('Tên chương: ', ''); const chapterContent = document.querySelector('.ttv-content-editor').value; // Lấy số chương từ tiêu đề const chapterNumber = chapterTitle.match(/Chương\s+(\d+)/)?.[1] || '1'; // Điền vào form formFields.chapterName.value = chapterName; formFields.content.value = chapterContent; formFields.chapterNumber.value = chapterNumber; formFields.chapterOrder.value = chapterNumber; formFields.volume.value = '1'; // Mặc định là quyển 1 showNotification('Đã tự động điền nội dung vào form đăng chương!'); console.log('Form đã được điền:', { chapterName, chapterNumber, contentLength: chapterContent.length }); } 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; // Ném lỗi để hàm gọi có thể xử lý } } // 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'; } // 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(); }); })();