TTV Auto Upload

Tự động điền form đăng chương trên tangthuvien.net với tính năng nâng cao

目前为 2025-03-08 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name TTV Auto Upload
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.7
  5. // @description Tự động điền form đăng chương trên tangthuvien.net với tính năng nâng cao
  6. // @author HA
  7. // @match https://tangthuvien.net/dang-chuong/story/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Thêm CSS cho thông báo và nút
  15. const style = document.createElement('style');
  16. style.textContent = `
  17. .ttv-notification {
  18. position: fixed;
  19. top: 20px;
  20. right: 20px;
  21. padding: 10px 20px;
  22. background: #4CAF50;
  23. color: white;
  24. border-radius: 4px;
  25. z-index: 9999;
  26. display: none;
  27. }
  28. .ttv-error {
  29. background: #f44336;
  30. }
  31. .ttv-control-panel {
  32. position: fixed;
  33. top: 70px;
  34. left: 20px;
  35. background: white;
  36. padding: 15px;
  37. border-radius: 8px;
  38. box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  39. z-index: 9998;
  40. width: 400px;
  41. }
  42. .ttv-button-group {
  43. display: flex;
  44. flex-direction: column;
  45. gap: 10px;
  46. }
  47. .ttv-button-group button,
  48. .ttv-file-label {
  49. width: 100%;
  50. padding: 8px;
  51. border: none;
  52. border-radius: 4px;
  53. cursor: pointer;
  54. font-size: 14px;
  55. text-align: center;
  56. }
  57. .ttv-file-input {
  58. display: none;
  59. }
  60. .ttv-file-label {
  61. background: #5bc0de;
  62. color: white;
  63. margin: 0;
  64. }
  65. .ttv-file-label:hover {
  66. background: #46b8da;
  67. }
  68. .ttv-content-editor {
  69. width: 100%;
  70. height: 400px;
  71. margin: 10px 0;
  72. padding: 10px;
  73. border: 1px solid #ddd;
  74. border-radius: 4px;
  75. font-size: 14px;
  76. line-height: 1.5;
  77. resize: vertical;
  78. }
  79. .ttv-heading {
  80. font-size: 12px;
  81. color: #666;
  82. margin: 5px 0;
  83. display: flex;
  84. justify-content: space-between;
  85. align-items: center;
  86. }
  87. .ttv-word-count {
  88. font-size: 11px;
  89. color: #999;
  90. }
  91. .ttv-preview {
  92. display: none;
  93. width: 100%;
  94. height: 400px;
  95. margin: 10px 0;
  96. padding: 10px;
  97. border: 1px solid #ddd;
  98. border-radius: 4px;
  99. font-size: 14px;
  100. line-height: 1.5;
  101. overflow-y: auto;
  102. background: #f9f9f9;
  103. }
  104. .ttv-control-panel.fullscreen {
  105. position: fixed;
  106. top: 0;
  107. left: 0;
  108. bottom: 0;
  109. right: 0;
  110. width: 100%;
  111. height: 100%;
  112. border-radius: 0;
  113. z-index: 9999;
  114. }
  115. .ttv-control-panel.fullscreen .ttv-content-editor,
  116. .ttv-control-panel.fullscreen .ttv-preview {
  117. height: calc(100vh - 250px);
  118. }
  119. .ttv-toolbar {
  120. display: flex;
  121. gap: 5px;
  122. }
  123. .ttv-toolbar button {
  124. padding: 2px 8px;
  125. font-size: 12px;
  126. color: #666;
  127. background: none;
  128. border: 1px solid #ddd;
  129. border-radius: 3px;
  130. cursor: pointer;
  131. }
  132. .ttv-toolbar button:hover {
  133. background: #f0f0f0;
  134. }
  135. .ttv-control-panel.minimized {
  136. width: auto;
  137. height: auto;
  138. padding: 10px;
  139. }
  140. .ttv-control-panel.minimized .ttv-button-group,
  141. .ttv-control-panel.minimized .ttv-header {
  142. display: none;
  143. }
  144. .ttv-header {
  145. margin-bottom: 15px;
  146. padding-bottom: 10px;
  147. border-bottom: 1px solid #eee;
  148. font-weight: bold;
  149. text-align: center;
  150. display: flex;
  151. justify-content: space-between;
  152. align-items: center;
  153. }
  154. `;
  155. document.head.appendChild(style);
  156.  
  157. // Tạo div thông báo
  158. const notification = document.createElement('div');
  159. notification.className = 'ttv-notification';
  160. document.body.appendChild(notification);
  161.  
  162. // Hiển thị thông báo
  163. function showNotification(message, isError = false) {
  164. notification.textContent = message;
  165. notification.className = 'ttv-notification' + (isError ? ' ttv-error' : '');
  166. notification.style.display = 'block';
  167. setTimeout(() => {
  168. notification.style.display = 'none';
  169. }, 3000);
  170. }
  171.  
  172. // Đọc nội dung file
  173. function readFileContent(file) {
  174. return new Promise((resolve, reject) => {
  175. const reader = new FileReader();
  176. reader.onload = (e) => resolve(e.target.result);
  177. reader.onerror = (e) => reject(new Error('Lỗi đọc file: ' + e.target.error));
  178. reader.readAsText(file, 'UTF-8');
  179. });
  180. }
  181.  
  182. // Đếm số từ và ký tự
  183. function updateWordCount(content) {
  184. const wordCount = content.trim().split(/\s+/).length;
  185. const charCount = content.length;
  186. return `${wordCount} t | ${charCount} ký tự`;
  187. }
  188.  
  189.  
  190. // Xử lý khi chọn file
  191. async function handleFileSelect(event) {
  192. try {
  193. const file = event.target.files[0];
  194. if (!file) return;
  195.  
  196. // Đọc nội dung file
  197. const content = await readFileContent(file);
  198.  
  199. // Điền vào khung soạn thảo nội dung
  200. const contentEditor = document.querySelector('.ttv-content-editor');
  201. if (contentEditor) {
  202. contentEditor.value = content;
  203.  
  204. // Cập nhật số từ
  205. const wordCountSpan = document.querySelector('.ttv-word-count');
  206. if (wordCountSpan) {
  207. wordCountSpan.textContent = updateWordCount(content);
  208. }
  209.  
  210. showNotification('Đã tải nội dung từ file thành công!');
  211. } else {
  212. showNotification('Không tìm thấy khung soạn thảo!', true);
  213. }
  214. } catch (error) {
  215. console.error('Lỗi xử lý file:', error);
  216. showNotification('Có lỗi xảy ra khi đọc file!', true);
  217. }
  218. }
  219.  
  220. // Chuyển nội dung từ khung soạn thảo sang form
  221. function transferContent() {
  222. const contentEditor = document.querySelector('.ttv-content-editor');
  223. const contentInput = document.querySelector('textarea[name="introduce[1]"]');
  224.  
  225. if (contentEditor && contentInput) {
  226. contentInput.value = contentEditor.value;
  227. showNotification('Đã chuyển nội dung sang form đăng chương!');
  228. } else {
  229. showNotification('Không tìm thấy form đăng chương!', true);
  230. }
  231. }
  232.  
  233. // Chuyển đổi giữa chế độ soạn thảo và xem trước
  234. function togglePreview() {
  235. const contentEditor = document.querySelector('.ttv-content-editor');
  236. const preview = document.querySelector('.ttv-preview');
  237. const previewBtn = document.querySelector('.ttv-preview-btn');
  238.  
  239. if (contentEditor.style.display !== 'none') {
  240. contentEditor.style.display = 'none';
  241. preview.style.display = 'block';
  242. preview.innerHTML = contentEditor.value.replace(/\n/g, '<br>');
  243. previewBtn.textContent = 'Soạn thảo';
  244. } else {
  245. contentEditor.style.display = 'block';
  246. preview.style.display = 'none';
  247. previewBtn.textContent = 'Xem trước';
  248. }
  249. }
  250.  
  251. // Chuyển đổi chế độ toàn màn hình
  252. function toggleFullscreen() {
  253. const panel = document.querySelector('.ttv-control-panel');
  254. const fullscreenBtn = document.querySelector('.ttv-fullscreen-btn');
  255.  
  256. panel.classList.toggle('fullscreen');
  257. fullscreenBtn.textContent = panel.classList.contains('fullscreen') ? 'Thu nhỏ' : 'Toàn màn hình';
  258. }
  259.  
  260. // Thêm panel điều khiển
  261. function addControlPanel() {
  262. // Tạo panel
  263. const panel = document.createElement('div');
  264. panel.className = 'ttv-control-panel';
  265.  
  266. // Thêm header
  267. const header = document.createElement('div');
  268. header.className = 'ttv-header';
  269. header.innerHTML = `
  270. <div>Son Tho Ni Dung</div>
  271. <div class="ttv-toolbar">
  272. <button class="ttv-preview-btn" onclick="togglePreview()">Xem trước</button>
  273. <button class="ttv-fullscreen-btn" onclick="toggleFullscreen()">Toàn màn hình</button>
  274. <button class="ttv-minimize">−</button>
  275. </div>
  276. `;
  277.  
  278. // Container cho các nút
  279. const buttonGroup = document.createElement('div');
  280. buttonGroup.className = 'ttv-button-group';
  281.  
  282. // Input chọn file
  283. const fileInput = document.createElement('input');
  284. fileInput.type = 'file';
  285. fileInput.accept = '.txt';
  286. fileInput.className = 'ttv-file-input';
  287. fileInput.id = 'ttv-file-input';
  288. fileInput.onchange = handleFileSelect;
  289.  
  290. // Label cho input file
  291. const fileLabel = document.createElement('label');
  292. fileLabel.htmlFor = 'ttv-file-input';
  293. fileLabel.className = 'ttv-file-label';
  294. fileLabel.innerHTML = '<span>Chọn file txt chứa nội dung</span>';
  295.  
  296. // Khung soạn thảo nội dung
  297. const contentEditorLabel = document.createElement('div');
  298. contentEditorLabel.className = 'ttv-heading';
  299. contentEditorLabel.innerHTML = `
  300. Ni dung chương:
  301. <span class="ttv-word-count">0 t | 0 ký tự</span>
  302. `;
  303.  
  304. const contentEditor = document.createElement('textarea');
  305. contentEditor.className = 'ttv-content-editor';
  306. contentEditor.placeholder = 'Nhập hoặc dán nội dung chương vào đây...';
  307.  
  308. // Khung xem trước
  309. const preview = document.createElement('div');
  310. preview.className = 'ttv-preview';
  311.  
  312. // Cập nhật số từ khi nhập nội dung
  313. contentEditor.oninput = () => {
  314. const wordCountSpan = document.querySelector('.ttv-word-count');
  315. if (wordCountSpan) {
  316. wordCountSpan.textContent = updateWordCount(contentEditor.value);
  317. }
  318. };
  319.  
  320. // Nút chuyển nội dung
  321. const transferBtn = document.createElement('button');
  322. transferBtn.type = 'button';
  323. transferBtn.className = 'btn btn-warning';
  324. transferBtn.innerHTML = '<span>Chuyển nội dung sang form</span>';
  325. transferBtn.onclick = transferContent;
  326.  
  327. // Thêm các phần tử vào panel
  328. panel.appendChild(header);
  329. buttonGroup.appendChild(fileInput);
  330. buttonGroup.appendChild(fileLabel);
  331. buttonGroup.appendChild(contentEditorLabel);
  332. buttonGroup.appendChild(contentEditor);
  333. buttonGroup.appendChild(preview);
  334. buttonGroup.appendChild(transferBtn);
  335. panel.appendChild(buttonGroup);
  336.  
  337. document.body.appendChild(panel);
  338.  
  339. // Thêm xử lý sự kiện cho các nút trong toolbar
  340. const minimizeBtn = panel.querySelector('.ttv-minimize');
  341. minimizeBtn.onclick = () => {
  342. if (panel.classList.contains('minimized')) {
  343. panel.classList.remove('minimized');
  344. minimizeBtn.innerHTML = '−';
  345. } else {
  346. panel.classList.add('minimized');
  347. minimizeBtn.innerHTML = '+';
  348. }
  349. };
  350.  
  351. window.togglePreview = togglePreview;
  352. window.toggleFullscreen = toggleFullscreen;
  353. }
  354.  
  355. // Thêm control panel khi trang đã load
  356. window.addEventListener('load', function() {
  357. addControlPanel();
  358. });
  359. })();