Add a control panel for novel reading on truyen.tangthuvien.net
目前為
// ==UserScript==
// @name Chapter Downloader
// @namespace http://tampermonkey.net/
// @version 1.9
// @description Add a control panel for novel reading on truyen.tangthuvien.net
// @author You
// @match https://truyen.tangthuvien.net/doc-truyen/*
// @match https://truyen.tangthuvien.net/doc-truyen/*/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Create panel container
const panel = document.createElement('div');
panel.id = 'reader-panel';
panel.style.cssText = `
position: fixed;
top: 10%;
right: 10px;
width: 300px;
background-color: #f8f8f8;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
z-index: 9999;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
font-family: Arial, sans-serif;
`;
// Create textarea
const textarea = document.createElement('textarea');
textarea.id = 'content-textarea';
textarea.style.cssText = `
width: 100%;
height: 300px;
margin-bottom: 10px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
`;
// Create start button
const startButton = document.createElement('button');
startButton.textContent = 'Bắt đầu';
startButton.style.cssText = `
width: 100%;
padding: 8px;
margin-bottom: 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
// Create container for getTextButton and nextChapterButton (same row)
const buttonRow1 = document.createElement('div');
buttonRow1.style.cssText = `
display: flex;
justify-content: space-between;
margin-bottom: 10px;
`;
// Create get text button
const getTextButton = document.createElement('button');
getTextButton.textContent = 'Lấy Text';
getTextButton.style.cssText = `
flex: 1;
padding: 8px;
margin-right: 5px;
background-color: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
// Create next chapter button
const nextChapterButton = document.createElement('button');
nextChapterButton.textContent = 'Chương Tiếp';
nextChapterButton.style.cssText = `
flex: 1;
padding: 8px;
margin-left: 5px;
background-color: #ff9800;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
// Create container for clearButton and copyButton (same row)
const buttonRow2 = document.createElement('div');
buttonRow2.style.cssText = `
display: flex;
justify-content: space-between;
`;
// Create clear button
const clearButton = document.createElement('button');
clearButton.textContent = 'Xoá';
clearButton.style.cssText = `
flex: 1;
padding: 8px;
margin-right: 5px;
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
// Create copy button
const copyButton = document.createElement('button');
copyButton.textContent = 'Sao Chép';
copyButton.style.cssText = `
flex: 1;
padding: 8px;
margin-left: 5px;
background-color: #9c27b0;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
`;
// Add buttons to their respective row containers
buttonRow1.appendChild(getTextButton);
buttonRow1.appendChild(nextChapterButton);
buttonRow2.appendChild(clearButton);
buttonRow2.appendChild(copyButton);
// Add all elements to panel
panel.appendChild(textarea);
panel.appendChild(startButton);
panel.appendChild(buttonRow1);
panel.appendChild(buttonRow2);
// Add panel to body
document.body.appendChild(panel);
// Load saved content from localStorage if exists
const savedContent = localStorage.getItem('chapterDownloaderContent');
if (savedContent) {
textarea.value = savedContent;
}
// Function to save content to localStorage
function saveContent() {
localStorage.setItem('chapterDownloaderContent', textarea.value);
console.log('Content saved to localStorage');
}
// Auto save when textarea value changes
textarea.addEventListener('input', saveContent);
// Function to extract and add chapter content
function extractChapterContent() {
// Get chapter title
const titleElement = document.querySelector('h2');
// Get chapter content
const contentElement = document.querySelector('.box-chap');
if (titleElement && contentElement) {
// Append to existing content rather than replacing
const title = titleElement.innerText.trim();
const content = contentElement.innerText.trim();
// Append with proper formatting
if (textarea.value) {
textarea.value += '\n\n------------\n\n' + title + '\n\n' + content;
} else {
textarea.value = title + '\n\n' + content;
}
// Save content after adding new chapter
saveContent();
return true;
} else {
console.log('Could not find title or content elements');
return false;
}
}
// Function to navigate to next chapter
function goToNextChapter() {
// Find the next chapter button with the correct selector
const nextChapterLink = document.querySelector('.bot-next_chap.bot-control');
if (nextChapterLink) {
console.log('Found next chapter link, navigating...');
nextChapterLink.click();
return true;
} else {
console.log('Could not find next chapter link with class ".bot-next_chap.bot-control"');
// Try alternative selectors if the main one doesn't work
const alternativeNextLinks = document.querySelectorAll('a[href*="chuong"]');
for (const link of alternativeNextLinks) {
if (link.textContent.includes('tiếp') || link.textContent.includes('sau') || link.textContent.includes('next')) {
console.log('Found alternative next chapter link, navigating...');
link.click();
return true;
}
}
return false;
}
}
// Variable to track auto-downloading state
let isAutoDownloading = false;
// Function to handle automatic downloading
function startAutoDownloading() {
if (!isAutoDownloading) return;
console.log('Auto-downloading: Extracting chapter content...');
if (extractChapterContent()) {
// After extracting, wait 1 second then navigate to next chapter
setTimeout(() => {
if (!isAutoDownloading) return;
console.log('Auto-downloading: Navigating to next chapter...');
if (goToNextChapter()) {
// After navigation, wait 2 seconds for page to load then extract again
setTimeout(() => {
if (isAutoDownloading) {
startAutoDownloading();
}
}, 2000); // Wait 2 seconds for page to load before extracting next chapter
} else {
console.log('Auto-downloading: Could not find next chapter, stopping.');
isAutoDownloading = false;
startButton.textContent = 'Bắt đầu';
startButton.style.backgroundColor = '#4CAF50';
}
}, 1000); // Wait 1 second before navigating
} else {
console.log('Auto-downloading: Could not extract chapter content, stopping.');
isAutoDownloading = false;
startButton.textContent = 'Bắt đầu';
startButton.style.backgroundColor = '#4CAF50';
}
}
// Add event listeners
startButton.addEventListener('click', () => {
// Toggle auto-downloading state
isAutoDownloading = !isAutoDownloading;
if (isAutoDownloading) {
console.log('Start button clicked: Beginning auto-download sequence');
startButton.textContent = 'Dừng';
startButton.style.backgroundColor = '#f44336'; // Red color to indicate active
startAutoDownloading(); // Start the sequence
} else {
console.log('Start button clicked: Stopping auto-download sequence');
startButton.textContent = 'Bắt đầu';
startButton.style.backgroundColor = '#4CAF50'; // Green color when inactive
}
});
getTextButton.addEventListener('click', () => {
console.log('Get text button clicked');
extractChapterContent();
});
nextChapterButton.addEventListener('click', () => {
console.log('Next chapter button clicked');
goToNextChapter();
});
clearButton.addEventListener('click', () => {
console.log('Clear button clicked');
textarea.value = '';
saveContent(); // Save empty content to localStorage
});
copyButton.addEventListener('click', () => {
console.log('Copy button clicked');
textarea.select();
document.execCommand('copy');
// Show a temporary copy notification
const notification = document.createElement('div');
notification.textContent = 'Đã sao chép!';
notification.style.cssText = `
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0,0,0,0.8);
color: white;
padding: 10px 20px;
border-radius: 4px;
z-index: 10000;
`;
document.body.appendChild(notification);
// Remove notification after 2 seconds
setTimeout(() => {
document.body.removeChild(notification);
}, 2000);
});
// Make panel draggable
let isDragging = false;
let offsetX, offsetY;
panel.addEventListener('mousedown', (e) => {
if (e.target === panel) {
isDragging = true;
offsetX = e.clientX - panel.getBoundingClientRect().left;
offsetY = e.clientY - panel.getBoundingClientRect().top;
}
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
panel.style.left = (e.clientX - offsetX) + 'px';
panel.style.top = (e.clientY - offsetY) + 'px';
panel.style.right = 'auto';
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
// Add keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Only handle if not typing in a text field
if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT') {
// Alt+G: Get text
if (e.altKey && e.key === 'g') {
extractChapterContent();
e.preventDefault();
}
// Alt+N: Next chapter
else if (e.altKey && e.key === 'n') {
goToNextChapter();
e.preventDefault();
}
// Alt+S: Toggle auto-downloading (same as clicking Start/Stop button)
else if (e.altKey && e.key === 's') {
// Simulate clicking the start button
startButton.click();
e.preventDefault();
}
// Alt+C: Copy text
else if (e.altKey && e.key === 'c') {
textarea.select();
document.execCommand('copy');
e.preventDefault();
}
}
});
// Add information about keyboard shortcuts
const shortcutsInfo = document.createElement('div');
shortcutsInfo.style.cssText = `
font-size: 11px;
color: #666;
margin-top: 10px;
padding-top: 5px;
border-top: 1px solid #ddd;
`;
shortcutsInfo.innerHTML = `
<b>Phím tắt:</b> Alt+G (Lấy text), Alt+N (Chương tiếp), Alt+S (Bắt đầu/Dừng tự động), Alt+C (Sao chép)
`;
panel.appendChild(shortcutsInfo);
console.log('Chapter Downloader script initialized successfully');
})();