Greasy Fork镜像 is available in English.

old.myshows.me

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

Mint 2024.05.02.. Lásd a legutóbbi verzió

// ==UserScript==
// @name         old.myshows.me
// @namespace    http://tampermonkey.net/
// @version      2024-v28
// @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';

    const originalTitleIsNeeded = true; // Всегда выводить оригинальные названия на странице 'https://myshows.me/profile/next/'. Не надо — замените на false
    const postfix = '1080'; // Текст после s01e01. Мне так удобнее на торрентах искать. Не надо — замените на ''

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

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

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

    // Проверка, что ссылки одинаковые
    function linksAreSimilar(link1, link2) {
        // Удаляем последний слеш, если он есть
        const link1mod = link1.endsWith('/') ? link1.slice(0, -1) : link1;
        const link2mod = link2.endsWith('/') ? link2.slice(0, -1) : link2;
        // Сравниваем ссылки
        return link1mod === link2mod;
    }

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

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

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

    // Получаем из строки "1 x 1 - название эпизода" данные {se: 's01e01', name: 'название эпизода'}
    function fixEpisodeInfo(episodeText) {
        const [season,, episode, ...rest] = episodeText.split(' ').map(str => str.trim());
        const s = addPrefix(season, 's');
        const e = `${addPrefix(episode, 'e')}${(postfix === '' ? '' : ` ${postfix}`)}`;
        let name = rest.join(' ').trim();
        if (name.startsWith('-')) {
            name = name.replace('-', '').trim();
        }
        return {
            se: `${s}${e}`,
            name: name
        };
    }

    // Исправляем текст, который стал в несколько строк с последним обновлением сайта.
    // И сортируем строки
    function fixWatchSoonElements() {
        const watchSoonElements = document.querySelectorAll('.WatchSoon__title-wrapper');
        if (!watchSoonElements) return;

        //const dataMap = createDataMap(parseScriptData());
        //if (!dataMap.size) return;

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

        // Заполняем массив объектами на основе данных на странице
        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 = originalTitleIsNeeded === true ? fixTitle(showLink) : showLink.textContent;
            const episodeInfo = fixEpisodeInfo(episodeLink.textContent);
            const innerHTML = `<a href="${showLink.href}" target="_blank">${showTitle}</a> — ${episodeInfo.se} — <a href="${episodeLink.href}" target="_blank">${episodeInfo.name}</a>`;

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

        // Сортируем массив по 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);
                changed += 1;
            }
        });
    }

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

    // Создание своей карты соответствия данных
    function createMap() {
        const dataObject = parseScriptData();
        if (!dataObject) return;
        let showIDs; // Массив сериалов
        let iShowIDs; // Адрес массива сериалов
        // Получаем нужные данные из объекта
        if (dataObject.length > 0) {
            // Тут будут нужные данные, если изначально была открыта страница 'https://myshows.me/profile/next/'
            iShowIDs = findKeyInArray(dataObject, 'list', 30);
            if (!iShowIDs) {
                // Тут будут нужные данные, если изначально была открыта страница 'https://myshows.me/<имя профиля>'
                iShowIDs = findKeyInArray(dataObject, 'userShows', 30);
            }
            // Проверяем заполненность. Если пусто, то
            if (!iShowIDs) {
                // Перезагружаем страницу.
                // Тогда точно попадутся правильные данные в '__NUXT_DATA__'
                location.reload();
                return;
            }
            // Если нашли адрес, получаем список сериалов
            showIDs = dataObject[iShowIDs];
        }

        // Перебираем полученный массив сериалов, заполняем карту.
        // На всякий случай с попыткой
        if (showIDs && typeof showIDs.forEach === 'function') {
            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 fixProfileNumbers() {
        // Выбираем все div с классом UserHeader__stats-value на странице
        const statsValues = document.querySelectorAll('div.UserHeader__stats-value');
        if (statsValues && typeof statsValues.forEach === 'function' && statsValues.lenght !== 0) {
            // Перебираем коллекцию элементов и меняем их содержимое
            statsValues.forEach(element => {
                element.textContent = element.title;
            });
        }
    }

    // Функция, чтобы оставить ссылку только в русском названии шоу на странице myshows.me/profile/
    function modifyShowTitleLink() {
        let showTitles = document.querySelectorAll('a.Unwatched-showTitle');
        if (!showTitles) return;
        let newElement;

        showTitles.forEach(function(title) {

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

            // Создаем новый элемент <div>
            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(function(titleElement) {
                newElement = document.createElement('a');
                newElement.href = hrefValue;
                newElement.className = 'Unwatched-showTitle-title';
                newElement.innerHTML = titleElement.innerHTML;

                // Заменяем элемент <span> на новый элемент <a>
                titleElement.parentNode.replaceChild(newElement, titleElement);
                changed += 1;
            });

        });
    }

    // Функция замены "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*$)/;

        // Перебираем каждый элемент
        elements.forEach(function(element) {
            // Получаем текстовое содержимое элемента
            let text = element.textContent;

            // Ищем подстроку " с e" и последующей цифрой
            const match = text.match(regex);

            // Если подстрока найдена и цифра меньше 10
            if (match && parseInt(match[1], 10) < 10) {
                // Заменяем найденную цифру на "0" + цифра
                const newText = ` с e0${match[1]}`;
                // Устанавливаем новый текстовый контент элемента
                element.textContent = text.replace(match[0], newText);
            }
        });
    }

    // Внешний вид стилей попробуем менять через CSS
    function initStyle() {
        // Получить существующий элемент <style>
        STYLE = document.querySelector('style');
        if (!STYLE) {
            // Создать новый элемент <style> и установить его содержимое
            STYLE = document.createElement('style');
            // Вставить новый элемент <style> в <head>
            document.head.appendChild(STYLE);
        }
        const statsRowColor = 'white';
        const hideWatchSoon = changed === 0 ? '' : '.WatchSoon__title-wrapper {display: none;}';

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

.OldMyShowsClass {
    font-size: 14px;
}

.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}
}

.Unwatched-season ~ div .UnwatchedEpisodeItem {
    height: 30px;
}

.WatchSoon-episodes .Row {
    height: 30px;
}

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

     // Функция, которая будет выполняться при изменении DOM
    function checkPage() {
        // Для /profile/next/ ---->
        // Проверяем, является ли текущий URL страницей 'https://myshows.me/profile/next/'
        if (!linksAreSimilar(window.location.href, 'https://myshows.me/profile/next/') && changeIsNeeded !== true) {
            changeIsNeeded = true;
            changed = 0;
        }
        if (linksAreSimilar(window.location.href, 'https://myshows.me/profile/next/') && changeIsNeeded === true) {
            // Не знаю, какими стандартными функциями получаются данные.
            if (originalTitleIsNeeded === true) {
                // Создаём свою карту данных
                createMap();
            }
            if (myMap.size > 0 || originalTitleIsNeeded === false) {
                fixWatchSoonElements();
            }
            changeIsNeeded = changed === 0;
        }
        // <---- Для /profile/next/

        // Для /username/ ---->
        if (linksAreSimilar(window.location.href, 'https://myshows.me/' + userName)) {
            fixProfileNumbers();
        }
        // <---- Для /username/

        // Для /profile/ ---->
        if (linksAreSimilar(window.location.href, 'https://myshows.me/profile')) {
            modifyShowTitleLink();
            modifyShowSeasonMeta();
        }
        // <---- Для /profile/

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

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

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

})();
长期地址
遇到问题?请前往 GitHub 提 Issues,或加Q群1031348184

赞助商

Fishcpy

广告

Rainyun

注册一下就行

Rainyun

一年攒够 12 元