Hitomi.la Language Filter
This commit is contained in:
160
hitomi-language-filter.user.js
Normal file
160
hitomi-language-filter.user.js
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
// ==UserScript==
|
||||||
|
// @name Hitomi.la Language Filter
|
||||||
|
// @namespace https://git.ponywave.de/Akamaru/Userscripts
|
||||||
|
// @version 1.0
|
||||||
|
// @description Filter manga by language on hitomi.la
|
||||||
|
// @author Akamaru
|
||||||
|
// @match https://hitomi.la/*
|
||||||
|
// @grant GM_getValue
|
||||||
|
// @grant GM_setValue
|
||||||
|
// @grant GM_registerMenuCommand
|
||||||
|
// @updateURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/hitomi-language-filter.user.js
|
||||||
|
// @downloadURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/hitomi-language-filter.user.js
|
||||||
|
// @run-at document-idle
|
||||||
|
// ==/UserScript==
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Available languages
|
||||||
|
const languages = {
|
||||||
|
'all': 'All Languages',
|
||||||
|
'english': 'English',
|
||||||
|
'japanese': 'Japanese',
|
||||||
|
'chinese': 'Chinese',
|
||||||
|
'korean': 'Korean',
|
||||||
|
'spanish': 'Spanish',
|
||||||
|
'french': 'French',
|
||||||
|
'german': 'German',
|
||||||
|
'russian': 'Russian',
|
||||||
|
'italian': 'Italian',
|
||||||
|
'portuguese': 'Portuguese',
|
||||||
|
'thai': 'Thai',
|
||||||
|
'vietnamese': 'Vietnamese',
|
||||||
|
'polish': 'Polish',
|
||||||
|
'indonesian': 'Indonesian',
|
||||||
|
'turkish': 'Turkish'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get saved language preference (default: english)
|
||||||
|
let selectedLanguage = GM_getValue('selectedLanguage', 'english');
|
||||||
|
|
||||||
|
// Create language selector UI
|
||||||
|
function createLanguageSelector() {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.id = 'language-filter-container';
|
||||||
|
container.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 10000;
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #333;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.textContent = 'Language: ';
|
||||||
|
label.style.fontWeight = 'bold';
|
||||||
|
|
||||||
|
const select = document.createElement('select');
|
||||||
|
select.style.cssText = `
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-left: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Add options
|
||||||
|
for (const [code, name] of Object.entries(languages)) {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = code;
|
||||||
|
option.textContent = name;
|
||||||
|
if (code === selectedLanguage) {
|
||||||
|
option.selected = true;
|
||||||
|
}
|
||||||
|
select.appendChild(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle language change
|
||||||
|
select.addEventListener('change', function() {
|
||||||
|
selectedLanguage = this.value;
|
||||||
|
GM_setValue('selectedLanguage', selectedLanguage);
|
||||||
|
filterGalleries();
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(label);
|
||||||
|
container.appendChild(select);
|
||||||
|
document.body.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter galleries based on selected language
|
||||||
|
function filterGalleries() {
|
||||||
|
// Find all gallery items (both classes used on the site)
|
||||||
|
const galleries = document.querySelectorAll('.gallery, .dj, .manga');
|
||||||
|
|
||||||
|
galleries.forEach(gallery => {
|
||||||
|
if (selectedLanguage === 'all') {
|
||||||
|
gallery.style.display = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for language link in format /index-LANGUAGE.html
|
||||||
|
const languageTag = gallery.querySelector('a[href*="/index-"][href$=".html"]');
|
||||||
|
if (!languageTag) {
|
||||||
|
gallery.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const href = languageTag.getAttribute('href');
|
||||||
|
const langMatch = href.match(/\/index-([^.]+)\.html/);
|
||||||
|
|
||||||
|
if (langMatch && langMatch[1]) {
|
||||||
|
const galleryLang = langMatch[1].toLowerCase();
|
||||||
|
if (galleryLang === selectedLanguage) {
|
||||||
|
gallery.style.display = '';
|
||||||
|
} else {
|
||||||
|
gallery.style.display = 'none';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gallery.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register menu command for quick access
|
||||||
|
GM_registerMenuCommand('Change Language Filter', function() {
|
||||||
|
const newLang = prompt('Enter language code:\n' +
|
||||||
|
Object.entries(languages).map(([code, name]) => `${code} - ${name}`).join('\n'));
|
||||||
|
if (newLang && languages[newLang]) {
|
||||||
|
selectedLanguage = newLang;
|
||||||
|
GM_setValue('selectedLanguage', selectedLanguage);
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
function init() {
|
||||||
|
createLanguageSelector();
|
||||||
|
filterGalleries();
|
||||||
|
|
||||||
|
// Watch for dynamic content loading
|
||||||
|
const observer = new MutationObserver(function(mutations) {
|
||||||
|
filterGalleries();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for page to load
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user