initial commit
This commit is contained in:
0
packages/KomSearcher/README.md
Normal file
0
packages/KomSearcher/README.md
Normal file
53
packages/KomSearcher/background.js
Normal file
53
packages/KomSearcher/background.js
Normal file
@@ -0,0 +1,53 @@
|
||||
// This is the background script for the extension
|
||||
console.log('Background script running');
|
||||
|
||||
// Listen for messages from the content script
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.action === 'search') {
|
||||
const query = message.query;
|
||||
const apiUrl = `http://127.0.0.1:5001/api/search?q=${encodeURIComponent(query)}`; // Add the http:// protocol to the URL
|
||||
|
||||
console.log(`Searching for: ${query}`);
|
||||
fetch(apiUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const results = data.results || [];
|
||||
console.log('Search results:', results);
|
||||
sendResponse({ results });
|
||||
|
||||
// Send the results to the popup
|
||||
chrome.runtime.sendMessage({ action: 'displayResults', results });
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching search results:', error);
|
||||
sendResponse({ results: [] });
|
||||
});
|
||||
|
||||
// Return true to indicate that the response will be sent asynchronously
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Periodically check the active tab's URL every 0.5 seconds
|
||||
setInterval(() => {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (tabs.length > 0) {
|
||||
const activeTab = tabs[0];
|
||||
const url = new URL(activeTab.url);
|
||||
const hostname = url.hostname;
|
||||
|
||||
// List of supported sites
|
||||
const supportedSites = [
|
||||
'mangadex.org',
|
||||
'anilist.co',
|
||||
'kitsu.app',
|
||||
'myanimelist.net'
|
||||
];
|
||||
|
||||
if (supportedSites.includes(hostname)) {
|
||||
console.log(`Sending addLens message to content script for hostname: ${hostname}`);
|
||||
chrome.tabs.sendMessage(activeTab.id, { action: 'addLens', hostname });
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
84
packages/KomSearcher/content.js
Normal file
84
packages/KomSearcher/content.js
Normal file
@@ -0,0 +1,84 @@
|
||||
// Detect the active site and add a magnifying lens in the appropriate location
|
||||
const siteHandlers = {
|
||||
'mangadex.org': () => {
|
||||
const headers = document.querySelectorAll('div.title p.mb-1'); // Select the text inside p.mb-1 within div.title
|
||||
headers.forEach(header => addLens(header));
|
||||
},
|
||||
'anilist.co': () => {
|
||||
const headers = document.querySelectorAll('.header-title');
|
||||
headers.forEach(header => addLens(header));
|
||||
},
|
||||
'kitsu.app': () => {
|
||||
const headers = document.querySelectorAll('section.media--title h3'); // Select the text inside h3 within section.media--title
|
||||
headers.forEach(header => addLens(header));
|
||||
},
|
||||
'myanimelist.net': () => {
|
||||
const headers = document.querySelectorAll('.title-name');
|
||||
headers.forEach(header => addLens(header));
|
||||
}
|
||||
};
|
||||
|
||||
function addLens(header) {
|
||||
// Check if the lens is already present
|
||||
if (header.querySelector('span.lens-icon')) {
|
||||
return; // Exit if the lens is already added
|
||||
}
|
||||
|
||||
const lens = document.createElement('span');
|
||||
lens.textContent = '🔍';
|
||||
lens.className = 'lens-icon'; // Add a class to identify the lens
|
||||
lens.style.cursor = 'pointer';
|
||||
lens.style.marginLeft = '10px';
|
||||
lens.title = 'Search for this title?'; // Add a tooltip to the lens
|
||||
lens.addEventListener('click', () => {
|
||||
const query = header.textContent.trim().replace('🔍', '').trim(); // Remove the lens icon from the query
|
||||
console.log(`Searching for title: ${query}`);
|
||||
chrome.runtime.sendMessage({ action: 'search', query }, (response) => {
|
||||
if (response && response.results) {
|
||||
displayResults(response.results);
|
||||
}
|
||||
});
|
||||
});
|
||||
header.appendChild(lens);
|
||||
}
|
||||
|
||||
function displayResults(results) {
|
||||
const resultWindow = document.createElement('div');
|
||||
resultWindow.style.position = 'fixed';
|
||||
resultWindow.style.top = '10px';
|
||||
resultWindow.style.right = '10px';
|
||||
resultWindow.style.backgroundColor = 'white';
|
||||
resultWindow.style.border = '1px solid black';
|
||||
resultWindow.style.padding = '10px';
|
||||
resultWindow.style.zIndex = '10000';
|
||||
resultWindow.style.maxHeight = '400px';
|
||||
resultWindow.style.overflowY = 'auto'; // Make the result window scrollable
|
||||
|
||||
// Ensure results are displayed as titles
|
||||
resultWindow.innerHTML = results.map(r => `<p>${r.title || 'No title available'}</p>`).join('');
|
||||
|
||||
// Remove any existing result window before appending a new one
|
||||
const existingWindow = document.querySelector('.result-window');
|
||||
if (existingWindow) {
|
||||
existingWindow.remove();
|
||||
}
|
||||
|
||||
resultWindow.className = 'result-window';
|
||||
document.body.appendChild(resultWindow);
|
||||
}
|
||||
|
||||
// Execute the handler for the current site immediately on page load
|
||||
const hostname = window.location.hostname;
|
||||
if (siteHandlers[hostname]) {
|
||||
siteHandlers[hostname]();
|
||||
}
|
||||
|
||||
// Listen for messages from the background script
|
||||
chrome.runtime.onMessage.addListener((message) => {
|
||||
if (message.action === 'addLens') {
|
||||
const hostname = message.hostname;
|
||||
if (siteHandlers[hostname]) {
|
||||
siteHandlers[hostname]();
|
||||
}
|
||||
}
|
||||
});
|
||||
0
packages/KomSearcher/icon.png
Normal file
0
packages/KomSearcher/icon.png
Normal file
25
packages/KomSearcher/manifest.json
Normal file
25
packages/KomSearcher/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "KomSearcher",
|
||||
"version": "1.0",
|
||||
"description": "A Firefox extension used to request manga from various sites.",
|
||||
"permissions": ["activeTab", "tabs"],
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
"browser_action": {
|
||||
"default_icon": "icon.png",
|
||||
"default_title": "KomSearcher"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://mangadex.org/*",
|
||||
"https://anilist.co/*",
|
||||
"https://kitsu.app/*",
|
||||
"https://myanimelist.net/*"
|
||||
],
|
||||
"js": ["content.js"]
|
||||
}
|
||||
]
|
||||
}
|
||||
62
packages/KomSearcher/popup.html
Normal file
62
packages/KomSearcher/popup.html
Normal file
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Search Results</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 10px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto; /* Make the popup scrollable */
|
||||
}
|
||||
.result {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Search Results</h1>
|
||||
<div id="results"></div>
|
||||
<script>
|
||||
// Helper function to calculate similarity between two strings
|
||||
function calculateSimilarity(searchTitle, resultTitle) {
|
||||
const searchWords = searchTitle.toLowerCase().split(/\s+/);
|
||||
const resultWords = resultTitle.toLowerCase().split(/\s+/);
|
||||
const commonWords = searchWords.filter(word => resultWords.includes(word));
|
||||
return (commonWords.length / Math.max(searchWords.length, resultWords.length)) * 100;
|
||||
}
|
||||
|
||||
// This script will populate the results dynamically
|
||||
chrome.runtime.onMessage.addListener((message) => {
|
||||
if (message.action === 'displayResults' && message.results) {
|
||||
const resultsDiv = document.getElementById('results');
|
||||
const searchTitle = message.searchTitle || '';
|
||||
|
||||
// Sort results by similarity
|
||||
const sortedResults = message.results.map(result => {
|
||||
const similarity = calculateSimilarity(searchTitle, result.title || '');
|
||||
return { ...result, similarity };
|
||||
}).sort((a, b) => b.similarity - a.similarity);
|
||||
|
||||
// Render results
|
||||
resultsDiv.innerHTML = sortedResults.map(result => {
|
||||
const similarity = result.similarity.toFixed(2);
|
||||
return `
|
||||
<div class='result'>
|
||||
<div style="position: relative;">
|
||||
<span style="position: absolute; top: 0; left: 0; background: #007bff; color: white; padding: 2px 5px; border-radius: 3px; font-size: 12px;">${similarity}%</span>
|
||||
</div>
|
||||
<h3>${result.title || 'No title available'}</h3>
|
||||
<p>Filetypes: ${result.filetypes || 'Unknown'}</p>
|
||||
<p>Size: ${result.size || 'Unknown'}</p>
|
||||
<span style="background: #28a745; color: white; padding: 2px 5px; border-radius: 3px; font-size: 12px;">Volumes: ${result.volumes || 0}</span>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user