Tải toàn bộ chương Tangthuvien

Tự động tải tất cả chương từ trang danh sách chương của truyện trên Tangthuvien

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

  1. // ==UserScript==
  2. // @name Tải toàn bộ chương Tangthuvien
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Tự động tải tất cả chương từ trang danh sách chương của truyện trên Tangthuvien
  6. // @match https://truyen.tangthuvien.vn/doc-truyen/*
  7. // @grant GM_xmlhttpRequest
  8. // @connect truyen.tangthuvien.vn
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. 'use strict';
  13.  
  14. if (!location.href.includes('/doc-truyen/')) return;
  15.  
  16. const textarea = document.createElement('textarea');
  17. textarea.style.cssText = 'width:100%; height:300px; margin-bottom:10px; white-space: pre-wrap;';
  18.  
  19. const container = document.createElement('div');
  20. container.style.cssText = `
  21. position: fixed; top: 10%; right: 10px; z-index: 9999;
  22. width: 400px; background: white; border: 1px solid #ccc;
  23. padding: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.3);
  24. `;
  25.  
  26. const startBtn = document.createElement('button');
  27. startBtn.textContent = 'Bắt đầu tải';
  28. startBtn.style.cssText = 'width: 100%; padding: 8px; margin-bottom: 10px;';
  29.  
  30. const copyBtn = document.createElement('button');
  31. copyBtn.textContent = 'Sao chép';
  32. copyBtn.style.cssText = 'width: 100%; padding: 8px;';
  33.  
  34. container.appendChild(textarea);
  35. container.appendChild(startBtn);
  36. container.appendChild(copyBtn);
  37. document.body.appendChild(container);
  38.  
  39. function sleep(ms) {
  40. return new Promise(resolve => setTimeout(resolve, ms));
  41. }
  42.  
  43. function fetchChapter(url) {
  44. return new Promise((resolve) => {
  45. GM_xmlhttpRequest({
  46. method: "GET",
  47. url: url,
  48. onload: function (response) {
  49. const parser = new DOMParser();
  50. const doc = parser.parseFromString(response.responseText, 'text/html');
  51. const title = doc.querySelector('h2')?.innerText.trim() || 'Không tiêu đề';
  52. const content = doc.querySelector('.box-chap')?.innerText.trim() || 'Không có nội dung';
  53. resolve(`\n\n------------\n\n${title}\n\n${content}`);
  54. },
  55. onerror: () => resolve(`\n\n[Không ti được chương: ${url}]`)
  56. });
  57. });
  58. }
  59.  
  60. startBtn.addEventListener('click', async () => {
  61. startBtn.disabled = true;
  62. startBtn.textContent = 'Đang tải...';
  63.  
  64. const links = [...document.querySelectorAll('a')]
  65. .filter(a => a.href.includes('/chuong-') && a.href.includes(location.pathname))
  66. .sort((a, b) => {
  67. const n1 = parseInt(a.textContent.match(/\d+/)?.[0] || 0);
  68. const n2 = parseInt(b.textContent.match(/\d+/)?.[0] || 0);
  69. return n1 - n2;
  70. });
  71.  
  72. if (links.length === 0) {
  73. textarea.value = 'Không tìm thấy danh sách chương!';
  74. startBtn.disabled = false;
  75. startBtn.textContent = 'Bắt đầu tải';
  76. return;
  77. }
  78.  
  79. for (const link of links) {
  80. const url = link.href;
  81. textarea.value += `\nĐang ti: ${url}`;
  82. const data = await fetchChapter(url);
  83. textarea.value += data;
  84. await sleep(1500); // nghỉ 1.5s giữa mỗi chương để tránh bị chặn
  85. }
  86.  
  87. startBtn.textContent = 'Đã xong!';
  88. startBtn.disabled = false;
  89. });
  90.  
  91. copyBtn.addEventListener('click', () => {
  92. textarea.select();
  93. document.execCommand('copy');
  94. alert('Đã sao chép nội dung vào clipboard!');
  95. });
  96. })();