Hitomi Language Settings

Pin Hitomi pages to all languages or a specific language, and remembers your choice.

// ==UserScript==
// @name:ko           Hitomi 언어 설정
// @name              Hitomi Language Settings
// @name:ja           Hitomi言語設定
// @name:zh-TW        Hitomi語言設置
// @name:zh-CN        Hitomi语言设置

// @description:ko    모든 언어 또는 특정 언어로 Hitomi 페이지를 고정하고 설정을 기억합니다.
// @description       Pin Hitomi pages to all languages or a specific language, and remembers your choice.
// @description:ja    Hitomiのページをすべての言語または特定の言語に固定し、選択を記憶します。
// @description:zh-TW 將Hitomi頁面固定為所有語言或特定語言,並記住您的選擇。
// @description:zh-CN 将Hitomi页面固定为所有语言或特定语言,并记住您的选择。

// @namespace         https://ndaesik.tistory.com/
// @version           2.1
// @author            ndaesik & SFGFDSD(修改)
// @icon              https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://hitomi.la
// @match             https://hitomi.la/*

// @grant             GM.getValue
// @grant             GM.setValue
// @grant             GM_registerMenuCommand
// @grant             GM_addStyle
// ==/UserScript==

(async () => {
    'use strict';

    const STORAGE_KEY = 'hitomi_language_preference';

    const LANGUAGES = {
        'all': 'All Languages',
        'english': 'English',
        'japanese': '日本語',
        'chinese': '中文',
        'korean': '한국어',
        'russian': 'Русский',
        'spanish': 'Español',
        'portuguese': 'Português',
        'french': 'Français',
        'german': 'Deutsch',
        'italian': 'Italiano',
    };

    async function showSettingsMenu() {
        if (document.getElementById('hl-settings-modal')) return;
        const currentPref = await GM.getValue(STORAGE_KEY, { mode: 'all', lang: 'all' });
        const modal = document.createElement('div');
        modal.id = 'hl-settings-modal';
        let radioButtonsHTML = '';
        for (const [key, name] of Object.entries(LANGUAGES)) {
            const isChecked = currentPref.lang === key;
            radioButtonsHTML += `<label><input type="radio" name="language_choice" value="${key}" ${isChecked ? 'checked' : ''}> ${name}</label>`;
        }
        modal.innerHTML = `
            <div class="hl-modal-content">
                <h2>选择语言模式</h2>
                <p>选择“All Languages”恢复默认行为。</p>
                <div class="hl-lang-list">${radioButtonsHTML}</div>
                <div class="hl-button-group">
                    <button id="hl-save-btn">保存并刷新</button>
                    <button id="hl-cancel-btn">取消</button>
                </div>
            </div>
        `;
        document.body.appendChild(modal);

        document.getElementById('hl-save-btn').addEventListener('click', async () => {
            const selectedLang = document.querySelector('input[name="language_choice"]:checked').value;
            await GM.setValue(STORAGE_KEY, { mode: selectedLang === 'all' ? 'all' : 'language', lang: selectedLang });

            const searchInput = document.getElementById('query-input');
            if (searchInput) {
                if (/language:\w+/.test(searchInput.value)) {
                    if (selectedLang === 'all') {
                         searchInput.value = searchInput.value.replace(/language:\w+\s*/, '').trim();
                    } else {
                        searchInput.value = searchInput.value.replace(/language:\w+/, `language:${selectedLang}`);
                    }
                }
            }
            window.location.reload();
        });

        const closeModal = () => document.body.removeChild(modal);
        document.getElementById('hl-cancel-btn').addEventListener('click', closeModal);
        modal.addEventListener('click', (e) => { if (e.target.id === 'hl-settings-modal') closeModal(); });
    }

    async function applyLanguagePreference() {
        const pref = await GM.getValue(STORAGE_KEY, { mode: 'all', lang: 'all' });

        if (pref.mode === 'all') {
            return;
        }

        const targetLang = pref.lang;
        const currentUrl = window.location.toString();

        if (currentUrl.endsWith('hitomi.la/') || currentUrl.includes('-all.html')) {
            let newUrl = currentUrl;
            if (currentUrl.endsWith('hitomi.la/')) {
                newUrl = 'https://hitomi.la/index-all.html';
            }
            newUrl = newUrl.replace(/-all\./, `-${targetLang}\.`);

            if (newUrl !== currentUrl) {
                window.location.replace(newUrl);
                return;
            }
        }

        const logoLink = document.querySelector('#logo > a');
        if (logoLink) {
            logoLink.setAttribute('href', `https://hitomi.la/index-${targetLang}.html`);
        }

        if (currentUrl.includes(`-${targetLang}.`)) {
             if (history.length > 1 && document.referrer.includes('-all.html')) {
                 history.replaceState(null, '', currentUrl);
             }
        }
    }

    GM_addStyle(`
        #hl-settings-fab {
            position: fixed; bottom: 20px; left: 20px; z-index: 9999;
            padding: 12px; background-color: #3a87ad; color: white;
            border: none; border-radius: 50%; width: 56px; height: 56px;
            box-shadow: 0 4px 10px rgba(0,0,0,0.25); cursor: pointer;
            display: flex; align-items: center; justify-content: center;
            transition: transform 0.2s ease-in-out;
        }
        #hl-settings-fab:hover { transform: scale(1.1); }
        #hl-settings-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); z-index: 10000; display: flex; align-items: center; justify-content: center; }
        .hl-modal-content { background-color: #fff; color: #333; padding: 20px 25px; border-radius: 8px; width: 90vw; max-width: 400px; box-shadow: 0 5px 20px rgba(0,0,0,0.3); font-family: sans-serif; }
        .hl-modal-content h2 { margin-top: 0; text-align: center; }
        .hl-modal-content p { font-size: 14px; color: #666; text-align: center; margin-bottom: 20px; }
        .hl-lang-list { max-height: 50vh; overflow-y: auto; display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; border-top: 1px solid #eee; border-bottom: 1px solid #eee; padding: 15px 5px; }
        .hl-lang-list label { display: flex; align-items: center; padding: 8px; border-radius: 5px; cursor: pointer; transition: background-color 0.2s; }
        .hl-lang-list label:hover { background-color: #f0f0f0; }
        .hl-lang-list input[type="radio"] { margin-right: 10px; }
        .hl-button-group { margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px; }
        .hl-button-group button { padding: 10px 20px; border-radius: 5px; border: none; font-weight: bold; cursor: pointer; }
        #hl-save-btn { background-color: #3a87ad; color: white; }
        #hl-cancel-btn { background-color: #ccc; color: #333; }
    `);

    const fab = document.createElement('button');
    fab.title = '语言设置';
    fab.id = 'hl-settings-fab';
    fab.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.89 1.45l8 4A2 2 0 0 1 22 7.24v9.53a2 2 0 0 1-1.11 1.79l-8 4a2 2 0 0 1-1.79 0l-8-4A2 2 0 0 1 2 16.77V7.24a2 2 0 0 1 1.11-1.79l8-4a2 2 0 0 1 1.78 0z"></path><polyline points="2.32 6.16 12 11 21.68 6.16"></polyline><line x1="12" y1="22.76" x2="12" y2="11"></line></svg>`;
    fab.addEventListener('click', showSettingsMenu);
    document.body.appendChild(fab);

    GM_registerMenuCommand('设置语言 (Set Language)', showSettingsMenu);

    await applyLanguagePreference();

})();