Upload files to "FirefoxExtension"
This commit is contained in:
		
							
								
								
									
										86
									
								
								FirefoxExtension/background.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								FirefoxExtension/background.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| if (typeof browser === "undefined") { | ||||
|   var browser = chrome; | ||||
| } | ||||
|  | ||||
| let autoRecordInterval = null; | ||||
| let autoRecordAllowedPrefixes = []; // Global storage for allowed URL prefixes | ||||
|  | ||||
| browser.runtime.onMessage.addListener((message, sender, sendResponse) => { | ||||
|   if (message.action === "login") { | ||||
|     fetch("http://192.168.0.226:25570/api/login", { | ||||
|       method: "POST", | ||||
|       credentials: "include", | ||||
|       headers: { "Content-Type": "application/json" }, | ||||
|       body: JSON.stringify({ username: message.username, password: message.password }) | ||||
|     }) | ||||
|       .then(response => response.json()) | ||||
|       .then(data => { | ||||
|         console.log("Login response:", data); | ||||
|         sendResponse(data); | ||||
|       }) | ||||
|       .catch(err => sendResponse({ error: err.toString() })); | ||||
|     return true; | ||||
|   } else if (message.action === "register") { | ||||
|     fetch("http://192.168.0.226:25570/api/register", { | ||||
|       method: "POST", | ||||
|       credentials: "include", | ||||
|       headers: { "Content-Type": "application/json" }, | ||||
|       body: JSON.stringify({ username: message.username, password: message.password }) | ||||
|     }) | ||||
|       .then(response => response.json()) | ||||
|       .then(data => { | ||||
|         console.log("Registration response:", data); | ||||
|         sendResponse(data); | ||||
|       }) | ||||
|       .catch(err => sendResponse({ error: err.toString() })); | ||||
|     return true; | ||||
|   } else if (message.action === "startAutoRecord") { | ||||
|     if (message.allowedPrefixes) { | ||||
|       autoRecordAllowedPrefixes = message.allowedPrefixes | ||||
|         .split(",") | ||||
|         .map(s => s.trim()) | ||||
|         .filter(s => s.length > 0); | ||||
|     } else { | ||||
|       autoRecordAllowedPrefixes = []; | ||||
|     } | ||||
|     if (autoRecordInterval) { | ||||
|       clearInterval(autoRecordInterval); | ||||
|     } | ||||
|     autoRecordInterval = setInterval(() => { | ||||
|       browser.tabs.query({ active: true, currentWindow: true }, (tabs) => { | ||||
|         if (tabs && tabs.length > 0) { | ||||
|           const currentUrl = tabs[0].url; | ||||
|           let recordUrl = false; | ||||
|           for (let prefix of autoRecordAllowedPrefixes) { | ||||
|             if (currentUrl.startsWith(prefix)) { | ||||
|               recordUrl = true; | ||||
|               break; | ||||
|             } | ||||
|           } | ||||
|           if (recordUrl) { | ||||
|             fetch("http://192.168.0.226:25570/record", { | ||||
|               method: "POST", | ||||
|               credentials: "include", | ||||
|               headers: { "Content-Type": "application/json" }, | ||||
|               body: JSON.stringify({ url: currentUrl }) | ||||
|             }) | ||||
|               .then(response => response.json()) | ||||
|               .then(data => console.log("Recorded URL:", data)) | ||||
|               .catch(err => console.error("Recording error:", err)); | ||||
|           } else { | ||||
|             console.log("URL does not match allowed prefixes, skipping: " + currentUrl); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|     }, message.interval || 10000); | ||||
|     sendResponse({ success: true, message: "Auto recording started" }); | ||||
|     return true; | ||||
|   } else if (message.action === "stopAutoRecord") { | ||||
|     if (autoRecordInterval) { | ||||
|       clearInterval(autoRecordInterval); | ||||
|       autoRecordInterval = null; | ||||
|     } | ||||
|     sendResponse({ success: true, message: "Auto recording stopped" }); | ||||
|     return true; | ||||
|   } | ||||
| }); | ||||
							
								
								
									
										
											BIN
										
									
								
								FirefoxExtension/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								FirefoxExtension/icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										19
									
								
								FirefoxExtension/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								FirefoxExtension/manifest.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| { | ||||
|   "manifest_version": 2, | ||||
|   "name": "NovelBin Auto Recorder", | ||||
|   "version": "1.0", | ||||
|   "description": "Records the current chapter URL from NovelBin via the server, with account registration.", | ||||
|   "permissions": [ | ||||
|     "tabs", | ||||
|     "storage", | ||||
|     "http://192.168.0.226:25570/*" | ||||
|   ], | ||||
|   "background": { | ||||
|     "scripts": ["background.js"], | ||||
|     "persistent": true | ||||
|   }, | ||||
|   "browser_action": { | ||||
|     "default_popup": "popup.html", | ||||
|     "default_icon": "icon.png" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										79
									
								
								FirefoxExtension/popup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								FirefoxExtension/popup.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|   <title>NovelBin Auto Recorder</title> | ||||
|   <style> | ||||
|     body { | ||||
|       font-family: Arial, sans-serif; | ||||
|       padding: 10px; | ||||
|     } | ||||
|     .resizable { | ||||
|       resize: both; | ||||
|       overflow: auto; | ||||
|       border: 1px solid #ccc; | ||||
|       padding: 10px; | ||||
|       margin-bottom: 10px; | ||||
|     } | ||||
|     input[type="text"], | ||||
|     input[type="password"] { | ||||
|       width: 100%; | ||||
|       box-sizing: border-box; | ||||
|       padding: 5px; | ||||
|       margin-bottom: 5px; | ||||
|     } | ||||
|     button { | ||||
|       margin: 5px 0; | ||||
|     } | ||||
|     #authSection, #recorderSection, #chapterListSection { | ||||
|       min-width: 300px; | ||||
|     } | ||||
|     #chapterList li { | ||||
|       list-style: none; | ||||
|       padding: 3px 0; | ||||
|       border-bottom: 1px solid #ccc; | ||||
|     } | ||||
|   </style> | ||||
| </head> | ||||
| <body> | ||||
|   <!-- Authentication Section --> | ||||
|   <div id="authSection" class="resizable"> | ||||
|     <div id="loginSection"> | ||||
|       <h2>Login</h2> | ||||
|       <input type="text" id="loginUsername" placeholder="Username"><br/> | ||||
|       <input type="password" id="loginPassword" placeholder="Password"><br/> | ||||
|       <button id="loginBtn">Login</button> | ||||
|       <p>Don't have an account? <a href="#" id="showRegisterLink">Register here</a></p> | ||||
|     </div> | ||||
|     <div id="registerSection" style="display: none;"> | ||||
|       <h2>Register</h2> | ||||
|       <input type="text" id="registerUsername" placeholder="Username"><br/> | ||||
|       <input type="password" id="registerPassword" placeholder="Password"><br/> | ||||
|       <input type="password" id="registerConfirmPassword" placeholder="Confirm Password"><br/> | ||||
|       <button id="registerBtn">Register</button> | ||||
|       <p>Already have an account? <a href="#" id="showLoginLink">Login here</a></p> | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
|   <!-- Recorder Section --> | ||||
|   <div id="recorderSection" class="resizable" style="display: none;"> | ||||
|     <h2>Auto Record Settings</h2> | ||||
|     <label> | ||||
|       <input type="checkbox" id="autoRecordToggle"> Auto Record Current Chapter URL | ||||
|     </label><br/> | ||||
|     <input type="text" id="allowedPrefixes" placeholder="Enter allowed URL prefixes (comma separated)"><br/> | ||||
|     <button id="openResizableBtn">Open Resizable Window</button><br/> | ||||
|     <button id="logoutBtn">Logout</button> | ||||
|   </div> | ||||
|  | ||||
|   <!-- Chapter List Section --> | ||||
|   <div id="chapterListSection" class="resizable" style="display: none;"> | ||||
|     <h2>Recorded Chapters</h2> | ||||
|     <input type="text" id="searchQuery" placeholder="Search chapters..."><br/> | ||||
|     <input type="text" id="excludeWords" placeholder="Exclude words (comma separated)"><br/> | ||||
|     <ul id="chapterList"></ul> | ||||
|   </div> | ||||
|  | ||||
|   <div id="status"></div> | ||||
|   <script src="popup.js"></script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										200
									
								
								FirefoxExtension/popup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								FirefoxExtension/popup.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| if (typeof browser === "undefined") { | ||||
|   var browser = chrome; | ||||
| } | ||||
|  | ||||
| document.addEventListener('DOMContentLoaded', function() { | ||||
|   // Authentication elements | ||||
|   const authSection = document.getElementById('authSection'); | ||||
|   const loginSection = document.getElementById('loginSection'); | ||||
|   const registerSection = document.getElementById('registerSection'); | ||||
|   const loginBtn = document.getElementById('loginBtn'); | ||||
|   const loginUsernameInput = document.getElementById('loginUsername'); | ||||
|   const loginPasswordInput = document.getElementById('loginPassword'); | ||||
|   const showRegisterLink = document.getElementById('showRegisterLink'); | ||||
|   const registerBtn = document.getElementById('registerBtn'); | ||||
|   const registerUsernameInput = document.getElementById('registerUsername'); | ||||
|   const registerPasswordInput = document.getElementById('registerPassword'); | ||||
|   const registerConfirmPasswordInput = document.getElementById('registerConfirmPassword'); | ||||
|   const showLoginLink = document.getElementById('showLoginLink'); | ||||
|  | ||||
|   // Recorder and list elements | ||||
|   const recorderSection = document.getElementById('recorderSection'); | ||||
|   const autoRecordToggle = document.getElementById('autoRecordToggle'); | ||||
|   const allowedPrefixesInput = document.getElementById('allowedPrefixes'); | ||||
|   const logoutBtn = document.getElementById('logoutBtn'); | ||||
|   const openResizableBtn = document.getElementById('openResizableBtn'); | ||||
|   const chapterListSection = document.getElementById('chapterListSection'); | ||||
|   const searchQueryInput = document.getElementById('searchQuery'); | ||||
|   const excludeWordsInput = document.getElementById('excludeWords'); | ||||
|   const chapterListElement = document.getElementById('chapterList'); | ||||
|   const statusDiv = document.getElementById('status'); | ||||
|  | ||||
|   // Load stored allowedPrefixes from browser.storage | ||||
|   browser.storage.local.get('allowedPrefixes', function(result) { | ||||
|     if (result.allowedPrefixes) { | ||||
|       allowedPrefixesInput.value = result.allowedPrefixes; | ||||
|     } | ||||
|   }); | ||||
|   allowedPrefixesInput.addEventListener('change', function() { | ||||
|     const allowedPrefixes = allowedPrefixesInput.value.trim(); | ||||
|     browser.storage.local.set({ allowedPrefixes: allowedPrefixes }); | ||||
|   }); | ||||
|  | ||||
|   // Toggle between login and registration views | ||||
|   showRegisterLink.addEventListener('click', function(e) { | ||||
|     e.preventDefault(); | ||||
|     loginSection.style.display = "none"; | ||||
|     registerSection.style.display = "block"; | ||||
|   }); | ||||
|   showLoginLink.addEventListener('click', function(e) { | ||||
|     e.preventDefault(); | ||||
|     registerSection.style.display = "none"; | ||||
|     loginSection.style.display = "block"; | ||||
|   }); | ||||
|  | ||||
|   // Check login status on load | ||||
|   fetch("http://192.168.0.226:25570/api/status", { | ||||
|     method: "GET", | ||||
|     credentials: "include" | ||||
|   }) | ||||
|     .then(response => response.json()) | ||||
|     .then(data => { | ||||
|       if (data.loggedIn) { | ||||
|         authSection.style.display = "none"; | ||||
|         recorderSection.style.display = "block"; | ||||
|         chapterListSection.style.display = "block"; | ||||
|         statusDiv.textContent = "Logged in as " + data.username; | ||||
|       } else { | ||||
|         authSection.style.display = "block"; | ||||
|         recorderSection.style.display = "none"; | ||||
|         chapterListSection.style.display = "none"; | ||||
|       } | ||||
|     }) | ||||
|     .catch(err => { | ||||
|       console.error("Error checking login status:", err); | ||||
|     }); | ||||
|  | ||||
|   // Login handler | ||||
|   loginBtn.addEventListener('click', function() { | ||||
|     const username = loginUsernameInput.value.trim(); | ||||
|     const password = loginPasswordInput.value.trim(); | ||||
|     if (!username || !password) { | ||||
|       statusDiv.textContent = "Please enter username and password."; | ||||
|       return; | ||||
|     } | ||||
|     browser.runtime.sendMessage({ action: "login", username, password }, (response) => { | ||||
|       console.log("Login response:", response); | ||||
|       if (response.success) { | ||||
|         statusDiv.textContent = response.message; | ||||
|         authSection.style.display = "none"; | ||||
|         recorderSection.style.display = "block"; | ||||
|         chapterListSection.style.display = "block"; | ||||
|       } else { | ||||
|         statusDiv.textContent = response.message || "Login failed."; | ||||
|       } | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   // Registration handler | ||||
|   registerBtn.addEventListener('click', function() { | ||||
|     const username = registerUsernameInput.value.trim(); | ||||
|     const password = registerPasswordInput.value.trim(); | ||||
|     const confirmPassword = registerConfirmPasswordInput.value.trim(); | ||||
|     if (!username || !password || !confirmPassword) { | ||||
|       statusDiv.textContent = "Please fill in all registration fields."; | ||||
|       return; | ||||
|     } | ||||
|     if (password !== confirmPassword) { | ||||
|       statusDiv.textContent = "Passwords do not match."; | ||||
|       return; | ||||
|     } | ||||
|     browser.runtime.sendMessage({ action: "register", username, password }, (response) => { | ||||
|       console.log("Registration response:", response); | ||||
|       if (response.success) { | ||||
|         statusDiv.textContent = response.message; | ||||
|         authSection.style.display = "none"; | ||||
|         recorderSection.style.display = "block"; | ||||
|         chapterListSection.style.display = "block"; | ||||
|       } else { | ||||
|         statusDiv.textContent = response.message || "Registration failed."; | ||||
|       } | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   // Auto record toggle handler | ||||
|   autoRecordToggle.addEventListener('change', function(e) { | ||||
|     if (e.target.checked) { | ||||
|       const allowedPrefixes = allowedPrefixesInput.value.trim(); | ||||
|       browser.runtime.sendMessage({ action: "startAutoRecord", interval: 10000, allowedPrefixes }, (response) => { | ||||
|         statusDiv.textContent = response.message || response.error; | ||||
|       }); | ||||
|     } else { | ||||
|       browser.runtime.sendMessage({ action: "stopAutoRecord" }, (response) => { | ||||
|         statusDiv.textContent = response.message || response.error; | ||||
|       }); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   // Open resizable window button | ||||
|   openResizableBtn.addEventListener('click', function() { | ||||
|     browser.windows.create({ | ||||
|       url: browser.runtime.getURL("popup.html"), | ||||
|       type: "popup", | ||||
|       width: 600, | ||||
|       height: 600 | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   // Logout handler | ||||
|   logoutBtn.addEventListener('click', function() { | ||||
|     window.location.href = "http://192.168.0.226:25570/logout"; | ||||
|   }); | ||||
|  | ||||
|   // Function to update the chapter list | ||||
|   function updateChapterList() { | ||||
|     fetch("http://192.168.0.226:25570/logs", { | ||||
|       method: "GET", | ||||
|       credentials: "include" | ||||
|     }) | ||||
|       .then(response => response.json()) | ||||
|       .then(data => { | ||||
|         let chapters = data.updates || []; | ||||
|         // Sort alphabetically by URL | ||||
|         chapters.sort((a, b) => a.url.localeCompare(b.url)); | ||||
|         // Filter by search query (case-insensitive) | ||||
|         const searchQuery = searchQueryInput.value.trim().toLowerCase(); | ||||
|         if (searchQuery) { | ||||
|           chapters = chapters.filter(item => item.url.toLowerCase().includes(searchQuery)); | ||||
|         } | ||||
|         // Filter out entries with excluded words | ||||
|         const excludeWordsStr = excludeWordsInput.value.trim().toLowerCase(); | ||||
|         if (excludeWordsStr) { | ||||
|           const excludeWords = excludeWordsStr.split(",").map(s => s.trim()).filter(s => s.length > 0); | ||||
|           if (excludeWords.length > 0) { | ||||
|             chapters = chapters.filter(item => { | ||||
|               for (const word of excludeWords) { | ||||
|                 if (item.url.toLowerCase().includes(word)) { | ||||
|                   return false; | ||||
|                 } | ||||
|               } | ||||
|               return true; | ||||
|             }); | ||||
|           } | ||||
|         } | ||||
|         // Update the chapter list UI | ||||
|         chapterListElement.innerHTML = ""; | ||||
|         chapters.forEach(item => { | ||||
|           const li = document.createElement("li"); | ||||
|           li.textContent = item.url + " (" + new Date(item.timestamp).toLocaleTimeString() + ")"; | ||||
|           chapterListElement.appendChild(li); | ||||
|         }); | ||||
|       }) | ||||
|       .catch(err => { | ||||
|         console.error("Error fetching logs:", err); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   setInterval(updateChapterList, 5000); | ||||
|   searchQueryInput.addEventListener('input', updateChapterList); | ||||
|   excludeWordsInput.addEventListener('input', updateChapterList); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user