Hitomi.la Language Filter komplett überarbeitet
This commit is contained in:
@@ -62,7 +62,7 @@ Sendet Magnet-Links und `.torrent`-Dateien direkt an einen Transmission-Server i
|
|||||||
Filtert Manga auf hitomi.la nach Sprache. Zeigt nur Inhalte in der ausgewählten Sprache an.
|
Filtert Manga auf hitomi.la nach Sprache. Zeigt nur Inhalte in der ausgewählten Sprache an.
|
||||||
|
|
||||||
**Funktionen:**
|
**Funktionen:**
|
||||||
- Dropdown-Menü oben rechts zur Sprachauswahl
|
- Dropdown-Menü zur Sprachauswahl
|
||||||
- Unterstützt 15+ Sprachen (Englisch, Japanisch, Koreanisch, Chinesisch, Deutsch, etc.)
|
- Unterstützt 15+ Sprachen (Englisch, Japanisch, Koreanisch, Chinesisch, Deutsch, etc.)
|
||||||
- Filtert Galerien automatisch und in Echtzeit
|
- Filtert Galerien automatisch und in Echtzeit
|
||||||
- Speichert Spracheinstellung persistent (Standard: Englisch)
|
- Speichert Spracheinstellung persistent (Standard: Englisch)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Hitomi.la Language Filter
|
// @name Hitomi.la Language Filter
|
||||||
// @namespace https://git.ponywave.de/Akamaru/Userscripts
|
// @namespace https://git.ponywave.de/Akamaru/Userscripts
|
||||||
// @version 1.1
|
// @version 2.0
|
||||||
// @description Filter manga by language on hitomi.la
|
// @description Filter manga by language on hitomi.la with improved integration
|
||||||
// @author Akamaru
|
// @author Akamaru
|
||||||
// @match https://hitomi.la/*
|
// @match https://hitomi.la/*
|
||||||
// @icon https://www.google.com/s2/favicons?domain=hitomi.la&sz=32
|
// @icon https://www.google.com/s2/favicons?domain=hitomi.la&sz=32
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Available languages
|
// Available languages (matching hitomi.la's language options)
|
||||||
const languages = {
|
const languages = {
|
||||||
'all': 'All Languages',
|
'all': 'All Languages',
|
||||||
'english': 'English',
|
'english': 'English',
|
||||||
@@ -34,39 +34,85 @@
|
|||||||
'vietnamese': 'Vietnamese',
|
'vietnamese': 'Vietnamese',
|
||||||
'polish': 'Polish',
|
'polish': 'Polish',
|
||||||
'indonesian': 'Indonesian',
|
'indonesian': 'Indonesian',
|
||||||
'turkish': 'Turkish'
|
'turkish': 'Turkish',
|
||||||
|
'dutch': 'Dutch',
|
||||||
|
'norwegian': 'Norwegian',
|
||||||
|
'finnish': 'Finnish',
|
||||||
|
'swedish': 'Swedish',
|
||||||
|
'tagalog': 'Tagalog',
|
||||||
|
'arabic': 'Arabic',
|
||||||
|
'hebrew': 'Hebrew'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get saved language preference (default: english)
|
// Get saved language preference (default: all)
|
||||||
let selectedLanguage = GM_getValue('selectedLanguage', 'english');
|
let selectedLanguage = GM_getValue('selectedLanguage', 'all');
|
||||||
|
|
||||||
// Create language selector UI
|
// Create language selector UI integrated into the page
|
||||||
function createLanguageSelector() {
|
function createLanguageSelector() {
|
||||||
const container = document.createElement('div');
|
// Find the list-title section where "Order by" dropdown is located
|
||||||
container.id = 'language-filter-container';
|
const listTitle = document.querySelector('.list-title');
|
||||||
container.style.cssText = `
|
if (!listTitle) {
|
||||||
position: fixed;
|
console.warn('Hitomi Language Filter: .list-title not found');
|
||||||
top: 10px;
|
return;
|
||||||
right: 10px;
|
}
|
||||||
z-index: 10000;
|
|
||||||
background: #fff;
|
// Make list-title use flexbox for horizontal layout
|
||||||
border: 2px solid #333;
|
listTitle.style.cssText = `
|
||||||
border-radius: 5px;
|
display: flex !important;
|
||||||
padding: 10px;
|
flex-direction: row !important;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
align-items: center !important;
|
||||||
|
justify-content: space-between !important;
|
||||||
|
gap: 15px !important;
|
||||||
|
flex-wrap: nowrap !important;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const label = document.createElement('label');
|
// Fix h3 margin to prevent layout issues
|
||||||
label.textContent = 'Language: ';
|
const h3 = listTitle.querySelector('h3');
|
||||||
label.style.fontWeight = 'bold';
|
if (h3) {
|
||||||
|
h3.style.marginBottom = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a wrapper for the dropdowns to keep them together on the right
|
||||||
|
let dropdownWrapper = document.querySelector('.header-sort-wrapper');
|
||||||
|
if (!dropdownWrapper) {
|
||||||
|
dropdownWrapper = document.createElement('div');
|
||||||
|
dropdownWrapper.className = 'header-sort-wrapper';
|
||||||
|
dropdownWrapper.style.cssText = `
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix position and float of existing header-sort-select to work with flexbox
|
||||||
|
const existingSort = document.querySelector('.header-sort-select');
|
||||||
|
if (existingSort) {
|
||||||
|
existingSort.style.cssText = `
|
||||||
|
position: relative !important;
|
||||||
|
float: none !important;
|
||||||
|
top: auto !important;
|
||||||
|
right: auto !important;
|
||||||
|
`;
|
||||||
|
// Move existing sort into wrapper if not already there
|
||||||
|
if (existingSort.parentElement !== dropdownWrapper) {
|
||||||
|
dropdownWrapper.appendChild(existingSort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create container for the filter
|
||||||
|
const filterContainer = document.createElement('div');
|
||||||
|
filterContainer.id = 'language-filter-container';
|
||||||
|
filterContainer.className = 'header-sort-select';
|
||||||
|
filterContainer.style.cssText = `
|
||||||
|
position: relative !important;
|
||||||
|
float: none !important;
|
||||||
|
top: auto !important;
|
||||||
|
right: auto !important;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Create select element (no label)
|
||||||
const select = document.createElement('select');
|
const select = document.createElement('select');
|
||||||
select.style.cssText = `
|
select.id = 'language-filter-dropdown';
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-left: 5px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Add options
|
// Add options
|
||||||
for (const [code, name] of Object.entries(languages)) {
|
for (const [code, name] of Object.entries(languages)) {
|
||||||
@@ -84,17 +130,28 @@
|
|||||||
selectedLanguage = this.value;
|
selectedLanguage = this.value;
|
||||||
GM_setValue('selectedLanguage', selectedLanguage);
|
GM_setValue('selectedLanguage', selectedLanguage);
|
||||||
filterGalleries();
|
filterGalleries();
|
||||||
|
updateVisibleCount();
|
||||||
});
|
});
|
||||||
|
|
||||||
container.appendChild(label);
|
filterContainer.appendChild(select);
|
||||||
container.appendChild(select);
|
|
||||||
document.body.appendChild(container);
|
// Add filter to the dropdown wrapper
|
||||||
|
dropdownWrapper.appendChild(filterContainer);
|
||||||
|
|
||||||
|
// Add wrapper to list-title if not already there
|
||||||
|
if (!dropdownWrapper.parentElement) {
|
||||||
|
listTitle.appendChild(dropdownWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all gallery elements (including all types)
|
||||||
|
function getAllGalleries() {
|
||||||
|
return document.querySelectorAll('.gallery, .dj, .manga, .acg, .imageset');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter galleries based on selected language
|
// Filter galleries based on selected language
|
||||||
function filterGalleries() {
|
function filterGalleries() {
|
||||||
// Find all gallery items (both classes used on the site)
|
const galleries = getAllGalleries();
|
||||||
const galleries = document.querySelectorAll('.gallery, .dj, .manga');
|
|
||||||
|
|
||||||
galleries.forEach(gallery => {
|
galleries.forEach(gallery => {
|
||||||
if (selectedLanguage === 'all') {
|
if (selectedLanguage === 'all') {
|
||||||
@@ -105,6 +162,7 @@
|
|||||||
// Check for language link in format /index-LANGUAGE.html
|
// Check for language link in format /index-LANGUAGE.html
|
||||||
const languageTag = gallery.querySelector('a[href*="/index-"][href$=".html"]');
|
const languageTag = gallery.querySelector('a[href*="/index-"][href$=".html"]');
|
||||||
if (!languageTag) {
|
if (!languageTag) {
|
||||||
|
// Hide if no language tag found
|
||||||
gallery.style.display = 'none';
|
gallery.style.display = 'none';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -114,42 +172,91 @@
|
|||||||
|
|
||||||
if (langMatch && langMatch[1]) {
|
if (langMatch && langMatch[1]) {
|
||||||
const galleryLang = langMatch[1].toLowerCase();
|
const galleryLang = langMatch[1].toLowerCase();
|
||||||
if (galleryLang === selectedLanguage) {
|
gallery.style.display = (galleryLang === selectedLanguage) ? '' : 'none';
|
||||||
gallery.style.display = '';
|
|
||||||
} else {
|
|
||||||
gallery.style.display = 'none';
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
gallery.style.display = 'none';
|
gallery.style.display = 'none';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register menu command for quick access
|
// Update visible gallery count (optional visual feedback)
|
||||||
GM_registerMenuCommand('Change Language Filter', function() {
|
function updateVisibleCount() {
|
||||||
const newLang = prompt('Enter language code:\n' +
|
const galleries = getAllGalleries();
|
||||||
Object.entries(languages).map(([code, name]) => `${code} - ${name}`).join('\n'));
|
const visible = Array.from(galleries).filter(g => g.style.display !== 'none').length;
|
||||||
if (newLang && languages[newLang]) {
|
const total = galleries.length;
|
||||||
selectedLanguage = newLang;
|
|
||||||
GM_setValue('selectedLanguage', selectedLanguage);
|
// Update or create count display
|
||||||
location.reload();
|
let countDisplay = document.getElementById('language-filter-count');
|
||||||
|
if (!countDisplay) {
|
||||||
|
countDisplay = document.createElement('span');
|
||||||
|
countDisplay.id = 'language-filter-count';
|
||||||
|
countDisplay.style.cssText = 'margin-left: 10px; color: #666; font-size: 0.9em;';
|
||||||
|
const filterContainer = document.getElementById('language-filter-container');
|
||||||
|
if (filterContainer) {
|
||||||
|
filterContainer.appendChild(countDisplay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedLanguage === 'all') {
|
||||||
|
countDisplay.textContent = '';
|
||||||
|
} else {
|
||||||
|
countDisplay.textContent = `(${visible}/${total})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register menu command for quick access
|
||||||
|
GM_registerMenuCommand('Reset Language Filter', function() {
|
||||||
|
selectedLanguage = 'all';
|
||||||
|
GM_setValue('selectedLanguage', selectedLanguage);
|
||||||
|
const select = document.getElementById('language-filter-dropdown');
|
||||||
|
if (select) {
|
||||||
|
select.value = 'all';
|
||||||
|
}
|
||||||
|
filterGalleries();
|
||||||
|
updateVisibleCount();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
function init() {
|
function init() {
|
||||||
createLanguageSelector();
|
// Wait a bit for the page to fully load
|
||||||
filterGalleries();
|
setTimeout(() => {
|
||||||
|
createLanguageSelector();
|
||||||
// Watch for dynamic content loading
|
|
||||||
const observer = new MutationObserver(function(mutations) {
|
|
||||||
filterGalleries();
|
filterGalleries();
|
||||||
});
|
updateVisibleCount();
|
||||||
|
|
||||||
observer.observe(document.body, {
|
// Watch for dynamic content loading (e.g., infinite scroll)
|
||||||
childList: true,
|
const observer = new MutationObserver(function(mutations) {
|
||||||
subtree: true
|
// Only filter if gallery content changes
|
||||||
});
|
for (const mutation of mutations) {
|
||||||
|
if (mutation.addedNodes.length > 0) {
|
||||||
|
const hasGalleryNodes = Array.from(mutation.addedNodes).some(node => {
|
||||||
|
if (node.nodeType === 1) { // Element node
|
||||||
|
return node.classList.contains('dj') ||
|
||||||
|
node.classList.contains('manga') ||
|
||||||
|
node.classList.contains('acg') ||
|
||||||
|
node.classList.contains('imageset') ||
|
||||||
|
node.classList.contains('gallery');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasGalleryNodes) {
|
||||||
|
filterGalleries();
|
||||||
|
updateVisibleCount();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const galleryContent = document.querySelector('.gallery-content');
|
||||||
|
if (galleryContent) {
|
||||||
|
observer.observe(galleryContent, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for page to load
|
// Wait for page to load
|
||||||
|
|||||||
Reference in New Issue
Block a user