您需要先安装一个扩展,例如 篡改猴、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 1.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'; // Thêm CSS cho thông báo và nút 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; } // Điều chỉnh toolbar và các nút điều khiển .ttv-toolbar { display: flex; gap: 6px; align-items: center; } .ttv-toolbar button { padding: 2px 8px; font-size: 11px; color: #555; background: #f5f5f5; border: 1px solid #ddd; border-radius: 3px; cursor: pointer; transition: all 0.2s ease; } .ttv-toolbar button:hover { background: #e9e9e9; transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .ttv-button-group { display: flex; flex-direction: column; gap: 15px; } .ttv-file-label { width: 100%; padding: 12px; background: #5bc0de; color: white; border-radius: 6px; cursor: pointer; font-size: 14px; text-align: center; transition: all 0.2s ease; margin: 0; } .ttv-file-label:hover { background: #46b8da; transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .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; } .ttv-chapter-item:hover { background: #f5f5f5; } .ttv-chapter-item:last-child { border-bottom: none; } // 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: '', // Thêm trường name để lưu tên chương content: [] }; const chapterPattern = /^\t*Chương\s+\d+:/; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (chapterPattern.test(line)) { // Nếu đã có chương trước đó, lưu lại if (currentChapter.title && currentChapter.content.length > 0) { chapters.push({...currentChapter}); currentChapter = {title: '', name: '', content: []}; } // Kiểm tra dòng tiếp theo if (i + 1 < lines.length && chapterPattern.test(lines[i + 1])) { // Nếu dòng tiếp theo cũng là tiêu đề chương, gộp 2 dòng currentChapter.title = line + '\n' + lines[i + 1]; // Lấy tên chương từ dòng đầu sau dấu : currentChapter.name = line.split(':')[1]?.trim() || ''; i++; // Bỏ qua dòng tiếp theo } else { currentChapter.title = line; // Lấy tên chương sau dấu : currentChapter.name = line.split(':')[1]?.trim() || ''; } } else { // Nếu chưa có tiêu đề chương, tạo chương mới if (!currentChapter.title) { currentChapter.title = "Chương không tiêu đề"; currentChapter.name = "Chương không tiêu đề"; } currentChapter.content.push(line); } } // Thêm chương cuối cùng if (currentChapter.title && currentChapter.content.length > 0) { chapters.push(currentChapter); } return chapters; } // 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'; chapterItem.textContent = `${chapter.title} (${chapter.content.length} dòng)`; chapterItem.onclick = () => selectChapter(chapter); chapterList.appendChild(chapterItem); }); const existingList = document.querySelector('.ttv-chapter-list'); if (existingList) { existingList.remove(); } const contentEditor = document.querySelector('.ttv-content-editor'); contentEditor.parentNode.insertBefore(chapterList, contentEditor); } // Chọn chương để hiển thị function selectChapter(chapter) { 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); } } // Đọc nội dung file function readFileContent(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => resolve(e.target.result); reader.onerror = (e) => reject(new Error('Lỗi đọc file: ' + e.target.error)); reader.readAsText(file, 'UTF-8'); }); } // Đế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ự`; } // Xử lý khi chọn file async function handleFileSelect(event) { try { const file = event.target.files[0]; if (!file) return; // Đọc nội dung file const content = await readFileContent(file); // Phân tích và tách chương const chapters = parseChapters(content); displayChapters(chapters); // Điền vào khung soạn thảo nội dung chương đầu tiên nếu có if (chapters.length > 0) { const contentEditor = document.querySelector('.ttv-content-editor'); if (contentEditor) { const firstChapter = chapters[0]; contentEditor.value = firstChapter.title + '\n' + firstChapter.content.join('\n'); // Cập nhật số từ const wordCountSpan = document.querySelector('.ttv-word-count'); if (wordCountSpan) { wordCountSpan.textContent = updateWordCount(contentEditor.value); } showNotification(`Đã tải ${chapters.length} chương từ file thành công!`); } } else { showNotification('Không tìm thấy chương nào trong file!', true); } } catch (error) { console.error('Lỗi xử lý file:', error); showNotification('Có lỗi xảy ra khi đọc file!', true); } } // Chuyển nội dung từ khung soạn thảo sang form function transferContent() { const contentEditor = document.querySelector('.ttv-content-editor'); const chapterNameInput = document.querySelector('input[name="chap_name[1]"]'); const contentInput = document.querySelector('textarea[name="introduce[1]"]'); if (contentEditor && contentInput) { const content = contentEditor.value; const chapters = parseChapters(content); if (chapters.length > 0) { // Điền tên chương vào input tên chương if (chapterNameInput && chapters[0].name) { chapterNameInput.value = chapters[0].name; } // Điền nội dung vào textarea contentInput.value = chapters[0].content.join('\n'); showNotification('Đã chuyển nội dung sang form đăng chương!'); } else { contentInput.value = contentEditor.value; showNotification('Đã chuyển toàn bộ nội dung sang form!'); } } else { showNotification('Không tìm thấy form đăng chương!', true); } } // 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> `; // Container cho các nút const buttonGroup = document.createElement('div'); buttonGroup.className = 'ttv-button-group'; // Input chọn file const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.txt'; fileInput.className = 'ttv-file-input'; fileInput.id = 'ttv-file-input'; fileInput.style.display = 'none'; fileInput.onchange = handleFileSelect; // Label cho input file const fileLabel = document.createElement('label'); fileLabel.htmlFor = 'ttv-file-input'; fileLabel.className = 'ttv-file-label'; fileLabel.innerHTML = '<span>Chọn file txt chứa nội dung</span>'; // 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); showNotification(`Đã tìm thấy ${chapters.length} chương!`); } }, 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(fileInput); buttonGroup.appendChild(fileLabel); 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(); }); })();