NotificationSoundSettings

Интерфейс изменения звука уведомлений.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         NotificationSoundSettings
// @namespace    MeloniuM/LZT
// @version      1.4.1
// @description  Интерфейс изменения звука уведомлений.
// @author
// @match        *://lolz.live/*
// @match        *://zelenka.guru/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    $("<style/>").text(`
    input#sound-file::file-selector-button {
        border-radius: 6px;
        border-style: none;
    }
    .NotificationSoundSettings {
        position: absolute;
        cursor: pointer;
        right: 85px;
        top: 14px;
        font-size: 18px;
    }
    .NotificationSound--icon{
        color: #949494;
    }
    .NotificationSound--icon:hover{
        color: rgb(58, 169, 119);
    }
    #NSSuploaded-filename {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        background: #2b2b2b;
        border: 1px solid #444;
        border-radius: 4px;
        padding: 6px 12px;
        margin: 6px 0;
        font-size: 13px;
        color: #ccc;
        cursor: pointer;
        user-select: none;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    `).appendTo("head");

    const STORAGE_KEY = 'customNotificationSound';
    const MAX_FILE_SIZE = 500 * 1024;
    let audio = null;

    const OriginalAudio = window.Audio;
    let NotifAudio;
    let fix_notif_sound = true;

	window.Audio = function (src) {
		// Проверяем, нужно ли заменить источник
		if (src === Im.soundNotificationFile && Im.customSoundNotificationFile) {
			console.log('[Audio] Заменён src:', src, '→', Im.customSoundNotificationFile);
			src = Im.customSoundNotificationFile;
		}

		return new OriginalAudio(src);
	};

	window.Audio.prototype = OriginalAudio.prototype;

    $(window).on("load", function() {
        //фикс звука
        Im.soundNotificationFile = 'https://lolz.live/public/pm2.wav';
        Im.customSoundNotificationFile = Im.soundNotificationFile
        applyStoredSound();
        NotifAudio = new Audio(Im.soundNotificationFile);
        let Notification = $('html').data('Im.Notification');
        Notification.displayNotificationOld = Notification.displayNotification;
        Notification.displayNotification = (e) => {
            Notification.displayNotificationOld(e);
            if (!e.isSiteFocused && e.shouldPlayAudio && Im.soundNotificationsEnabled && fix_notif_sound) {
                try {
                    NotifAudio?.play();
                } catch (e) {};
            }
        };
    })

    $(document).bind('PopupMenuShow', function(e){
        let $menu = e.$menu
        if (!$menu.is('#AlertsMenu')) {
            return;
        }
        if ($menu.find('.NotificationSoundSettings').length) return;
        let $settings = createSoundSettingsUI();

        $menu.prepend($settings).xfActivate();
        prefillInputs();

        $('#NSSsound-mode').on('change', function () {
            updateModeUI(this.value);
        });

        $('#NSSsound-url').on('input', toggleApplyButton);
        $('#NSSsound-file').on('change', toggleApplyButton);
        $('#NSSsound-mode').on('change', toggleApplyButton);

        $('#fix_notif_sound').on('click', (e) => {
            e.stopPropagation();

            let val = localStorage.getItem(STORAGE_KEY);

            try {
                let parsed = {fix: fix_notif_sound};
                if (val) {
                    parsed = JSON.parse(val);
                }

                fix_notif_sound = $('#fix_notif_sound').prop('checked');
                parsed.fix = fix_notif_sound;
                localStorage.setItem(STORAGE_KEY, JSON.stringify(parsed));
            } catch (e) {
                console.warn('Ошибка применения сохранённого звука');
            }
        });


        $('#NSSapply-sound').on('click', (e) => {
            e.stopPropagation();
            e.preventDefault();
            const urlInput = $('#NSSsound-url').val().trim();
            const fileInput = $('#NSSsound-file').get(0).files[0];

            if (urlInput) {
                if (!isValidURL(urlInput)) {
                    alert('Введите корректную ссылку на звук');
                    return;
                }
                const data = {
                    type: 'url',
                    value: urlInput,
                    fix: $('#fix_notif_sound').val()
                };
                localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
                Im.customSoundNotificationFile = urlInput;
                alert('Ссылка на звук сохранена');
            } else if (fileInput) {
                if (fileInput.size > MAX_FILE_SIZE) {
                    alert(`Файл слишком большой. Лимит: ${MAX_FILE_SIZE / 1024} КБ`);
                    return;
                }
                const reader = new FileReader();
                reader.onload = e => {
                    const data = {
                        type: 'file',
                        name: fileInput.name,
                        value: e.target.result,
                        fix: $('#fix_notif_sound').val()
                    };
                    localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
                    Im.customSoundNotificationFile = data.value;
                    alert('Файл сохранён и применён');
                    NotifAudio = new Audio(Im.soundNotificationFile);
                };
                reader.readAsDataURL(fileInput);
            } else {
                alert('Введите ссылку или выберите файл');
            }
        });

        $('#NSSuploaded-filename').on('click', () => {
            e.stopPropagation();
            e.preventDefault();
            $('#NSSsound-file').click();
        });

        $('#NSSsound-file, #NSSsound-mode').on('click', (e) => {
            e.stopPropagation();
        });

        $('#NSSsound-file').on('change', function () {
            const file = this.files[0];
            updateFilenameDisplay(file?.name);
            $('#NSStest-sound').slideDown(200);
        });

        $('#NSSreset-sound').on('click', (e) => {
            e.stopPropagation();
            e.preventDefault();
            localStorage.removeItem(STORAGE_KEY);
            Im.customSoundNotificationFile = Im.soundNotificationFile;
            NotifAudio = new Audio(Im.soundNotificationFile);
            updateFilenameDisplay();
            $('#NSSsound-url').val('');
            $('#NSSsound-file').val('');
            $('#NSSapply-sound').slideUp(200);
        });

        $('#NSStest-sound').on('click', (e) => {
            e.stopPropagation();
            e.preventDefault();
            const urlInput = $('#NSSsound-url').val().trim();
            const fileInput = $('#NSSsound-file').get(0).files[0];

            if (urlInput) {
                playSound(urlInput);
            } else if (fileInput) {
                if (fileInput.size > MAX_FILE_SIZE) {
                    alert(`Файл слишком большой. Лимит: ${MAX_FILE_SIZE / 1024} КБ`);
                    return;
                }
                const reader = new FileReader();
                reader.onload = e => playSound(e.target.result);
                reader.readAsDataURL(fileInput);
            } else {
                playSound(Im.customSoundNotificationFile);
            }
        });

    });

    function isValidURL(url) {
        try {
            new URL(url);
            return true;
        } catch (_) {
            return false;
        }
    }


    function createSoundSettingsUI() {
        return $(`
            <div class="NotificationSoundSettings navTab Popup PopupInPopup PopupClosed">
                <a rel="Menu" class="navLink NoPopupGadget">
                    <i class="fa fa-file-audio NotificationSound--icon Popup"></i>
                </a>
                <div class="Menu HeaderMenu Popup" style="left: 276.453px;top: 53px !important;position: fixed;padding: 10px;">
                    <div style="margin-bottom: 10px; font-size: 15px; font-weight: bold;">
                        Настройка звука уведомлений
                    </div>
                    <div style="margin-bottom: 8px;">
                        <label for="NSSsound-mode"><strong>Источник звука:</strong></label>
                        <select id="NSSsound-mode" style="margin-left: 8px; background: #2b2b2b; color: #fff; border: 1px solid #444; border-radius: 4px; padding: 4px;">
                            <option value="url">Ссылка</option>
                            <option value="file">Файл</option>
                        </select>
                        <label for="fix_notif_sound"><input type="checkbox" checked name="fix_notif_sound" id="fix_notif_sound" value="1">фикс звука</label>
                    </div>

                   <label style="display:block; margin-bottom: 6px;">
                        <span>Ссылка:</span>
                        <input type="text" id="NSSsound-url" placeholder="https://..." style="width:100%; box-sizing: border-box; padding:5px; background:#2b2b2b; color:#fff; border:1px solid #444; border-radius:4px;">
                    </label>
                    <label for="sound-file" style="display: none; margin-top: 6px;">
                        <input type="file" id="NSSsound-file" accept="audio/*" style="display: none;">
                        <div id="NSSuploaded-filename" class="button">
                            <span style="color: #ccc;">Выбран файл:</span>
                            <div style="color: #888;">Нажмите для выбора файла</div>
                        </div>
                    </label>
                    <div style="display: flex; flex-direction: column; gap: 6px;">
                        <button id="NSSapply-sound" style="display: none;background:#4caf50; color:white; padding:6px; border:none; border-radius:4px; cursor:pointer;">Применить</button>
                        <button id="NSStest-sound" style="background:#2196f3; color:white; padding:6px; border:none; border-radius:4px; cursor:pointer;">Тест</button>
                        <button id="NSSreset-sound" style="background:#f44336; color:white; padding:6px; border:none; border-radius:4px; cursor:pointer;">Сбросить</button>
                    </div>
                </div>
            </div>
        `);
    }


    function applyStoredSound() {
        const val = localStorage.getItem(STORAGE_KEY);
        if (!val) return;

        try {
            const parsed = JSON.parse(val);
            if (parsed && parsed.value) {
                Im.customSoundNotificationFile = parsed.value;
            }
            if (parsed?.fix) {
                fix_notif_sound = !!parsed?.fix;
            }
        } catch (e) {
            console.warn('Ошибка применения сохранённого звука');
        }
    }

    function toggleApplyButton() {
        const mode = $('#NSSsound-mode').val();
        const url = $('#NSSsound-url').val().trim();
        const file = $('#NSSsound-file').get(0).files[0];

        const shouldShow = (mode === 'url' && url) || (mode === 'file' && file);
        if (shouldShow) {
            $('#NSSapply-sound').slideDown(200);
        } else {
            $('#NSSapply-sound').slideUp(200);
        }
    }

    function playSound(src) {
        try {
            audio?.pause();
            audio = new Audio(src);
            audio.play().catch(err => {
                console.error('Ошибка воспроизведения:', err);
                alert('Не удалось воспроизвести звук.');
            });
        } catch {
            alert('Ошибка воспроизведения звука');
        }
    }

    function updateFilenameDisplay(name) {
        const MAX_LENGTH = 24;
        let displayName = name || 'Нажмите для выбора файла';

        if (name && name.length > MAX_LENGTH) {
            const start = name.slice(0, 6);
            const end = name.slice(-7);
            displayName = `${start}...${end}`;
        }

        $('#NSSuploaded-filename').html(`
            <span style="color: #ccc;">Выбран файл:</span>
            <div style="color: #ccc;">${displayName}</div>
        `);
    }


    function updateModeUI(mode) {
        if (mode === 'url') {
            $('#NSSsound-url').closest('label').show();
            $('#NSSuploaded-filename').closest('label').hide();
        } else if (mode === 'file') {
            $('#NSSsound-url').closest('label').hide();
            $('#NSSuploaded-filename').closest('label').show();
        }
    }

    function prefillInputs() {
        const val = localStorage.getItem(STORAGE_KEY);
        if (!val) return;

        try {
            const parsed = JSON.parse(val);

            if (parsed.type === 'url') {
                $('#NSSsound-url').val(parsed.value);
            } else if (parsed.type === 'file') {
                updateFilenameDisplay(parsed.name || '[без имени]');
            }
            $('#NSSsound-mode').val(parsed.type);
            if (parsed?.fix) {
                fix_notif_sound = !!parsed?.fix;
            }
            $('#fix_notif_sound').prop('checked', fix_notif_sound);
            updateModeUI(parsed.type);
            if (parsed.value) {
                Im.customSoundNotificationFile = parsed.value;
                $('#NSSapply-sound').show();
            }
        } catch (e) {
            console.warn('Ошибка чтения сохранённого звука');
        }
    }

})();