Greasy Fork is available in English.

old.myshows.me

С 1 мая 2024 года обещали отключить old.myshows.me. Под ручку с ChatGPT попытался починить нужные мне места.

// ==UserScript==
// @name         old.myshows.me
// @namespace    http://tampermonkey.net/
// @version      2024-v32
// @description  С 1 мая 2024 года обещали отключить old.myshows.me. Под ручку с ChatGPT попытался починить нужные мне места.
// @             Желательно использовать вместе с внешним видом от другого энтузиаста: https://userstyles.world/style/15722/old-myshows-me (инструкцию ищите там же)
// @author       SanBest93
// @match        https://myshows.me/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=myshows.me
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // Добавлены настройки на страницу, собсно, «Настройки» (https://myshows.me/profile/edit/)
    // Сохраняются в локальное хранение. Удаляются при очистке кэша.
    // Вписывайте ниже жёстко, если это критично
    const CONFIG = {
        OriginalTitleIsNeeded: getItem('OriginalTitleIsNeeded') // Всегда выводить оригинальные названия на странице 'https://myshows.me/profile/next/'
        , ModifyS01E01: getItem('ModifyS01E01') // Замена "1 x 1" на "s01e01" (и ещё по мелочи) на странице 'https://myshows.me/profile/next/'
        , s01e01Postfix: getItem('s01e01PostfixValue') // Текст после s01e01 на странице 'https://myshows.me/profile/next/'. Мне так удобнее на торрентах искать
        , ModifyProfileNumbers: getItem('ModifyProfileNumbers') // Вывод полных чисел в шапке профиля
        , ModifyShowSeasonMeta: getItem('ModifyShowSeasonMeta') // Замена "5 эпизодов с e1" на "5 эпизодов с e01" на странице 'https://myshows.me/profile/'
        , ModifyShowTitleLink: getItem('ModifyShowTitleLink') // Ссылка только в русском названии шоу на странице 'https://myshows.me/profile/'
    };

    // Массив флагов выполнения функций
    let MODS = {
        profileNumbersModified: false
        , showSeasonMetaModified: false
        , showTitleLinkModified: false
        , S01E01Modified: false
        , watchSoonElementsModified: false
    }

    let myMap = new Map(); // Сюда будем складывать соответствие showId — titleOriginal
    let changed = 0; // Для проверки количества внесённых изменений
    let changeIsNeeded = true; // Так мне просто понятнее
    let STYLE;
    let lastUrl = location.href;

    // Запоминаем userName
    const userName = document.querySelector('div.HeaderLogin__username')?.textContent;

    // Создаём объект для хранения данных о шоу
    class ShowData {
        constructor(index, showTitle, episodeInfo, innerHTML) {
            this.index = index;
            this.showTitle = showTitle;
            this.episodeInfo = episodeInfo;
            this.innerHTML = innerHTML;
        }
    }

    // Получение настроек со страницы настроек по id
    function getItem (id) {
        const value = localStorage.getItem(id);
        return value === 'true' ? true : value === 'false' ? false : value;
    }

    // Сброс флага по имена
    function resetFlag(flagName) {
        if (flagName in MODS) {
            MODS[flagName] = false;
        }
    }

    // Проверка смены страницы
    function checkUrlChange() {
        const currentUrl = location.href;
        if (currentUrl !== lastUrl) {
            lastUrl = currentUrl;
            Object.keys(MODS).forEach(flagName => resetFlag(flagName));
        }
    }

    // Функция для создания флажка
    function createCheckbox(parent, id, labelText, addTextContent) {
        // Создаём элемент метки (label)
        const label = document.createElement('label');
        label.className = 'oldMyshowsSettings-label'; // Устанавливаем класс метки

        // Создаём элемент флажка (input)
        const checkbox = document.createElement('input');
        checkbox.className = 'oldMyshowsSettings-checkbox';
        checkbox.type = 'checkbox';
        checkbox.id = id;
        checkbox.checked = getItem(id); // Устанавливаем состояние флажка на основе значения из localStorage
        checkbox.addEventListener('change', function() { // Добавляем обработчик события изменения состояния флажка
            localStorage.setItem(id, this.checked); // Сохраняем состояние флажка в localStorage
            /*const element = document.getElementById(id);
            element.checked = this.checked;*/
        });
        label.appendChild(checkbox); // Добавляем флажок в метку

        // Создаём элемент span для текстового содержимого метки
        const span = document.createElement('span');
        span.innerText = labelText;
        label.appendChild(span); // Добавляем span в метку

        // Если требуется добавить дополнительное текстовое содержимое
        if (addTextContent === true) {
            const idValue = `${id}Value`; // Генерируем id для дополнительного текстового поля
            const textContent = document.createElement('input');
            textContent.className = 'oldMyshowsSettings-checkbox-textContent';
            textContent.type = 'text';
            textContent.id = idValue;
            textContent.value = getItem(idValue); // Устанавливаем значение текстового поля на основе значения из localStorage
            textContent.addEventListener('change', function() { // Добавляем обработчик события изменения значения текстового поля
                localStorage.setItem(idValue, this.value); // Сохраняем значение текстового поля в localStorage
            });
            label.appendChild(textContent); // Добавляем текстовое поле в метку
        }

        parent.appendChild(label); // Добавляем метку в указанный родительский элемент

        return label; // Возвращаем созданную метку
    }

    // Создание флажков на странице настроек
    function createCheckboxes() {
        const groupTitleTextContent = 'Настройки скрипта old.myshows.me';

        // Проверяем, существует ли уже наша группа.
        // Если уже существует, прекращаем выполнение функции
        if (document.querySelector('.oldMyshowsSettings')) return;

        const parentElement = document.querySelector('.mb-5'); // Элемент, в который мы хотим добавить новую группу
        const thelastChild = parentElement?.lastChild; // Его последний дочерний элемент
        if (!parentElement || !thelastChild) return;

        // Создаём группу
        let sectionAccordion = document.createElement('div');
        sectionAccordion.classList.add('SectionAccordion');

        // Создаём заголовок для группы
        let sectionAccordionTitle = document.createElement('div');
        sectionAccordionTitle.textContent = groupTitleTextContent;
        sectionAccordionTitle.classList.add('SectionAccordion-title');

        // Создаём контейнер для флажков
        let checkboxesContainer = document.createElement('div');
        checkboxesContainer.classList.add('oldMyshowsSettings');

        // Создаём контейнер для стиля
        let checkboxesStyle = document.createElement('div');
        checkboxesStyle.classList.add('oldMyshowsSettings-style');

        // Создаём флажки
        createCheckbox(checkboxesStyle,
                       'ModifyShowTitleLink',
                       '«Мои сериалы»: оставить ссылку только в русском названии');
        createCheckbox(checkboxesStyle,
                       'ModifyShowSeasonMeta',
                       '«Мои сериалы»: замена "N эпизодов с e1" на "N эпизодов с e01"');
        createCheckbox(checkboxesStyle,
                       'ModifyProfileNumbers',
                       '«Профиль»: вывод полных чисел в шапке');
        createCheckbox(checkboxesStyle,
                       'OriginalTitleIsNeeded',
                       '«Календарь»: всегда выводить оригинальные названия');
        createCheckbox(checkboxesStyle,
                       'ModifyS01E01',
                       '«Календарь»: замена "1 x 1" на "s01e01" (и ещё по мелочи)');
        createCheckbox(checkboxesStyle,
                       's01e01Postfix',
                       '«Календарь»: текст после s01e01',
                       true);

        // Добавляем флажки в контейнер
        checkboxesContainer.appendChild(checkboxesStyle);
        checkboxesContainer.classList.toggle('hidden');

        // Добавляем заголовок и контейнер с флажками в сворачиваемую группу
        sectionAccordion.appendChild(sectionAccordionTitle);
        sectionAccordion.appendChild(checkboxesContainer);

        // Добавляем обработчик события клика на заголовок для переключения видимости контейнера с флажками
        sectionAccordionTitle.addEventListener('click', function() {
            checkboxesContainer.classList.toggle('hidden');
        });

        // Вставляем новую группу перед последним дочерним элементом
        parentElement.insertBefore(sectionAccordion, thelastChild);
    }

    // Проверка, что ссылки одинаковые
    function linksAreSimilar(link1, link2) {
        // Сравниваем ссылки
        return link1.replace(/\/$/, '') === link2.replace(/\/$/, '');
    }

    // Общая функция для поиска ключа в массиве данных
    function findKeyInArray(data, key, N = data.length) {
        for (let i = 0; i < Math.min(N, data.length); i += 1) {
            // Проверяем, является ли элемент объектом и содержит ли указанный ключ
            if (typeof data[i] === 'object' && !!data[i] && key in data[i]) {
                // Если да, выводим значение ключа
                return data[i][key];
            }
        }
        return undefined;
    }

    // Функция для поиска значений в __NUXT_DATA__ по пути
    function findValueByPath(data, path) {
        // Разделяем путь на компоненты
        let keys = path.split('.');
        let index = 0;
        let currentData = data[index];

        // Проходимся по каждому компоненту пути
        for (let key of keys) {
            if (Array.isArray(currentData) && currentData.length > 0) {
                // Если текущие данные являются массивом, то
                // на 2024.06.18 структура такая, что вид ['Reactive', число]
                let indexReactive = currentData[0].indexOf('Reactive') + 1; // Это число
                if (!isNaN(indexReactive) && indexReactive > 0 && indexReactive < currentData.length) {
                    index = currentData[indexReactive];
                    currentData = data[index];
                } else {
                    return undefined; // Если индекс некорректный или за пределами массива
                }
            }
            if (typeof currentData === 'object' && currentData !== null) {
                // Если текущие данные являются объектом
                if (key in currentData) {
                    index = currentData[key];
                    currentData = data[index];
                } else {
                    return undefined; // Если ключ не найден в текущем объекте
                }
            } else {
                return undefined; // Если текущие данные не являются ни объектом, ни массивом
            }
        }
        return currentData;
    }

    // Замена названия на оригинальное
    function fixTitle(show) {
        if (myMap.size > 0) {
            const showId = show.pathname.split("/").slice(-2)[0];
            return myMap.get(showId) || '';
        }
        return '';
    }

    // Добавление префикса S или E к номеру сезона или серии
    function addPrefix(text, prefix) {
        const num = parseInt(text, 10);
        return isNaN(num) ? text : `${prefix}${num < 10 ? '0' : ''}${num}`;
    }

    // Получаем из строки "1 x 1 - название эпизода" данные {se: 's01e01', name: 'название эпизода'}
    function fixEpisodeInfo(episodeText) {
        let se = '';
        let name = '';

        if (!episodeText) return { se, name };

        if (CONFIG.ModifyS01E01) {
            const parts = episodeText.split(' ');
            if (parts.length >= 3) {
                const season = parts[0];
                const episode = parts[2];
                const s = addPrefix(season, 's');
                const e = `${addPrefix(episode, 'e')}${CONFIG.s01e01Postfix ? ` ${CONFIG.s01e01Postfix}` : ''}`;
                se = `${s}${e}`;
                name = parts.slice(3).join(' ').replace(/^-\s*/, '').trim();
            }
        } else {
            const parts = episodeText.split(' - ');
            if (parts.length >= 2) {
                se = parts[0].trim();
                name = parts.slice(1).join(' - ').trim();
            } else {
                se = episodeText.trim();
            }
        }
        return { se, name };
    }

    // Исправляем текст, который стал в несколько строк с последним обновлением сайта.
    // И сортируем строки
    function fixWatchSoonElements() {
        if (MODS.watchSoonElementsModified) return;

        const watchSoonElements = document.querySelectorAll('.WatchSoon__title-wrapper');
        if (!watchSoonElements) return;

        const showsData = []; // Массив объектов для сортировки данных о шоу и эпизодах
        let index = -1; // Индекс для сортировки по дням
        let prevWatchSoonLeft = ''; // Для проверки, что текст сменился
        let changes = 0;

        // Заполняем массив объектами на основе данных на странице
        watchSoonElements.forEach(element => {
            const showLink = element.querySelector('.WatchSoon-show');
            const episodeLink = element.querySelector('.WatchSoon-episode');
            if (!showLink || !episodeLink || !episodeLink.textContent.includes(' - ')) return;

            // Находим родительский элемент с классом ".WatchSoon-left"
            let parent = element;
            while (parent) {
                if (parent.querySelector('.WatchSoon-left')) break;
                parent = parent.parentElement;
            }
            if (!parent) return; // Если не найден родительский элемент, выходим

            // Добавим признак группировки из правой колонки (да, я вижу, что в коде она называется left)
            const watchSoonLeft = parent.querySelector('.WatchSoon-left').textContent.trim();
            if (watchSoonLeft !== prevWatchSoonLeft) {
                prevWatchSoonLeft = watchSoonLeft;
                index += 1;
            }

            const showTitle = CONFIG.OriginalTitleIsNeeded === true ? fixTitle(showLink) : showLink.textContent;
            const episodeInfo = fixEpisodeInfo(episodeLink.textContent);
            const innerHTML = `<a href="${showLink.href}" target="_blank">${showTitle}</a><span> — ${episodeInfo.se} — </span><a href="${episodeLink.href}" target="_blank">${episodeInfo.name}</a>`;

            // Добавляем данные в массив объектов
            showsData.push(new ShowData(index, showTitle, episodeInfo.se, innerHTML));

            // Скрываем исходный элемент
            element.className = `${element.className} hidden`;
        });

        // Сортируем массив по index, затем по showText, затем по episodeInfo
        showsData.sort((a, b) => {
            if (a.index !== b.index) return a.index - b.index;
            if (a.showTitle !== b.showTitle) return a.showTitle.localeCompare(b.showTitle);
            return a.episodeInfo.localeCompare(b.episodeInfo);
        });

        // Вставляем элементы на основе отсортированных данных
        showsData.forEach((data, index) => {
            const newElement = document.createElement('div');
            newElement.innerHTML = data.innerHTML;
            newElement.classList.add('OldMyShowsClass');
            // (описание классов см. в initStyle())

            const parent = watchSoonElements[index];
            if (parent) {
                parent.parentNode.insertBefore(newElement, parent.nextSibling);
                changes += 1;
            }
        });

        // Меняем стили через CSS
        initStyle();

        MODS.watchSoonElementsModified = changes !== 0;
    }

    // Получение содержимого <script> и преобразование его в объект JavaScript
    function parseScriptData() {
        const scriptElement = document.getElementById('__NUXT_DATA__');
        return scriptElement ? JSON.parse(scriptElement.textContent) : null;
    }

    // Создание своей карты соответствия данных
    function createMap() {
        const dataObject = parseScriptData();
        if (!dataObject) return;

        // Адрес массива сериалов
        let iShowIDs = findKeyInArray(dataObject, 'list', 30) || findKeyInArray(dataObject, 'userShows', 30);
        // Проверяем заполненность. Если пусто, то
        if (!iShowIDs) {
            // Перезагружаем страницу.
            // Тогда точно попадутся правильные данные в '__NUXT_DATA__'
            location.reload();
            return;
        }

        // Если нашли адрес, получаем список сериалов
        const showIDs = dataObject?.[iShowIDs];
        if (Array.isArray(showIDs)) {
            // Перебираем полученный массив сериалов, заполняем карту.
            // На всякий случай с попыткой
            showIDs.forEach(element => {
                try {
                    const show = dataObject?.[element]?.show;
                    myMap.set(
                        dataObject?.[dataObject?.[show]?.id].toString().trim(),
                        dataObject?.[dataObject?.[show]?.titleOriginal].trim()
                    );
                } catch (error) {
                    console.error('[скрипт old.myshows.me] Ошибка в createMap():', error);
                }
            });
        }
    }

    // Смена чисел профиля с "1к" на "1 000"
    function modifyProfileNumbers() {
        if (MODS.profileNumbersModified) return;

        // Выбираем все div с классом UserHeader__stats-row на странице
        const statsRows = document.querySelectorAll('div.UserHeader__stats-row');
        if (!statsRows.length) return;

        const dataObject = parseScriptData();
        const path1 = 'data.User.profile.stats.watchedEpisodes';
        const path2 = 'data.User.profile.statsMovies.watchedMovies';
        const path3 = 'data.User.profile.statsTotal.watchedHours';

        // Ищем значения в __NUXT_DATA__. Могут быть в разных местах в зависимости от открытой страницы
        let value1 = findValueByPath(dataObject, path1);
        if (!value1) value1 = dataObject?.[findKeyInArray(dataObject, path1.split('.').pop())] ?? undefined;
        if (!value1) return;
        let value2 = findValueByPath(dataObject, path2);
        if (!value2) value2 = dataObject?.[findKeyInArray(dataObject, path2.split('.').pop())] ?? undefined;
        if (!value2) return;
        let value3 = findValueByPath(dataObject, path3);
        if (!value3) value3 = dataObject?.[dataObject?.[findKeyInArray(dataObject, 'statsTotal')]?.watchedHours] ?? undefined;
        if (!value3) return;
        let value4 = Math.ceil(value3 / 24);

        // Сохраняем значения по ключам
        const valueMap = {
            э: value1 // эпизодов
            , ф: value2 // фильмов
            , ч: value3 // часов
            , д: value4 // дней
        };
        let changes = 0;

        // Перебираем коллекцию элементов и меняем их содержимое
        statsRows.forEach(element => {
            const valueElement = element.querySelector('.UserHeader__stats-value');
            const titleElement = element.querySelector('.UserHeader__stats-title');
            if (!valueElement || !titleElement) return;

            // Получаем первую букву подписи
            const key = titleElement.textContent.trim().charAt(0);
            // Ищем такую в сохранённых
            const value = valueMap[key];
            // Если не пустая - присваиваем (без всяких привязок к классам и стилям, может потом)
            if (value !== undefined) {
                valueElement.textContent = Math.round(value).toLocaleString();
                changes += 1;
            }
        });
        MODS.profileNumbersModified = changes !== 0;
    }

    // Функция, чтобы оставить ссылку только в русском названии шоу на странице myshows.me/profile/
    function modifyShowTitleLink() {
        if (!CONFIG.ModifyShowTitleLink || MODS.showTitleLinkModified) return;

        let showTitles = document.querySelectorAll('a.Unwatched-showTitle');
        if (!showTitles) return;

        let changes = 0;
        showTitles.forEach(title => {
            // Получаем значение href из элемента с классом 'Unwatched-showTitle'
            const hrefValue = title.getAttribute('href');

            // Создаём новый элемент <div>
            const newElement = document.createElement('div');
            newElement.className = 'Unwatched-showTitle';

            // Перебираем все дочерние элементы элемента <a>
            while (title.firstChild) {
                // Перемещаем каждый дочерний элемент из <a> в новый <div>
                newElement.appendChild(title.firstChild);
            }

            // Заменяем элемент <a> на новый элемент <div>
            title.parentNode.replaceChild(newElement, title);

            // Ищем внутри нового элемента элементы с классом "Unwatched-showTitle-title" и заменяем их на ссылки
            let titleElements = newElement.querySelectorAll('span.Unwatched-showTitle-title');
            if (!titleElements) return;

            titleElements.forEach(titleElement => {
                const newLink = document.createElement('a');
                newLink.href = hrefValue;
                newLink.className = 'Unwatched-showTitle-title';
                newLink.innerHTML = titleElement.innerHTML;

                // Заменяем элемент <span> на новый элемент <a>
                titleElement.parentNode.replaceChild(newLink, titleElement);
                changes += 1;
            });
        });
        MODS.showTitleLinkModified = changes !== 0;
    }

    // Функция замены "5 эпизодов с e1" на "5 эпизодов с e01" на странице myshows.me/profile/
    function modifyShowSeasonMeta() {
        // Находим все элементы <div> с классом "Unwatched-showSeasonMeta"
        const elements = document.querySelectorAll('div.Unwatched-showSeasonMeta');
        const regex = / с e(0(?=$)|[1-9]\d*$)/;
        let changes = 0;

        // Перебираем каждый элемент
        elements.forEach(element => {
            // Получаем текстовое содержимое элемента
            const text = element.textContent;
            // Ищем подстроку " с e" и последующей цифрой
            const match = text.match(regex);
            // Если подстрока найдена и цифра меньше 10
            if (match && parseInt(match[1], 10) < 10) {
                // Заменяем найденную цифру на "0" + цифра.
                // Устанавливаем новый текстовый контент элемента
                element.textContent = text.replace(match[0], ` с e0${match[1]}`);
                changes += 1;
            }
        });
        MODS.showSeasonMetaModified = changes !== 0;
    }

    // Внешний вид стилей попробуем менять через CSS
    function initStyle() {
        // Получить существующий / создать новый элемент <style>
        STYLE = document.querySelector('style') || document.createElement('style');
        if (!STYLE.parentNode) {
            // Вставить новый элемент <style> в <head>
            document.head.appendChild(STYLE);
        }

        const statsRowColor = 'white';
        const hideWatchSoon = changed === 0 ? '' : '.WatchSoon__title-wrapper hidden';

        // Не будем много раз добавлять одно и то же
        const startPhrase = '/* old.myshows.me.start */';
        const endPhrase = '/* old.myshows.me.end */';

        // Находим индексы начала и конца текста между фразами
        let styleContent = STYLE.textContent;
        const startIndex = styleContent.indexOf(startPhrase);
        const endIndex = styleContent.indexOf(endPhrase) + endPhrase.length;

        if (startIndex !== -1 && endIndex !== -1) {
            // Удаляем существующий текст между startPhrase и endPhrase
            STYLE.textContent = (styleContent.substring(0, startIndex) + styleContent.substring(endIndex)).trim();
        }

        STYLE.textContent += /*CSS*/ `
${startPhrase}

.hidden {
	display: none;
}

/* ============================================================================================= */
/* https://myshows.me/<userName> */

.UserHeader__stats-row {
	text-shadow:
		-1px -1px 0 black,
		1px -1px 0 black,
		-1px 1px 0 black,
		1px 1px 0 black;
	color: ${statsRowColor};
}
.UserHeader__stats-title {
	color: ${statsRowColor}
}


/* ============================================================================================= */
/* https://myshows.me/profile/next */

.OldMyShowsClass {
	font-size: 14px;
}
.WatchSoon-episodes .Row {
	height: 30px;
	padding: 0 10px 0 10px;
}
.WatchSoon-date {
	max-width: 43px;
	font-weight: 500;
	font-size: 13px;
}
.WatchSoon-date a {
	display: flex;
	flex-wrap: wrap;
	gap: 0 4px;
}
.WatchSoon-date a div:first-child::after {
	content: ',';
}
.WatchSoon-show {
	font-size: 14px;
}


/* ============================================================================================= */
/* https://myshows.me/profile */

.Unwatched-showTitle-inline {
	display: inline-flex;
}
.Unwatched-showTitle-subTitle {
	display: none;
}
.Unwatched-showTitle-title {
	align-self: auto;
	padding-right: 10px;
}
.Unwatched-season~div .UnwatchedEpisodeItem {
	height: 30px;
}
.UnwatchedEpisodeItem__info {
	display: contents;
}


/* ============================================================================================= */
/* https://myshows.me/profile/edit */

.oldMyshowsSettings-style {
	display: grid;
}
.oldMyshowsSettings-label {
	display: inline-flex;
	margin-top: 20px;
}
.oldMyshowsSettings-label>* {
	margin-right: 7px; /* Увеличение отступа между полем флажка и текстом */
}
.oldMyshowsSettings-checkbox input[type="checkbox"] {
	width: 10px; /* Ширина поля флажка */
	height: 10px; /* Высота поля флажка */
}

${endPhrase}
`.replace(/;/g, '!important;');
    }

    // Функция, которая будет выполняться при изменении DOM
    function checkPage() {
        const currentUrl = window.location.href;

        // Для /profile/next/ ---->
        if (linksAreSimilar(currentUrl, 'https://myshows.me/profile/next/') && MODS.watchSoonElementsModified === false) {
            // Не знаю, какими стандартными функциями получаются данные.
            if (CONFIG.OriginalTitleIsNeeded === true) {
                // Создаём свою карту данных
                createMap();
            }
            if (myMap.size > 0 || CONFIG.OriginalTitleIsNeeded === false) {
                fixWatchSoonElements();
            }
        }
        // <---- Для /profile/next/

        // Для /username/ ---->
        if (linksAreSimilar(currentUrl, `https://myshows.me/${userName}`)) {
            if (CONFIG.ModifyProfileNumbers === true && MODS.profileNumbersModified === false) {
                modifyProfileNumbers();
            }
        }
        // <---- Для /username/

        // Для /profile/ ---->
        if (linksAreSimilar(currentUrl, 'https://myshows.me/profile')) {
            if (CONFIG.ModifyShowTitleLink === true && MODS.showTitleLinkModified === false) {
                modifyShowTitleLink();
            }
            if (CONFIG.ModifyShowSeasonMeta === true && MODS.showSeasonMetaModified === false) {
                modifyShowSeasonMeta();
            }
        }
        // <---- Для /profile/

        // Для /profile/edit/ ---->
        if (linksAreSimilar(currentUrl, 'https://myshows.me/profile/edit')) {
            createCheckboxes();
        }
        // <---- Для /profile/edit/

        // Меняем стили через CSS
        initStyle();
    }

    // Функция, которая будет выполняться после загрузки всего DOM
    function onPageLoad() {
        // Создаём новый экземпляр MutationObserver
        const observer = new MutationObserver(() => {
            // При каждом изменении DOM вызываем функцию checkUrlChange
            checkUrlChange();
            checkPage();
        });
        // Настраиваем наблюдение за изменениями DOM
        observer.observe(document.body, {
            subtree: true,
            childList: true
        });

        // Меняем стили через CSS
        initStyle();
    }

    // Обработчик события загрузки всего DOM
    window.addEventListener('load', onPageLoad());

})();