Chapter Downloader

Tự động trích xuất nội dung truyện từ truyen.tangthuvien.net

当前为 2025-03-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Chapter Downloader
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3
  5. // @description Tự động trích xuất nội dung truyện từ truyen.tangthuvien.net
  6. // @author TangThuVienExtractor
  7. // @match https://truyen.tangthuvien.net/doc-truyen/*/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Biến toàn cục để theo dõi trạng thái trích xuất tự động
  15. let isAutomaticExtraction = localStorage.getItem('isAutoExtractionActive') === 'true';
  16. // Biến đếm số chương đã trích xuất
  17. let chaptersExtracted = parseInt(localStorage.getItem('chaptersExtracted') || '0');
  18.  
  19. // Tạo panel ở bên phải trang
  20. function createPanel() {
  21. // Tạo container chính cho panel
  22. const panel = document.createElement('div');
  23. panel.style.position = 'fixed';
  24. panel.style.top = '100px';
  25. panel.style.right = '20px';
  26. panel.style.width = '300px';
  27. panel.style.backgroundColor = '#f9f9f9';
  28. panel.style.border = '1px solid #ddd';
  29. panel.style.borderRadius = '5px';
  30. panel.style.padding = '10px';
  31. panel.style.zIndex = '9999';
  32. panel.style.display = 'flex';
  33. panel.style.flexDirection = 'column';
  34. panel.style.gap = '10px';
  35. panel.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
  36.  
  37. // Tạo tiêu đề panel
  38. const title = document.createElement('h3');
  39. title.textContent = 'Trích xuất nội dung truyện';
  40. title.style.margin = '0 0 10px 0';
  41. title.style.textAlign = 'center';
  42. title.style.borderBottom = '1px solid #ddd';
  43. title.style.paddingBottom = '10px';
  44. panel.appendChild(title);
  45.  
  46. // Tạo textarea để hiển thị nội dung đã trích xuất
  47. const textarea = document.createElement('textarea');
  48. textarea.style.width = '100%';
  49. textarea.style.height = '200px';
  50. textarea.style.padding = '8px';
  51. textarea.style.borderRadius = '3px';
  52. textarea.style.border = '1px solid #ddd';
  53. textarea.style.marginBottom = '10px';
  54. textarea.style.resize = 'vertical';
  55. panel.appendChild(textarea);
  56.  
  57. // Lấy nội dung đã lưu từ localStorage (nếu có)
  58. const savedContent = localStorage.getItem('tangthuvien_extracted_content');
  59. if (savedContent) {
  60. textarea.value = savedContent;
  61. }
  62.  
  63. // Tạo container cho các nút
  64. const buttonContainer = document.createElement('div');
  65. buttonContainer.style.display = 'flex';
  66. buttonContainer.style.flexDirection = 'column';
  67. buttonContainer.style.gap = '5px';
  68. panel.appendChild(buttonContainer);
  69.  
  70. // Tạo nút bắt đầu trích xuất
  71. const startButton = document.createElement('button');
  72. startButton.textContent = 'Bắt đầu lấy text';
  73. startButton.style.padding = '8px';
  74. startButton.style.backgroundColor = '#4caf50';
  75. startButton.style.color = 'white';
  76. startButton.style.border = 'none';
  77. startButton.style.borderRadius = '3px';
  78. startButton.style.cursor = 'pointer';
  79. startButton.addEventListener('click', function() {
  80. if (!isAutomaticExtraction) {
  81. isAutomaticExtraction = true;
  82. localStorage.setItem('isAutoExtractionActive', 'true');
  83. startButton.textContent = 'Đang trích xuất...';
  84. startButton.style.backgroundColor = '#ff9800';
  85. // Đặt lại số chương khi bắt đầu lại quá trình
  86. if (!confirm("Tiếp tục đếm số chương đã trích xuất?")) {
  87. chaptersExtracted = 0;
  88. localStorage.setItem('chaptersExtracted', '0');
  89. }
  90. // Bắt đầu quá trình trích xuất tự động
  91. autoExtractAndNavigate();
  92. } else {
  93. isAutomaticExtraction = false;
  94. localStorage.setItem('isAutoExtractionActive', 'false');
  95. startButton.textContent = 'Bắt đầu lấy text';
  96. startButton.style.backgroundColor = '#4caf50';
  97. }
  98. });
  99. buttonContainer.appendChild(startButton);
  100.  
  101. // Tạo nút xóa nội dung
  102. const clearButton = document.createElement('button');
  103. clearButton.textContent = 'Xóa nội dung';
  104. clearButton.style.padding = '8px';
  105. clearButton.style.backgroundColor = '#f44336';
  106. clearButton.style.color = 'white';
  107. clearButton.style.border = 'none';
  108. clearButton.style.borderRadius = '3px';
  109. clearButton.style.cursor = 'pointer';
  110. clearButton.addEventListener('click', function() {
  111. textarea.value = '';
  112. localStorage.removeItem('tangthuvien_extracted_content');
  113. // Hiển thị thông báo
  114. const notification = document.createElement('div');
  115. notification.textContent = 'Đã xóa nội dung';
  116. notification.style.position = 'fixed';
  117. notification.style.bottom = '20px';
  118. notification.style.right = '20px';
  119. notification.style.backgroundColor = '#f44336';
  120. notification.style.color = 'white';
  121. notification.style.padding = '10px';
  122. notification.style.borderRadius = '5px';
  123. notification.style.zIndex = '10000';
  124. document.body.appendChild(notification);
  125. // Xóa thông báo sau 3 giây
  126. setTimeout(() => {
  127. document.body.removeChild(notification);
  128. }, 3000);
  129. });
  130. buttonContainer.appendChild(clearButton);
  131.  
  132. // Tạo nút sao chép nội dung
  133. const copyButton = document.createElement('button');
  134. copyButton.textContent = 'Sao chép nội dung';
  135. copyButton.style.padding = '8px';
  136. copyButton.style.backgroundColor = '#2196f3';
  137. copyButton.style.color = 'white';
  138. copyButton.style.border = 'none';
  139. copyButton.style.borderRadius = '3px';
  140. copyButton.style.cursor = 'pointer';
  141. copyButton.addEventListener('click', function() {
  142. textarea.select();
  143. document.execCommand('copy');
  144. // Hiển thị thông báo
  145. const notification = document.createElement('div');
  146. notification.textContent = 'Đã sao chép nội dung';
  147. notification.style.position = 'fixed';
  148. notification.style.bottom = '20px';
  149. notification.style.right = '20px';
  150. notification.style.backgroundColor = '#2196f3';
  151. notification.style.color = 'white';
  152. notification.style.padding = '10px';
  153. notification.style.borderRadius = '5px';
  154. notification.style.zIndex = '10000';
  155. document.body.appendChild(notification);
  156. // Xóa thông báo sau 3 giây
  157. setTimeout(() => {
  158. document.body.removeChild(notification);
  159. }, 3000);
  160. });
  161. buttonContainer.appendChild(copyButton);
  162.  
  163. // Thêm panel vào body
  164. document.body.appendChild(panel);
  165. // Trả về các phần tử để sử dụng sau này
  166. return {
  167. panel,
  168. textarea,
  169. startButton
  170. };
  171. }
  172.  
  173. // Hàm trích xuất nội dung chương hiện tại
  174. function extractCurrentChapterContent() {
  175. const panelElements = document.querySelector('textarea');
  176. const startButton = document.querySelector('button');
  177. if (!panelElements || !startButton) {
  178. console.error('Không tìm thấy panel hoặc textarea');
  179. return false;
  180. }
  181. // Lấy tiêu đề chương
  182. const chapterTitle = document.querySelector('h2');
  183. if (!chapterTitle) {
  184. console.error('Không tìm thấy tiêu đề chương');
  185. return false;
  186. }
  187. // Lấy nội dung chương
  188. const chapterContentSelector = '.box-chap';
  189. const chapterContent = document.querySelector(chapterContentSelector);
  190. if (!chapterContent) {
  191. console.error('Không tìm thấy nội dung chương');
  192. return false;
  193. }
  194. // Xử lý nội dung
  195. let contentText = chapterContent.innerText || chapterContent.textContent;
  196. contentText = contentText.trim();
  197. // Thêm nội dung vào textarea
  198. let currentContent = panelElements.value || '';
  199. // Thêm dấu phân cách nếu đã có nội dung trước đó
  200. if (currentContent !== '') {
  201. currentContent += '\n\n------------\n\n';
  202. }
  203. // Thêm tiêu đề và nội dung
  204. currentContent += chapterTitle.innerText + '\n\n' + contentText;
  205. // Cập nhật textarea và lưu vào localStorage
  206. panelElements.value = currentContent;
  207. localStorage.setItem('tangthuvien_extracted_content', currentContent);
  208. // Tăng số chương đã trích xuất và lưu vào localStorage
  209. chaptersExtracted++;
  210. localStorage.setItem('chaptersExtracted', chaptersExtracted);
  211. // Hiển thị thông báo
  212. const notification = document.createElement('div');
  213. notification.textContent = `Đã trích xut: ${chapterTitle.innerText} (Chương ${chaptersExtracted})`;
  214. notification.style.position = 'fixed';
  215. notification.style.bottom = '20px';
  216. notification.style.right = '320px';
  217. notification.style.backgroundColor = '#4caf50';
  218. notification.style.color = 'white';
  219. notification.style.padding = '10px';
  220. notification.style.borderRadius = '5px';
  221. notification.style.zIndex = '10000';
  222. document.body.appendChild(notification);
  223. // Xóa thông báo sau 3 giây
  224. setTimeout(() => {
  225. document.body.removeChild(notification);
  226. }, 3000);
  227. return true;
  228. }
  229.  
  230. // Biến đếm số lần thử trích xuất
  231. let extractionAttempts = 0;
  232. // Hàm tự động trích xuất và chuyển trang
  233. function autoExtractAndNavigate() {
  234. if (!isAutomaticExtraction) {
  235. return;
  236. }
  237. // Đặt lại biến đếm nếu quá trình tiếp tục
  238. extractionAttempts = 0;
  239. // Trích xuất nội dung chương hiện tại
  240. const extractionSuccess = extractCurrentChapterContent();
  241. if (extractionSuccess) {
  242. // Tìm nút chuyển đến chương tiếp theo
  243. const nextButton = document.querySelector('.bot-next_chap.bot-control');
  244. // Kiểm tra xem có thông báo "Bạn đã đọc đến chương mới nhất" hay không
  245. const pageContent = document.body.innerText || document.body.textContent;
  246. if (pageContent && pageContent.includes('Bạn đã đọc đến chương mới nhất')) {
  247. // Kết thúc quá trình khi thấy thông báo này
  248. console.log("Đã phát hiện thông báo: Bạn đã đọc đến chương mới nhất");
  249. isAutomaticExtraction = false;
  250. const startButton = document.querySelector('button');
  251. if (startButton) {
  252. startButton.textContent = 'Bắt đầu lấy text';
  253. startButton.style.backgroundColor = '#4caf50';
  254. }
  255. // Hiển thị thông báo hoàn thành
  256. const notification = document.createElement('div');
  257. notification.textContent = 'Đã hoàn thành trích xuất toàn bộ truyện!';
  258. notification.style.position = 'fixed';
  259. notification.style.bottom = '20px';
  260. notification.style.right = '320px';
  261. notification.style.backgroundColor = '#4caf50';
  262. notification.style.color = 'white';
  263. notification.style.padding = '10px';
  264. notification.style.borderRadius = '5px';
  265. notification.style.zIndex = '10000';
  266. document.body.appendChild(notification);
  267. // Xóa thông báo sau 5 giây
  268. setTimeout(() => {
  269. document.body.removeChild(notification);
  270. }, 5000);
  271. return;
  272. }
  273. if (nextButton) {
  274. // Hiển thị thông báo
  275. const notification = document.createElement('div');
  276. notification.textContent = 'Đang chuyển đến chương tiếp theo...';
  277. notification.style.position = 'fixed';
  278. notification.style.bottom = '20px';
  279. notification.style.right = '320px';
  280. notification.style.backgroundColor = '#ff9800';
  281. notification.style.color = 'white';
  282. notification.style.padding = '10px';
  283. notification.style.borderRadius = '5px';
  284. notification.style.zIndex = '10000';
  285. document.body.appendChild(notification);
  286. // Xóa thông báo sau 2 giây
  287. setTimeout(() => {
  288. document.body.removeChild(notification);
  289. }, 1000);
  290. // Nhấp vào nút chuyển trang
  291. nextButton.click();
  292. // Đợi 2 giây để trang mới tải xong, sau đó tiếp tục quá trình
  293. setTimeout(() => {
  294. console.log("Trang mới đã được tải, tiếp tục quá trình...");
  295. // Đánh dấu rõ ràng rằng quá trình tự động vẫn đang diễn ra
  296. window.localStorage.setItem('isAutoExtractionActive', 'true');
  297. isAutomaticExtraction = true;
  298. // Kiểm tra xem nút có bị ẩn hay không
  299. const startButton = document.querySelector('button');
  300. if (startButton) {
  301. startButton.textContent = 'Đang trích xuất...';
  302. startButton.style.backgroundColor = '#ff9800';
  303. }
  304. // Hiển thị thông báo đang đợi trang tải
  305. const notification = document.createElement('div');
  306. notification.textContent = 'Đợi trang tải xong...';
  307. notification.style.position = 'fixed';
  308. notification.style.bottom = '20px';
  309. notification.style.right = '320px';
  310. notification.style.backgroundColor = '#FFA500';
  311. notification.style.color = 'white';
  312. notification.style.padding = '10px';
  313. notification.style.borderRadius = '5px';
  314. notification.style.zIndex = '10000';
  315. document.body.appendChild(notification);
  316. // Xóa thông báo và bắt đầu trích xuất sau 3 giây (tăng thời gian cho trang tải đầy đủ)
  317. setTimeout(() => {
  318. try {
  319. document.body.removeChild(notification);
  320. } catch (error) {
  321. console.log("Thông báo đã bị xóa");
  322. }
  323. // Đảm bảo biến isAutomaticExtraction vẫn là true để tiếp tục quá trình
  324. isAutomaticExtraction = true;
  325. console.log("Bắt đầu trích xuất trang mới...");
  326. // Gọi lại hàm trích xuất để tiếp tục quá trình
  327. autoExtractAndNavigate();
  328. }, 3000);
  329. }, 3000);
  330. } else {
  331. // Nếu không tìm thấy nút chuyển trang
  332. isAutomaticExtraction = false;
  333. const startButton = document.querySelector('button');
  334. if (startButton) {
  335. startButton.textContent = 'Bắt đầu lấy text';
  336. startButton.style.backgroundColor = '#4caf50';
  337. }
  338. // Hiển thị thông báo
  339. const notification = document.createElement('div');
  340. notification.textContent = 'Không tìm thấy nút chuyển trang';
  341. notification.style.position = 'fixed';
  342. notification.style.bottom = '20px';
  343. notification.style.right = '320px';
  344. notification.style.backgroundColor = '#f44336';
  345. notification.style.color = 'white';
  346. notification.style.padding = '10px';
  347. notification.style.borderRadius = '5px';
  348. notification.style.zIndex = '10000';
  349. document.body.appendChild(notification);
  350. // Xóa thông báo sau 5 giây
  351. setTimeout(() => {
  352. document.body.removeChild(notification);
  353. }, 5000);
  354. }
  355. } else {
  356. // Nếu trích xuất thất bại
  357. extractionAttempts++;
  358. // Thử lại tối đa 3 lần nếu trích xuất thất bại
  359. if (extractionAttempts < 3) {
  360. console.log(`Th trích xut ln ${extractionAttempts}`);
  361. // Hiển thị thông báo
  362. const notification = document.createElement('div');
  363. notification.textContent = ang th li ln ${extractionAttempts}...`;
  364. notification.style.position = 'fixed';
  365. notification.style.bottom = '20px';
  366. notification.style.right = '320px';
  367. notification.style.backgroundColor = '#ff9800';
  368. notification.style.color = 'white';
  369. notification.style.padding = '10px';
  370. notification.style.borderRadius = '5px';
  371. notification.style.zIndex = '10000';
  372. document.body.appendChild(notification);
  373. // Xóa thông báo và thử lại sau 2 giây
  374. setTimeout(() => {
  375. document.body.removeChild(notification);
  376. autoExtractAndNavigate();
  377. }, 2000);
  378. return;
  379. }
  380. // Sau 3 lần thử vẫn thất bại
  381. isAutomaticExtraction = false;
  382. const startButton = document.querySelector('button');
  383. if (startButton) {
  384. startButton.textContent = 'Bắt đầu lấy text';
  385. startButton.style.backgroundColor = '#4caf50';
  386. }
  387. // Hiển thị thông báo
  388. const notification = document.createElement('div');
  389. notification.textContent = 'Trích xuất thất bại';
  390. notification.style.position = 'fixed';
  391. notification.style.bottom = '20px';
  392. notification.style.right = '320px';
  393. notification.style.backgroundColor = '#f44336';
  394. notification.style.color = 'white';
  395. notification.style.padding = '10px';
  396. notification.style.borderRadius = '5px';
  397. notification.style.zIndex = '10000';
  398. document.body.appendChild(notification);
  399. // Xóa thông báo sau 5 giây
  400. setTimeout(() => {
  401. document.body.removeChild(notification);
  402. }, 5000);
  403. }
  404. }
  405.  
  406. // Khởi tạo panel khi trang đã tải xong
  407. window.addEventListener('load', function() {
  408. // Đợi 1 giây để đảm bảo trang đã tải hoàn toàn
  409. setTimeout(() => {
  410. createPanel();
  411. }, 1000);
  412. });
  413. })();