Chapter Downloader

Tự động lấy nội dung từ chương hiện tại đến hết trên truyen.tangthuvien.vn

当前为 2025-04-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Chapter Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.3
  5. // @description Tự động lấy nội dung từ chương hiện tại đến hết trên truyen.tangthuvien.vn
  6. // @author Bạn
  7. // @match https://truyen.tangthuvien.vn/doc-truyen/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. const panel = document.createElement('div');
  15. panel.id = 'reader-panel';
  16. panel.style.cssText = `
  17. position: fixed;
  18. top: 10%;
  19. right: 10px;
  20. width: 300px;
  21. background-color: #f8f8f8;
  22. border: 1px solid #ccc;
  23. border-radius: 5px;
  24. padding: 10px;
  25. z-index: 9999;
  26. box-shadow: 0 0 10px rgba(0,0,0,0.2);
  27. font-family: Arial, sans-serif;
  28. `;
  29.  
  30. const textarea = document.createElement('textarea');
  31. textarea.id = 'content-textarea';
  32. textarea.style.cssText = `
  33. width: 100%;
  34. height: 300px;
  35. margin-bottom: 10px;
  36. padding: 8px;
  37. border: 1px solid #ddd;
  38. border-radius: 4px;
  39. resize: vertical;
  40. `;
  41.  
  42. const startButton = document.createElement('button');
  43. startButton.textContent = 'Bắt đầu';
  44. startButton.style.cssText = `
  45. width: 100%;
  46. padding: 8px;
  47. margin-bottom: 10px;
  48. background-color: #4CAF50;
  49. color: white;
  50. border: none;
  51. border-radius: 4px;
  52. cursor: pointer;
  53. `;
  54.  
  55. const buttonRow1 = document.createElement('div');
  56. buttonRow1.style.cssText = `display: flex; justify-content: space-between; margin-bottom: 10px;`;
  57.  
  58. const getTextButton = document.createElement('button');
  59. getTextButton.textContent = 'Lấy Text';
  60. getTextButton.style.cssText = `
  61. flex: 1;
  62. padding: 8px;
  63. margin-right: 5px;
  64. background-color: #2196F3;
  65. color: white;
  66. border: none;
  67. border-radius: 4px;
  68. cursor: pointer;
  69. `;
  70.  
  71. const nextChapterButton = document.createElement('button');
  72. nextChapterButton.textContent = 'Chương Tiếp';
  73. nextChapterButton.style.cssText = `
  74. flex: 1;
  75. padding: 8px;
  76. margin-left: 5px;
  77. background-color: #ff9800;
  78. color: white;
  79. border: none;
  80. border-radius: 4px;
  81. cursor: pointer;
  82. `;
  83.  
  84. const buttonRow2 = document.createElement('div');
  85. buttonRow2.style.cssText = `display: flex; justify-content: space-between;`;
  86.  
  87. const clearButton = document.createElement('button');
  88. clearButton.textContent = 'Xoá';
  89. clearButton.style.cssText = `
  90. flex: 1;
  91. padding: 8px;
  92. margin-right: 5px;
  93. background-color: #f44336;
  94. color: white;
  95. border: none;
  96. border-radius: 4px;
  97. cursor: pointer;
  98. `;
  99.  
  100. const copyButton = document.createElement('button');
  101. copyButton.textContent = 'Sao Chép';
  102. copyButton.style.cssText = `
  103. flex: 1;
  104. padding: 8px;
  105. margin-left: 5px;
  106. background-color: #9c27b0;
  107. color: white;
  108. border: none;
  109. border-radius: 4px;
  110. cursor: pointer;
  111. `;
  112.  
  113. buttonRow1.appendChild(getTextButton);
  114. buttonRow1.appendChild(nextChapterButton);
  115. buttonRow2.appendChild(clearButton);
  116. buttonRow2.appendChild(copyButton);
  117. panel.appendChild(textarea);
  118. panel.appendChild(startButton);
  119. panel.appendChild(buttonRow1);
  120. panel.appendChild(buttonRow2);
  121. document.body.appendChild(panel);
  122.  
  123. const savedContent = localStorage.getItem('chapterDownloaderContent');
  124. if (savedContent) textarea.value = savedContent;
  125.  
  126. function saveContent() {
  127. localStorage.setItem('chapterDownloaderContent', textarea.value);
  128. }
  129.  
  130. textarea.addEventListener('input', saveContent);
  131.  
  132. function extractChapterContent() {
  133. const titleElement = document.querySelector('h2');
  134. const contentElement = document.querySelector('.box-chap');
  135.  
  136. if (titleElement && contentElement) {
  137. const title = titleElement.innerText.trim();
  138. const content = contentElement.innerText.trim();
  139.  
  140. if (textarea.value) {
  141. textarea.value += '\n\n' + title + '\n\n' + content;
  142. } else {
  143. textarea.value = title + '\n\n' + content;
  144. }
  145.  
  146. saveContent();
  147. return true;
  148. } else {
  149. return false;
  150. }
  151. }
  152.  
  153. function goToNextChapter() {
  154. const nextChapterLink = document.querySelector('.bot-next_chap.bot-control');
  155. if (nextChapterLink) {
  156. nextChapterLink.click();
  157. return true;
  158. } else {
  159. const alternatives = document.querySelectorAll('a[href*="chuong"]');
  160. for (const link of alternatives) {
  161. if (link.textContent.toLowerCase().includes('tiếp') || link.textContent.toLowerCase().includes('sau') || link.textContent.toLowerCase().includes('next')) {
  162. link.click();
  163. return true;
  164. }
  165. }
  166. }
  167. return false;
  168. }
  169.  
  170. let isAutoDownloading = false;
  171.  
  172. function startAutoDownloading() {
  173. if (!isAutoDownloading) return;
  174.  
  175. if (extractChapterContent()) {
  176. setTimeout(() => {
  177. if (!isAutoDownloading) return;
  178. if (goToNextChapter()) {
  179. setTimeout(() => {
  180. if (isAutoDownloading) startAutoDownloading();
  181. }, 2000);
  182. } else {
  183. isAutoDownloading = false;
  184. startButton.textContent = 'Bắt đầu';
  185. startButton.style.backgroundColor = '#4CAF50';
  186. }
  187. }, 1000);
  188. } else {
  189. isAutoDownloading = false;
  190. startButton.textContent = 'Bắt đầu';
  191. startButton.style.backgroundColor = '#4CAF50';
  192. }
  193. }
  194.  
  195. startButton.addEventListener('click', () => {
  196. isAutoDownloading = !isAutoDownloading;
  197. if (isAutoDownloading) {
  198. startButton.textContent = 'Dừng';
  199. startButton.style.backgroundColor = '#f44336';
  200. startAutoDownloading();
  201. } else {
  202. startButton.textContent = 'Bắt đầu';
  203. startButton.style.backgroundColor = '#4CAF50';
  204. }
  205. });
  206.  
  207. getTextButton.addEventListener('click', extractChapterContent);
  208. nextChapterButton.addEventListener('click', goToNextChapter);
  209. clearButton.addEventListener('click', () => {
  210. textarea.value = '';
  211. saveContent();
  212. });
  213.  
  214. copyButton.addEventListener('click', () => {
  215. textarea.select();
  216. document.execCommand('copy');
  217.  
  218. const notification = document.createElement('div');
  219. notification.textContent = 'Đã sao chép!';
  220. notification.style.cssText = `
  221. position: fixed;
  222. bottom: 20px;
  223. left: 50%;
  224. transform: translateX(-50%);
  225. background-color: rgba(0,0,0,0.8);
  226. color: white;
  227. padding: 10px 20px;
  228. border-radius: 4px;
  229. z-index: 10000;
  230. `;
  231. document.body.appendChild(notification);
  232. setTimeout(() => document.body.removeChild(notification), 2000);
  233. });
  234.  
  235. let isDragging = false;
  236. let offsetX, offsetY;
  237.  
  238. panel.addEventListener('mousedown', (e) => {
  239. if (e.target === panel) {
  240. isDragging = true;
  241. offsetX = e.clientX - panel.getBoundingClientRect().left;
  242. offsetY = e.clientY - panel.getBoundingClientRect().top;
  243. }
  244. });
  245.  
  246. document.addEventListener('mousemove', (e) => {
  247. if (isDragging) {
  248. panel.style.left = (e.clientX - offsetX) + 'px';
  249. panel.style.top = (e.clientY - offsetY) + 'px';
  250. panel.style.right = 'auto';
  251. }
  252. });
  253.  
  254. document.addEventListener('mouseup', () => { isDragging = false; });
  255.  
  256. const shortcutsInfo = document.createElement('div');
  257. shortcutsInfo.style.cssText = `
  258. font-size: 11px;
  259. color: #666;
  260. margin-top: 10px;
  261. padding-top: 5px;
  262. border-top: 1px solid #ddd;
  263. `;
  264. shortcutsInfo.innerHTML = `
  265. <b>Phím tt:</b> Alt+G (Lấy text), Alt+N (Chương tiếp), Alt+S (Bắt đầu/Dng), Alt+C (Sao chép)
  266. `;
  267. panel.appendChild(shortcutsInfo);
  268.  
  269. document.addEventListener('keydown', (e) => {
  270. if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT') {
  271. if (e.altKey && e.key === 'g') { extractChapterContent(); e.preventDefault(); }
  272. else if (e.altKey && e.key === 'n') { goToNextChapter(); e.preventDefault(); }
  273. else if (e.altKey && e.key === 's') { startButton.click(); e.preventDefault(); }
  274. else if (e.altKey && e.key === 'c') { textarea.select(); document.execCommand('copy'); e.preventDefault(); }
  275. }
  276. });
  277.  
  278. console.log('Chapter Downloader script loaded (no separator mode)');
  279. })();