NotificationSoundSettings

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل 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('Ошибка чтения сохранённого звука');
        }
    }

})();