Полезная информация

Хотите узнать больше о расширениях? Посмотрите ролики, рассказывающие о работе с расширениями Firefox.
  • Форумы
  •  » Скрипты
  •  » YouTube Video Preview and Ratings Keyless и его частичная русификация

№101-05-2021 21:48:09

Пандёнок
Участник
 
Группа: Extensions
Зарегистрирован: 04-11-2008
Сообщений: 5543
UA: Firefox 78.0

YouTube Video Preview and Ratings Keyless и его частичная русификация

YouTube Video Preview and Ratings Keyless
Возможности:
Мгновенный предварительный просмотр видео во всплывающем проигрывателе при наведении курсора или щелчке на превью. Рейтинги видео и данные разрешения отображаются на превью.
Расширение получает информацию о видео без использования ключа API YouTube, который можно запретить или ограничить.
Скрипт добавляет маленькую красно-серую квадратную кнопку в верхнем правом углу страниц YouTube, при нажатии этой кнопки открываются настройки скрипта: включение/отключение предпросмотра при наведении, задержка при наведении, включение/отключение предпросмотра по щелчку, показа разрешения (качества) видео и рейтингов. Настройки применяются при нажатии крестика для закрытия меню настроек.


Установить скрипт (с настройками на английском)


Как сделать меню на русском?
Очень просто. На примере расширения Tampermonkey (но подобным образом это делается и в других расширениях для скриптов):
1. Жмём кнопку Tampermonkey в панели инструментов — открываем «Панель управления» — жмём кнопку «Редактировать» (листочек с карандашом).
2. Жмём «Найти», набираем фразу «Preview Options», ставим галочку «Aa» (то есть учёт регистра букв) и жмём «Найти далее».
3. Теперь важно заменить только выделенные мной здесь слова того фрагмента кода, который мы нашли, не трогая ничего остального — ни кавычек "…" вокруг слов, ни чего-то ещё рядом, каким бы неважным оно ни казалось!
Итак, я заменил:

скрытый текст

function pref_popup_open() {
    if (document.getElementById("vpp_pref_popup")) {
        return;
    }
    const popup = newElem("span", {"id": "vpp_pref_popup"}, null, document.body);
    newElem("div", {"id": "vpp_pref_title"}, "Preview Options", popup);

    let changed = false;

    const closemark = newElem("span", {"id": "vpp_pref_close", "title": "close options"}, "\u2716", popup);
    closemark.onclick = function () {
        popup.parentNode.removeChild(popup);
        if (changed) {
            location.reload();
        }
    };

    let mark = function(){changed = true;};
    new_checkbox("floatEnable", "Hover Preview", "div", popup, null, mark);
    new_plusminus("floatDelay", "open delay", popup);
    new_checkbox("playerEnable", "Click Preview", "div", popup, null, mark);
    new_checkbox("rateEnable", "Video Rating", "div", popup, null, mark);
    new_checkbox("defEnable", "Video Resolution", "div", popup, null, mark);
}


заменил на вот это:
скрытый текст

function pref_popup_open() {
    if (document.getElementById("vpp_pref_popup")) {
        return;
    }
    const popup = newElem("span", {"id": "vpp_pref_popup"}, null, document.body);
    newElem("div", {"id": "vpp_pref_title"}, "Опции предпросмотра", popup);

    let changed = false;

    const closemark = newElem("span", {"id": "vpp_pref_close", "title": "Закрыть и применить настройки"}, "\u2716", popup);
    closemark.onclick = function () {
        popup.parentNode.removeChild(popup);
        if (changed) {
            location.reload();
        }
    };

    let mark = function(){changed = true;};
    new_checkbox("floatEnable", "Предпросмотр при наведении", "div", popup, null, mark);
    new_plusminus("floatDelay", "открыть через", popup);
    new_checkbox("playerEnable", "Предпросмотр по щелчку", "div", popup, null, mark);
    new_checkbox("rateEnable", "Рейтинг видео", "div", popup, null, mark);
    new_checkbox("defEnable", "Разрешение", "div", popup, null, mark);
}


Сами слова можете написать какие вам нравятся (например, не «предпросмотр», а «превью», не «разрешение», а «качество», не «опции», а «настройки»), главное, чтобы не были затронуты кавычки и то, что за их пределами — иначе скрипт будет работать неправильно! По той же причине я не советую менять весь кусок оригинального кода на «мой» — в коде автор иногда делает изменения, а это сообщение не будет обновляться вслед за ними, потому меняйте только текст, который я здесь выделил.
4. Теперь в окне редактора жмём Файл — Сохранить.
Готово!


Мой перевод фразы «close options» как «Закрыть и применить настройки» — не ошибка, а намеренно сделанное пояснение. Новые настройки не действуют, пока меню не закрыто щелчком по этому крестику.

Отредактировано Пандёнок (02-05-2021 06:21:03)

Отсутствует

 

№202-05-2021 00:02:08

_zt
Участник
 
Группа: Members
Зарегистрирован: 10-11-2014
Сообщений: 1392
UA: Firefox 78.0

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

Пандёнок
1. При наведении действительно есть предпросмотр, который сам по себе мне вообще не интересен, так как уже давно реализован где попало и кроме раздражения ничего не вызывает.
2. В скрипте намного больше строк для перевода, чем вы нашли.
   
Интересен скрипт-плеер который вызывается по щелчку на значке в миниатюрах. Главная особенность этого скрипт-плеера, то что он позволяет последовательно просмотреть все видео на любой странице YT без лишних щелчков и перезагрузок страницы.
   
В нем то и находится еще два десятка строк для перевода. Настройки скрипт-плеера - в блоках под строкой "function player_options(parent) {" и кнопки заголовка скрипт-плеера - в блоках под строкой "const buttonArea = newElem("span", {"id":". Есть и еще по мелочи, уведомления и т.п.
   
Из того набора функций которыми я собираюсь пользоваться еще заметил, что не работает смена цвета разрешений на миниатюрах в зависимости от качества видео. А такая функция изначально была заложена. Для всех разрешений у меня работает строка с цветом для HD, т.е.".vpp_meta_def_hd.HD {color:...". Раз уж цветовая индикация не работает, то есть смысл раскрасить это более светлым оттенком.

Отредактировано _zt (02-05-2021 00:19:59)

Отсутствует

 

№302-05-2021 06:13:36

oleg.sgh
Участник
 
Группа: Members
Зарегистрирован: 19-04-2014
Сообщений: 3141
UA: Firefox 85.0

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

Пандёнок

Как сделать меню на русском?

Есть подозрение, что русификация слетит при ручном обновлении скрипта (измененный скрипт не обновляется). У меня слетает измененный размер плеера, и часть этого кода записана в блокнот.

Отсутствует

 

№402-05-2021 06:20:41

Пандёнок
Участник
 
Группа: Extensions
Зарегистрирован: 04-11-2008
Сообщений: 5543
UA: Yandex 21

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

_zt
1. Мне он тоже не интересен, я оставил только разрешение видео и заодно рейтинги.
2. Так переведите. Не пользуясь этими функциями и не имея понятия о них, я не могу проверить правильность перевода.

oleg.sgh

Есть подозрение, что русификация слетит при ручном обновлении скрипта (измененный скрипт не обновляется). У меня слетает измененный размер плеера, и часть этого кода записана в блокнот.

Это не подозрение. Точно слетит. Но недолго сделать заново.

Добавлено 02-05-2021 06:23:05
Кто-нибудь может починить смену цвета разрешений на миниатюрах в зависимости от качества видео?

Добавлено 02-05-2021 06:29:41

Выделить код

Код:

if(max_res != 0) {
            parent.setAttribute("vpp_meta_def", "");
            const def_node = newElem("span", {"class": "vpp_meta_def_container", "reveal": "true", "title": max_quality_label}, null, parent);
            const def_txt =
                max_res >= (15360 * 8640) ? "16K" :
                max_res >= (7680 * 4320) ? "8K" :
                max_res >= (2880 * 2160) ? "4K" :
                max_res >= (960 * 1080) ? "1080p" :
                max_res >= (640 * 720) ? "720p" : "SD";
            newElem("span", {"class": "vpp_meta_def_hd HD"}, def_txt, def_node);
        }

— похоже, автор отказался от разных цветов.

Отредактировано Пандёнок (02-05-2021 06:30:30)

Отсутствует

 

№502-05-2021 06:32:24

oleg.sgh
Участник
 
Группа: Members
Зарегистрирован: 19-04-2014
Сообщений: 3141
UA: Firefox 85.0

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

Пандёнок

Но недолго сделать заново.

Оно так, только переводить в скрипте слова опции, рейтинг, плеер...овчинка выделки не стоит. Я за всё время существования этого скрипта только раз лазил в настройки, когда он был переписан под новый интерфейс Ютуба.

Отсутствует

 

№602-05-2021 06:42:09

Пандёнок
Участник
 
Группа: Extensions
Зарегистрирован: 04-11-2008
Сообщений: 5543
UA: Yandex 21

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

oleg.sgh
Подозреваю, что через userContent.css можно сделать русификацию, которая не слетит, но я не умею. :)

Отсутствует

 

№702-05-2021 06:47:35

oleg.sgh
Участник
 
Группа: Members
Зарегистрирован: 19-04-2014
Сообщений: 3141
UA: Firefox 85.0

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

Пандёнок
Нечто подобное я подозреваю и с размером плеера...но тоже не умею :P

Отсутствует

 

№802-05-2021 15:35:03

_zt
Участник
 
Группа: Members
Зарегистрирован: 10-11-2014
Сообщений: 1392
UA: Firefox 78.0

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

Пандёнок
Я не вижу здесь никого кому нужен перевод, а позже скрипт будет уже обновлен автором. )
Да, похоже смена цвета вырезана, иначе, в той секции которую вы привели, должны бы были назначаться классы для каждого разрешения, а не один класс на все.

Отсутствует

 

№903-05-2021 22:20:43

Пандёнок
Участник
 
Группа: Extensions
Зарегистрирован: 04-11-2008
Сообщений: 5543
UA: Firefox 78.0

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

_zt

_zt пишет

а позже скрипт будет уже обновлен автором.

и тогда я возьму отсюда готовый перевод.

Отсутствует

 

№1006-05-2021 14:59:00

_zt
Участник
 
Группа: Members
Зарегистрирован: 10-11-2014
Сообщений: 1392
UA: Firefox 88.0

Re: YouTube Video Preview and Ratings Keyless и его частичная русификация

Пандёнок

скрытый текст

Выделить код

Код:

// ==UserScript==
// @name         YouTube Video Preview and Ratings Keyless
// @namespace    YouTubeVideoPreviewPlayer
// @version      20210316
// @description  Instant video previews in popup player by hovering or clicking video thumbs. Video ratings and resolution data shown on the thumbs. Gets video information without using a YouTube API key, which can be banned or limited.
// @author       Couchy
// @match        https://www.youtube.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @noframes
// ==/UserScript==

//==================================================================
//Userscript specific functions

function debug(...args) {
    //console.log(...args);
}

function set_pref(preference, new_value) {
    GM_setValue(preference, new_value);
}

function get_pref(preference) {
    return GM_getValue(preference);
}

function init_pref(preference, new_value) {
    let value = get_pref(preference);
    if (value == null) {
        set_pref(preference, new_value);
        value = new_value;
    }
    return value;
}

//==================================================================
// Styles

const style_basic = `
/* prefs */
#vpp_pref_popup {direction:ltr; font:11px/11px Roboto,arial,sans-serif; position:fixed; right:0px; top:0px; color:#e0e0e0; background:#202020; padding:15px 15px 15px 10px; border-radius:3px; box-shadow:0px 0px 5px 1px gray; /*z-index:2147483647;*/ z-index:2147483646;}
.vpp_pref_group {margin-left:15px; color:yellow;}
#vpp_pref_close {font:14px/14px Roboto,arial,sans-serif; color:lightgray; position:absolute; top:3px; right:5px; cursor:pointer; user-select:none; -moz-user-select:none;}
#vpp_pref_close:hover {color:white;}
#vpp_pref_title {font:500 13px/13px Roboto,arial,sans-serif; padding:5px !important;}
#vpp_pref_button {cursor:pointer; width:18px; height:18px; background-size:contain; background-repeat:no-repeat; opacity:0.7; position:absolute; right:0px; top:0px; user-select:none; -moz-user-select:none;}
#vpp_pref_button:hover {opacity:1;}
#vpp_pref_button {background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAeUlEQVR42u2UwQ2AMAwDk52YCWaCmdgp7QshKP6QKn7Yv0pudEqcuBXLBXB/7GaBzFv3Z3k+AdYY/z3cr+IZHgywPCZzxrv4T48AyAFAeKaH0ICmr2GFuDpAkQGOLRBAOQAIjw5Rtrg6QJEBji0QQDkACI8OUbYE0ADbonowcNn6sgAAAABJRU5ErkJggg==');}
#vpp_pref_delay_text {margin-left:20px; color:khaki;}
#vpp_pref_delay_num {margin-left:3px; color:khaki;}
.vpp_pref_delay_plusminus {margin-left:5px; cursor:pointer; text-align:center; font-weight:800; color:black; background:#B0B0B0; border-radius:2px; display:inline-block; width:10px; user-select:none; -moz-user-select:none;}
.vpp_pref_delay_plusminus:hover {background:#D0D0D0;}
#vpp_pref_delay_plus {margin-left:2px;}
#vpp_pref_delay_minus {margin-left:5px;}
/* meta data */
.vpp_meta_def_container {font:500 12px/14px Roboto,arial,sans-serif; position:absolute; top:0px; left:0px; background:#e8e8e8; padding:0px 3px; border-radius:2px; display:none; cursor:default;}
html[dark] .vpp_meta_def_container {background:#202020 !important;}
.vpp_meta_def_container[reveal] {display: block;}
.vpp_meta_def_container[space] .vpp_meta_def_hd {margin-right:2px;}
.vpp_meta_def_format {position:relative; color:black;}
 html[dark] .vpp_meta_def_format {color:#f0f0f0 !important;}
.vpp_meta_def_hd {position:relative;}
.vpp_meta_def_hd.HD {color:#FF6969;} //цвет текста разрешения на превью
.vpp_meta_def_hd.UHD {color:red;}
.vpp_meta_rate {direction:ltr; font:500 12px/14px Roboto,arial,sans-serif; position:absolute; top:0px; right:0px; color:white; padding:0px 3px; border-radius:2px; cursor:default;}
body[dir='ltr'] [vpp_meta_rate] ytd-thumbnail-overlay-toggle-button-renderer, body[dir='rtl'] [vpp_meta_def] ytd-thumbnail-overlay-toggle-button-renderer {margin-top:12px !important;}
#vpp_meta_box {position:relative; float:left; height:13px; margin-top:3px;}
#vpp_meta_box .vpp_meta_def_container {background:none !important; position:relative; clear:none; float:left;}
#vpp_meta_box .vpp_meta_rate {position:relative; clear:none; float:right; margin-left:5px;}
#gridtube_title_container {position:absolute; top:5px; right:5px;}
body[dir='rtl'] #gridtube_title_container {left:5px !important; right:auto !important;}
body ytd-video-primary-info-renderer {position:relative !important;}
/* play button*/
#vpp_now_playing {font:500 14px/14px Roboto,arial,sans-serif; position:absolute; bottom:0px; left:0px; background:red; color:white; padding:5px; cursor:default; z-index:0;}
body[dir='rtl'] #vpp_now_playing {left:auto !important; right:0px !important;}
body[vpp_reveal_play_button] .vpp_play_button, *[vpp_play_marked]:hover .vpp_play_button {visibility:visible !important;}
.vpp_play_button_container {position:absolute; bottom:0px; left:0px; width:100%;}
.vpp_play_button {display:block !important; position:relative !important; padding-bottom:1px !important; margin:0px auto !important; width:25px !important; height:25px !important;
                  opacity:0.75 !important; cursor:pointer !important; background-size:25px !important; background-repeat:no-repeat !important; text-decoration:none !important; z-index:1; user-select:none; -moz-user-select:none;}
.vpp_play_button:hover {opacity:1 !important;}
.vpp_play_button {background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAG7AAABuwBHnU4NQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAOqSURBVFiF5ZdPSBxnGMZ/M98s1V1nV01A17VZUwMRqsGY9FyQlBJQ2gYPIbaB0nMgwYZWeumxIQhlT7aQW0Mhl54CvQQiISQhkixN\
bWKXaNSaYuzu6v4Z98+38/WgY9Vu9l835NAHvsvM983zvs/7vPPOaEopXif018oOGAAfjY4GgEvAu0DnK+b8A5gCvvjp6tVl7cMzZ94EwkArgM/n42BXF7peX3Fs22b+2TPW19edSzGg3wAmgFbTNPlybIyDBw7UlXgv5hcX+WZigmQy2QpMiJ6+vhDQ9Pn587wVDGLb9itdXtOku7ubW7dvA+wzgHa3201XMEg2nyebzyOlpN69oQGG\
YfCGy0VXMIjb7cayrHYDoDMQIJPLkUynKdh2nal3Q+g6psdDZyDA75HIZhcoIJFKIQuFkofz2SxCCHTDqDkA27ZJpFLbCusAuVyOvJQopUquWDzO5OQkfy4vl91bauWlJJfL/ROAlLJiEy0sLfFtKMTP16+TsayazSilBLZeRI405aBsm8JWmW7cvMn0gweMnDrFocOHay7JpgeUqiiAQqGwHQBALB7n+ytXONLXx/DQEKbPVzGxM4Oq\
U0CpXQE4eBgOMzMzwwfDwwwcO1aVSatSwN5Rgr1TVErJj9eucefuXUZGRmjz+8smAzumYUXmKRSQUiKl3C7H3vV0fp5Lly9za2oKrcRzdylQLKOiCkDREuzFe4ODfDo6yloqxUo0WnJvzSUohg6/n/GxMXp6eogsLJDd6vViqM2ELwlACMHHp0/z2dmzLK2s8MvsbNlnOai6DZ0XiIMjvb18PT6Ox+vl4ZMnFZXI4dwOAKpvwyaPhwvn\
zjF08iSzc3Msrq5WRLwX28OoGg+8f+IEX128SMKyuBcO1zS6nTObClRYAndjI9+FQvT29vJbJMJGNlsDtRPBjhLouo6tFFqZM/v278dsamL60aPaidnM3vnmNIQQaEKwYVk0NDSUPJhMp0mm0/+JHCCTyaALgcswMLymqWwptfVEgkwmg9vtRgiBppXTozo4BrYsi2wuh7JtvF6vMjra21+sRqNtqVSKtKYRi8frSvyyYDSgw+9/YRw/\
evTpnfv329aiUQyXC+qceRF2lG3T0tzM8f7+OW3m8eP++NrajXvT061SSqRSZb8Ny6FoCpqGEIIGw8DlcvHOwEC82ecb1JRS/BWLvZ1MJn/I5/N+lGpUOwxQSo+SPtm6968dmrbhEuK51zQ/aWlp+VX73/8d/w3y7NP9Di2fPgAAAABJRU5ErkJggg==') !important;}
/* preview player */
#vpp_player_area {position:fixed; width:100%; height:100%; top:0px; left:0px; background:rgba(0,0,0,0.5); overflow:hidden !important; z-index:2147483646 !important;}
#vpp_player_area2 {position:relative; width:100%; height:100%; visibility:hidden;}
#vpp_player_box {position:absolute; background:#606060; box-shadow:0px 0px 8px 3px rgba(128,128,128,0.9); border-radius:5px 5px 0px 0px; max-width:100%; max-height:100%;}
#vpp_player_box[player_pos='00']:not([player_size='fit']) {top:0px; left:0px;}
#vpp_player_box[player_pos='01']:not([player_size='fit']) {top:0px; left:0px; right:0px; margin:auto;}
#vpp_player_box[player_pos='02']:not([player_size='fit']) {top:0px; right:0px;}
#vpp_player_box[player_pos='10']:not([player_size='fit']) {top:0px; bottom:0px; left:0px; margin:auto;}
#vpp_player_box[player_pos='11']:not([player_size='fit']) {top:0px; bottom:0px; left:0px; right:0px; margin:auto;}
#vpp_player_box[player_pos='12']:not([player_size='fit']) {top:0px; bottom:0px; right:0px; margin:auto;}
#vpp_player_box[player_pos='20']:not([player_size='fit']) {bottom:0px; left:0px;}
#vpp_player_box[player_pos='21']:not([player_size='fit']) {bottom:0px; left:0px; right:0px; margin:auto;}
#vpp_player_box[player_pos='22']:not([player_size='fit']) {bottom:0px; right:0px;}
#vpp_player_box[player_pos='right']:not([player_size='fit']) {float:right;}
#vpp_player_box[player_size='xxsmall'] {/*320x180*/ width:320px; height:200px;}
#vpp_player_box[player_size='xsmall'] {/*512x288*/ width:512px; height:308px;}
#vpp_player_box[player_size='small'] {/*768x432*/ width:768px; height:452px;}
#vpp_player_box[player_size='medium'] {/*1024x576*/  width:1024px; height:596px;}
#vpp_player_box[player_size='large'] {/*1280x720*/ width:1280px; height:740px;}
#vpp_player_box[player_size='xlarge'] {/*1600x900*/ width:1600px; height:920px;}
#vpp_player_box[player_size='xxlarge'] {/*1920x1080*/ width:1920px; height:1100px;}
#vpp_player_box[player_size='fit'] {width:100%; height:100%; border-radius:0px !important;}
#vpp_player_holder {position:relative; width:100%; height:100%;}
#vpp_player_holder2 {position:absolute; top:20px; left:0px; right:0px; bottom:0px; margin:auto; background:black;}
#vpp_player_box[player_size='fit'] #vpp_player_holder2 {left:0px !important; right:0px !important;}
#vpp_player_frame {position:relative; width:100%; height:100%; display:block; border:0px;}
#vpp_player_button_area_top {direction:ltr; font:500 14px/20px Roboto,arial,sans-serif; color:#101010; position:absolute; top:-2px; left:10px;}
#vpp_player_button_area_next {font:500 19px/20px Roboto,arial,sans-serif; color:#101010; position:absolute; top:0px; right:35px;}
.vpp_player_button {position:relative; cursor:pointer; padding:0px 5px; user-select:none; -moz-user-select:none;}
.vpp_player_button[button_kind='plus'], .vpp_player_button[button_kind='minus'] {font:500 20px/20px Roboto,arial,sans-serif !important; top:2px;}
.vpp_player_button[button_kind='left'] {padding:0px 2px 0px 5px;}
.vpp_player_button[button_kind='right'] {padding:0px 2px;}
.vpp_player_button[button_kind='up'] {padding:0px 2px;}
.vpp_player_button[button_kind='down'] {padding:0px 5px 0px 2px;}
.vpp_player_button:hover {color:#E0E0E0;}
#vpp_player_close_mark {font:14px/20px Roboto,arial,sans-serif; position:absolute; top:0px; right:5px; cursor:pointer; user-select:none; -moz-user-select:none;}
#vpp_player_close_mark:hover {color:#E0E0E0;}\
/* float preview */
#vpp_float_box {position:absolute; box-shadow:0px 0px 8px 3px rgba(128,128,128,0.9); background:black; z-index:2147483647;}
#vpp_float_frame {position:relative; width:100%; height:100%; border:0px;}
/* player options */
#vpp_player_options_popup {direction:ltr; position:absolute; left:0px; top:0px; font:11px/11px Roboto,arial,sans-serif; color:white; background:linear-gradient(#888888,#787878); padding:5px; border-radius:5px; /*z-index:2147483647;*/ z-index:2147483646;}
.vpp_player_options_text {font-weight:500; margin-left:5px; margin-top:7px; color:lemonchiffon;}
.vpp_player_options_close {font:14px/14px Roboto,arial,sans-serif; color:black; position:absolute; top:3px; right:5px; cursor:pointer; user-select:none; -moz-user-select:none;}
.vpp_player_options_close:hover {color:lightgray;}
.vpp_player_options_title {font:500 13px/13px Roboto,arial,sans-serif; padding:3px !important; color:lemonchiffon;}
/*other*/
.watched .video-thumb {opacity:1 !important;}
.yt-subscribe-button-right {margin-top:12px !important;}
.pl-video .pl-video-thumbnail,
.pl-video .pl-video-thumb,
.pl-video .yt-thumb {width: 120px !important;}
.pl-video .yt-thumb-clip > img {width:120px !important; height:auto !important;}
.pl-video-time .timestamp {padding-top:18px !important;}
`;


//==============================================================
//basic

const AREA_ID = "vpp_player_area";
const BOX_ID = "vpp_player_box";
const HOLDER_ID = "vpp_player_holder";

function newElem(tag, attrs = {}, text = null, parent = null) {

    const node = document.createElement(tag);
    for (let attr in attrs) {
        node.setAttribute(attr, attrs[attr]);
    }
    if (text) {
        node.textContent = text;
    }

    parent?.appendChild(node);

    return node;
}

function insertStyle(str, id, doc = document) {
    const style = document.getElementById(id);
    if (style) {
        style.textContent = str;
    }
    else {
        newElem("style", {"type": "text/css", "id": id}, str, document.head);
    }
}

function injectScript(str, src, doc = document) {
    if (str) {
        document.head.removeChild(newElem("script", {}, str, document.head));
    }
    else if (src) {
        newElem("script", {"src": src}, null, document.head)
    }
}

function xpath(outer_dom, inner_dom, query) {
    //XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7
    return outer_dom.evaluate(query, inner_dom, null, 7, null);
}


function docsearch(query) {
    return xpath(document, document, query);
}


function innersearch(inner, query) {
    return xpath(document, inner, query);
}


function simulClick(el) {
    const clickEvent = document.createEvent("MouseEvents");
    clickEvent.initEvent("click", true, true);
    clickEvent.artificialevent = true;
    el.dispatchEvent(clickEvent);
}

function filter(str, w, delim) {
    if (str) {
        const m = str.match(RegExp(`[${delim}]${w}[^${delim}]*`));
        if (m != null) {
            return m[0].replace(RegExp(`[${delim}]${w}`), "");
        }
    }
    return null;
}

//==============================================================
//preferences

var pref_floatEnable = init_pref("floatEnable", true);
var pref_floatDelay = init_pref("floatDelay", 1);
var pref_playerEnable = init_pref("playerEnable", true);
var pref_rateEnable = init_pref("rateEnable", true);
var pref_defEnable = init_pref("defEnable", true);

function new_plusminus(prefname, str, parent) {
    const div = newElem("div", {"class": "vpp_generic"}, null, parent);
    newElem("span", {"id": "vpp_pref_delay_text"}, str, div);
    const num = newElem("span", {"id": "vpp_pref_delay_num"}, get_pref("floatDelay").toString() + "s", div);
    const minus = newElem("span", {"id": "vpp_pref_delay_minus", "class": "vpp_pref_delay_plusminus"}, "\u2212", div);
    const plus = newElem("span", {"id": "vpp_pref_delay_plus", "class": "vpp_pref_delay_plusminus"}, "\u002B", div);

    minus.onclick = function () {
        let val = get_pref("floatDelay");
        if (val > 0) {
            val--;
            set_pref("floatDelay", val);
            num.textContent = get_pref("floatDelay").toString() + "s";
        }
    }

    plus.onclick = function () {
        let val = get_pref("floatDelay");
        if (val < 5) {
            val++;
            set_pref("floatDelay", val);
            num.textContent = get_pref("floatDelay").toString() + "s";
        }
    }

}


function new_checkbox(prefname, str, kind, parent, value, func = function(){}) {
    const div = newElem(kind, {"class": "vpp_generic"}, null, parent);
    const input = newElem("input", {"class": "vpp_generic", "type": "checkbox"}, null, div);
    if (!value) {
        input.checked = get_pref(prefname);
        input.onclick = function (e) {
            const val = get_pref(prefname);
            set_pref(prefname, !val);
            e.target.checked = !val;
            func();
        };
    }
    else {
        input.value = value;
        input.checked = (get_pref(prefname) == input.value);
        input.onclick = function (e) {
            const val = get_pref(prefname);
            set_pref(prefname, e.target.value);
            e.target.checked = true;
            const other = innersearch(parent.parentNode, `.//input[@value='${val}']`).snapshotItem(0);
            if (other && (other != e.target)) other.checked = false;
            func();
        };
    }
    newElem("span", {"class": "vpp_opendelay"}, str, div);
}


function pref_popup_close() {
    const popup = document.getElementById("vpp_pref_popup");
    popup?.parentNode?.removeChild(popup);
}

function pref_popup_open() {
    if (document.getElementById("vpp_pref_popup")) {
        return;
    }
    const popup = newElem("span", {"id": "vpp_pref_popup"}, null, document.body);
    newElem("div", {"id": "vpp_pref_title"}, "Настройки", popup);

    let changed = false;

    const closemark = newElem("span", {"id": "vpp_pref_close", "title": "Применить"}, "\u2716", popup);
    closemark.onclick = function () {
        popup.parentNode.removeChild(popup);
        if (changed) {
            location.reload();
        }
    };

    let mark = function(){changed = true;};
    new_checkbox("floatEnable", "При наведении", "div", popup, null, mark);
    new_plusminus("floatDelay", "задержка", popup);
    new_checkbox("playerEnable", "По щелчку", "div", popup, null, mark);
    new_checkbox("rateEnable", "Рейтинг", "div", popup, null, mark);
    new_checkbox("defEnable", "Качество", "div", popup, null, mark);
}


//==================================================================
// Player

const basic_str = "((local-name()='ytd-thumbnail') or (local-name()='ytd-playlist-thumbnail')) and (not(ancestor::*[@hidden]))";
const basic_str2 = "//img[contains(@src,'vi/') or contains(@src,'vi_webp/') or contains(@src,'/p/') or contains(@src,'/s_p/')]";

function player_script() {
    injectScript(`if (YT) {
                            var player = new YT.Player('vpp_player_frame');
                            var errort = null;
                            function error_reset() { if (errort) clearTimeout(errort); }
                            function check_error(t,fid) {
                               error_reset();
                               errort = setTimeout(
                                    function (fid) {
                                       var f = document.getElementById('vpp_player_frame');
                                       if (!f) return;
                                       if (f.getAttribute('fid') != fid) return;
									   fdoc = f.contentWindow.document;
                                       var s = player.getPlayerState();
                                       var a = player.getPlaylist();
                                       var i = player.getPlaylistIndex();
                                       if (a != null ? a.length == 0 : false)
                                           f.dispatchEvent(new Event('playend'));
                                       else
                                           if ((s == -1 && fdoc.getElementsByClassName('ytp-error').length > 0) || s == 5)
                                              if ((a != null && i != null) ? i < a.length - 1 : false)
                                                 player.nextVideo();
                                              else
                                                 f.dispatchEvent(new Event('playend'));
                                    }, t, fid);
                            }
                            player.addEventListener('onReady',
                                   function () {
                                        var f = document.getElementById('vpp_player_frame');
                                        if (f) {
                                             var q = f.getAttribute('quality');
                                             if (q != 'default' && q != null) player.setPlaybackQualityRange(q);
                                         }
                                   });
                            player.addEventListener('onStateChange',
                                 function () {
                                    var f = document.getElementById('vpp_player_frame');
                                    if (!f) return;
                                    var fid = f.getAttribute('fid');
                                    var q = f.getAttribute('quality');
                                    var s = player.getPlayerState();
                                    var a = player.getPlaylist();
                                    var i = player.getPlaylistIndex();
                                    var cond = ((a != null && i != null) ? i == a.length - 1 : true);
                                    if (s == -1 || s == 5) check_error(10000,fid); else error_reset();
                                    if (s == -1 && q != 'default' && q != null) player.setPlaybackQualityRange(q);
                                    if (s == 0 && cond) f.dispatchEvent(new Event('playend'));
                                 });
                            var frame = document.getElementById('vpp_player_frame');
                            if (frame) {
                                  check_error(10000,frame.getAttribute('fid'));
                                  frame.addEventListener('loadnewvideo',
                                                          function (x) {
                                                             var fid = x.target.getAttribute('fid');
                                                             var url = x.target.getAttribute('newvidurl');
                                                             var plist = x.target.getAttribute('plist');
                                                             player.pauseVideo();
                                                             if (plist) {
                                                                player.loadPlaylist({'list':plist});
                                                                check_error(10000,fid);
                                                             }
                                                             else if (url) {
                                                                player.loadVideoByUrl(url);
                                                                check_error(10000,fid);
                                                             }
                                                          });
                            }
                     }`);
}

const choices_def = ['default', 'small', 'medium', 'large', 'hd720', 'hd1080', 'hd1440', 'highres'];
const choices_size = ['xxsmall', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'xxlarge'];
const choices_pos = ['00', '01', '02', '10', '11', '12', '20', '21', '22'];

const scriptPrefs = new Proxy(JSON.parse(GM_getValue("YTVPR_prefs",
     `{
        "floatEnable": true,
        "floatDelay": 1,
        "playerEnable": true,
        "rateEnable": true,
        "defEnable": true,
        "playerFit": false,
        "playerDef": 0,
        "playerSize": 3,
        "playerPosLeft": 1,
        "playerPosTop": 1,
        "playerNext": true,
        "playerClose": true,
        "playerPause": true,
        "playerDim": true
     }`)),
     {
    set: function(obj, prop, value) {
        const clamp = function(val, min, max){ return Math.min(Math.max(min, val), max); };
        switch (prop) {
            case "playerDef": value = clamp(value, 0, choices_def.length()-1); break;
            case "playerSize": value = clamp(value, 0, choices_size.length()-1); break;
            case "playerPosLeft":
            case "playerPosTop": value = clamp(value, 0, 2); break;
        }

        if(obj[prop] != value) {
            obj[prop] = value;
            GM_setValue("YTVPR_prefs", JSON.stringify(obj));
        }
        return true;
    }
});

//player preferences
if (pref_playerEnable) {
    init_pref("playerFit", false);
    init_pref("playerDef", "default");
    init_pref("playerSize", "medium");
    init_pref("playerPos", "11");
    init_pref("playerNext", true);
    init_pref("playerClose", true);
    init_pref("playerPause", true);
    init_pref("playerDim", true);

    //fix char preferences
    if (choices_def.indexOf(get_pref("playerDef")) < 0) set_pref("playerDef", 'default');
    if (choices_size.indexOf(get_pref("playerSize")) < 0) set_pref("playerSize", 'medium');
    if (choices_pos.indexOf(get_pref("playerPos")) < 0) set_pref("playerPos", '11');
}


function player_options(parent) {
    if (document.getElementById("vpp_player_options_popup")) {
        return;
    }

    const popup = newElem("span", {"id": "vpp_player_options_popup"}, null, parent);
    newElem("div", {"class": "vpp_player_options_title"}, "Настройки плеера", popup);

    const closemark = newElem("span", {"class": "vpp_player_options_close", "title": "Закрыть"}, "\u2716", popup);
    closemark.onclick = close_player_options;

    new_checkbox("playerNext", "Автовоспроизводить следующее", "div", popup);
    new_checkbox("playerDim", "Снижать яркость фона", "div", popup, null, function () {
        document.getElementById(AREA_ID).style.visibility = (get_pref("playerDim") ? "visible" : "hidden");
    });
    new_checkbox("playerClose", "Закрывать по щелчку вне плеера", "div", popup);
    new_checkbox("playerPause", "Пауза YT-плеера при запуске скрипта", "div", popup);

    newElem("div", {"class": "vpp_player_options_text"}, "Разрешение", popup);
    //default, small, medium, large, hd720, hd1080, hd1440, highres;
    const group1 = newElem("div", {"class": "vpp_player_options_group"}, null, popup);
    const group2 = newElem("div", {"class": "vpp_player_options_group"}, null, popup);
    new_checkbox("playerDef", "Default", "span", group1, "default");
    new_checkbox("playerDef", "LQ 240", "span", group1, "small");
    new_checkbox("playerDef", "MQ 360", "span", group1, "medium");
    new_checkbox("playerDef", "HQ 480", "span", group1, "large");
    new_checkbox("playerDef", "HD 720", "span", group2, "hd720");
    new_checkbox("playerDef", "HD 1080", "span", group2, "hd1080");
    new_checkbox("playerDef", "HD 1440", "span", group2, "hd1440");
    new_checkbox("playerDef", "MAX", "span", group2, "highres");
}

function close_player_options() {
    const popup = document.getElementById("vpp_player_options_popup");
    if (popup) {
        popup.parentNode.removeChild(popup);
    }
}


function playerUrl(vid, pid) {
    let url = `${location.protocol}//${location.hostname}/`;

    if (vid) {
        url = `${url}embed/${vid}?`;
        if (pid) {
            url = `${url}list=${pid}`;
        }
    }
    else if (pid) {
        url = `${url}embed?listType=playlist&list=${pid}`;
    }
    else {
        console.error("Нулевой pid и vid!");
    }

    url = `${url}&autoplay=1&fs=1&iv_load_policy=3&rel=1&version=3&enablejsapi=1`;

    return (url);
}


function adjust_playing(node) {
    const playing = document.getElementById("vpp_now_playing");
    playing?.parentNode?.removeChild(playing);
    if (node) {
        newElem("span", {"id": "vpp_now_playing"}, "Сейчас играет", node);
    }
}

function build_player() {
    //constants
    var next_choice = new Object();
    next_choice['plus'] = new Object();
    next_choice['minus'] = new Object();
    next_choice['left'] = new Object();
    next_choice['right'] = new Object();
    next_choice['up'] = new Object();
    next_choice['down'] = new Object();

    next_choice['plus']['xxsmall'] = 'xsmall';
    next_choice['plus']['xsmall'] = 'small';
    next_choice['plus']['small'] = 'medium';
    next_choice['plus']['medium'] = 'large';
    next_choice['plus']['large'] = 'xlarge';
    next_choice['plus']['xlarge'] = 'xxlarge';
    next_choice['plus']['xxlarge'] = 'xxlarge';

    next_choice['minus']['xxsmall'] = 'xxsmall';
    next_choice['minus']['xsmall'] = 'xxsmall';
    next_choice['minus']['small'] = 'xsmall';
    next_choice['minus']['medium'] = 'small';
    next_choice['minus']['large'] = 'medium';
    next_choice['minus']['xlarge'] = 'large';
    next_choice['minus']['xxlarge'] = 'xlarge';

    {
        for (var i = 0; i < 3; i++)
            for (var j = 0; j < 3; j++) {
                next_choice['left'][i.toString() + j.toString()] = i.toString() + (j - 1 >= 0 ? j - 1 : 0).toString();
                next_choice['right'][i.toString() + j.toString()] = i.toString() + (j + 1 <= 2 ? j + 1 : 2).toString();
                next_choice['up'][i.toString() + j.toString()] = (i - 1 >= 0 ? i - 1 : 0).toString() + j.toString();
                next_choice['down'][i.toString() + j.toString()] = (i + 1 <= 2 ? i + 1 : 2).toString() + j.toString();
            }
    }

    var new_size = get_pref("playerSize");
    var new_pos = get_pref("playerPos");
    var new_fit = get_pref("playerFit");

    var that = this;
    var frame_count = 0;

    //public
    this.playerShow = function (vid, pid, node) {
        const box = document.getElementById(BOX_ID);
        if (box?.style.visibility == "hidden") {
            new_size = get_pref("playerSize");
            new_pos = get_pref("playerPos");
            new_fit = get_pref("playerFit");
        }
        playerAdjust((new_fit ? "fit" : new_size), new_pos, "visible", vid, pid);
        adjust_playing(node);
    }

    this.playerClose = function () {
        playerAdjust(null, null, "hidden", null, null);
        close_player_options();
        adjust_playing();
    }


    //private
    function play_next(findprevious) {
        const playing = document.getElementById("vpp_now_playing");
        if (!playing) {
            return;
        }

        const myimg = innersearch(playing.parentNode, ".//img[@src or @data-thumb]").snapshotItem(0);
        if (myimg) {
            let pos = -2;
            let l = docsearch(`//*[(${basic_str})]${basic_str2}`);

            myimg.setAttribute("matchfind", "true");

            for (let i = 0; i < l.snapshotLength; i++) {
                if (l.snapshotItem(i).getAttribute("matchfind")) {
                    pos = i;
                    break;
                }
            }

            myimg.removeAttribute("matchfind");
            pos = (findprevious ? pos - 1 : pos + 1);

            if (pos >= 0) {
                const img = l.snapshotItem(pos);
                if (img) {
                    img.setAttribute("matchfindimg", true);
                    const target = docsearch(`//*[${basic_str} and (.//img[@matchfindimg])]`).snapshotItem(0);
                    if (target) {
                        that.playerShow(find_vid(img), find_plist(img), target);
                    }
                    img.removeAttribute("matchfindimg");
                }
            }
        }
    }

    function playerAdjust(size, pos, vis, vid, pid) {
        const box = document.getElementById(BOX_ID);
        if (vis != null) {
            box.style.visibility = vis;
            area.style.visibility = (get_pref("playerDim") ? vis : "hidden");

            let frame = document.getElementById("vpp_player_frame");
            if (frame && (vis == "hidden")) {
                frame.parentNode.removeChild(frame);
                frame = null;
            }

            if (vis == "visible") {
                const vidurl = playerUrl(vid, pid);
                const def = get_pref("playerDef");
                frame_count++;

                if (frame) {
                    frame.setAttribute("newvidurl", vidurl);
                    if (pid) frame.setAttribute("plist", pid);
                    else frame.removeAttribute("plist");
                    frame.setAttribute("quality", def);
                    frame.setAttribute("fid", frame_count.toString());

                    const event = document.createEvent("Event");
                    event.initEvent("loadnewvideo", true, true);
                    frame.dispatchEvent(event);
                }
                else {
                    frame = newElem("iframe", {"id": "vpp_player_frame", "type": "text/html", "frameborder": "0", "allowfullscreen": "true", "quality": def, "fid": frame_count.toString(), "src": vidurl}, null, document.getElementById(HOLDER_ID).firstElementChild);
                    frame.addEventListener("playend", function(){if (get_pref("playerNext")) play_next();});
                    player_script();
                }
            }
        }

        if (size != null) {
            box.setAttribute("player_size", size);
            holder.setAttribute("player_size", size);
        }

        if (pos != null) {
            box.setAttribute("player_pos", pos);
        }
    }

    function click_pos_size(e) {
        var kind = e.target.getAttribute("button_kind");
        if (!kind) return;

        if (new_fit && !(kind == "options" || kind == "prev" || kind == "next")) {
            set_pref("playerFit", false);
            new_fit = false;
            playerAdjust(new_size, new_pos);
            return;
        }

        switch (kind) {
            case 'plus':
            case 'minus':
                new_size = next_choice[kind][new_size];
                set_pref("playerSize", new_size);
                playerAdjust(new_size, new_pos);
                break;

            case 'fit':
                set_pref("playerFit", true);
                new_fit = true;
                playerAdjust('fit');
                break;

            case 'left':
            case 'right':
            case 'up':
            case 'down':
                new_pos = next_choice[kind][new_pos];
                set_pref("playerPos", new_pos);
                playerAdjust(new_size, new_pos);
                break;

            case 'options':
                player_options(document.getElementById(BOX_ID));
                break;

            case 'prev':
                play_next(true);
                break;

            case 'next':
                play_next();
                break;
        }
    }

    function new_button(kind, str, str_popup, parent) {
        newElem("span", {"class": "vpp_player_button", "title": str_popup, "button_kind": kind}, str, parent).onclick = click_pos_size;
    }

    //initialization;
    if (document.getElementById(AREA_ID)) {
        return;
    }

    const area = newElem("div", {"id": AREA_ID, "style": "visibility: hidden;"}, null, document.body);
    const area2 = newElem("div", {"id": "vpp_player_area2"}, null, area);
    const box = newElem("div", {"id": BOX_ID, "style": "visibility: hidden;"}, null, area2);
    const holder = newElem("div", {"id": HOLDER_ID}, null, box);
    newElem("div", {"id": "vpp_player_holder2"}, null, holder);
    area.onclick = function (e) { if (e.target.id == AREA_ID && get_pref("playerClose")) that.playerClose(); };

    const buttonArea = newElem("span", {"id": "vpp_player_button_area_top"}, null, box);
    new_button("plus", "\u002B", "Увеличить", buttonArea);
    new_button("minus", "\u2212", "Уменьшить", buttonArea);
    new_button("fit", "\u2610", "Растянуть", buttonArea);
    new_button('left', '\u25C4', 'Влево', buttonArea);
    new_button('right', '\u25BA', 'Вправо', buttonArea);
    new_button('up', '\u25B2', 'Вверх', buttonArea);
    new_button('down', '\u25BC', 'Вниз', buttonArea);
    new_button("options", "\u2630", "Настройки", buttonArea);

    const bottomArea = newElem("span", {"id": "vpp_player_button_area_next"}, null, box);
    new_button("prev", "\u140A\u140A", "Играть предыдущее на странице", bottomArea);
    new_button("next", "\u1405\u1405", "Играть следующее на странице", bottomArea);
    //new_button("loop", "\u21BB", "repeat video", bottomArea);

    newElem("span", {"id": "vpp_player_close_mark", "title": "Закрыть плеер"}, "\u2716", box).onclick = this.playerClose;
}

var player = null;
if (pref_playerEnable) {
    player = new build_player();
    injectScript(null, "https://www.youtube.com/iframe_api");
}

function ytpause() {
    const func = `(function(){
        const mainVid = document.getElementById("movie_player");
        const channelVid = document.getElementById("c4-player");
        if (mainVid && (mainVid.getPlayerState() == 1))
            mainVid.pauseVideo();
        if (channelVid && (channelVid.getPlayerState() == 1))
            channelVid.pauseVideo();
    })();`;
    injectScript(func);
}


//==================================================================
// float player

//quality is default for faster upload, and youtube player is paused
function float_script() {
    const func = `(function(){
        const fplayer = new YT.Player("vpp_float_frame");
        fplayer.addEventListener("onReady", function () {
            const mainVid = document.getElementById("movie_player");
            const channelVid = document.getElementById("c4-player");
            if (mainVid && (mainVid.getPlayerState() == 1))
                mainVid.pauseVideo();
            if (channelVid && (channelVid.getPlayerState() == 1))
                channelVid.pauseVideo();
            fplayer.setPlaybackQualityRange("default");
            });
    })();`;
    injectScript(func);
}

function float_open(e, check) {
    //check tests if same url frame is already open
    const v_id = e.target.firstElementChild?.href?.match(/(?<=v=)[a-zA-Z0-9_-]*/)?.[0];

    if (!v_id || document.getElementById("vpp_player_frame")) {
        return false;
    }

    if (!check) {
        float_delay_clear();
        adjust_playing(e.target);
    }

    const url = "https://www.youtube.com/embed/" + v_id + "?&autoplay=1&controls=1&iv_load_policy=3&rel=0&showinfo=1&version=3&enablejsapi=1";
    const frame = document.getElementById("vpp_float_frame");
    if (frame) {
        if (frame.src == url) {
            return check;
        }
        if (!check) {
            frame.parentNode.removeChild(frame);
        }
    }

    if (check) {
        return false;
    }

    const float_width = 480; //512;
    const float_height = 270; //288;
    let box = document.getElementById("vpp_float_box");
    if (!box) {
        box = newElem("div", {"id": "vpp_float_box", "style": `width: ${float_width}px; height: ${float_height}px;`}, null, document.body);
        box.onmouseenter = float_delay_clear;
        box.onmouseleave = float_close_delay;
    }

    const r = e.target.getBoundingClientRect();
    const w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
    const h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
    const hpad = Math.round(-r.width / 3); //horizontal offset
    const vpad = 0; //vertical offset

    //priority to right
    let left = r.right + hpad;
    if ((left + float_width > w) && ((r.left - float_width - hpad >= 0) || (r.left > w - r.right))) {
            left = r.left - float_width - hpad;
    }

    //priority to left
    //let left = r.left - float_width - pad;
    //if (left < 0)
    //    if ((r.right + float_width + pad <= w) || (w - r.right > r.left))
    //        left = r.right + pad;

    //priority to top
    let top = r.top - float_height - vpad;
    if (top < 0 && (r.bottom + float_height + vpad <= h || h - r.bottom > r.top)) {
        top = r.bottom + vpad;
    }

    left += (document.body.scrollLeft || document.documentElement.scrollLeft);
    top += (document.body.scrollTop || document.documentElement.scrollTop);

    box.style.left = left + "px";
    box.style.top = top + "px";

    newElem("iframe", {"id": "vpp_float_frame", "type": "text/html", "frameborder": "0", "src": url}, null, box);

    float_script();
    return true;
}

let float_open_timeout = null;
let float_close_timeout = null;

function float_close(e) {
    clearTimeout(float_close_timeout);
    const box = document.getElementById("vpp_float_box");

    //check if mouse was in the area
    if (box) {
        if (!e || (e.target != box)) {
            box.parentNode.removeChild(box);
            adjust_playing();
        }
        else { //player should not close if mouse is still inside
            const r = box.getBoundingClientRect();
            if ((e.clientX <= r.left + 1) || (e.clientX >= r.right - 1) ||
                (e.clientY <= r.top + 1) || (e.clientY >= r.bottom - 1)) {
                box.parentNode.removeChild(box);
                adjust_playing();
            }
        }
    }
}


function float_delay_clear() {
    clearTimeout(float_open_timeout);
    clearTimeout(float_close_timeout);
}

function float_reset() {
    float_delay_clear();
    float_close();
}

function float_open_delay(e) {
    if (float_open(e, true)) {
        float_delay_clear();
    }
    else {
        clearTimeout(float_open_timeout);
        const delay = get_pref("floatDelay") * 1000;
        float_open_timeout = setTimeout(float_open.bind(null, e, false), delay);
    }
}

function float_close_delay(e) {
    float_delay_clear();
    float_close_timeout = setTimeout(float_close.bind(null, e), 200);
}


//==================================================================
//meta data

function callback(json_txt, parent) {
    debug("callback", parent)

    const parseQuery = s =>
		[...new URLSearchParams(s).entries()].reduce(
			(acc, [k, v]) => ((acc[k] = v), acc),
			{}
		)

    const params = parseQuery(json_txt);
    debug("params", params);

    if (pref_defEnable) {
        let max_res = 0;
        let max_quality_label = "";

        try {
            let formats = [];
            const player_response = JSON.parse(params.player_response);
            debug("player_response", player_response);
            if (player_response.streamingData.hasOwnProperty("formats")) {
                formats = formats.concat(player_response.streamingData.formats);
            }
            if (player_response.streamingData.hasOwnProperty("adaptiveFormats")) {
                formats = formats.concat(player_response.streamingData.adaptiveFormats);
            }

            for (let i = 0; i < formats.length; i++) {
                const res = formats[i].width * formats[i].height;
                if (res > max_res) {
                    max_res = res;
                    max_quality_label = formats[i].qualityLabel;
                }
            }
        } catch(e) {debug(e);}

        if(max_res != 0) {
            parent.setAttribute("vpp_meta_def", "");
            const def_node = newElem("span", {"class": "vpp_meta_def_container", "reveal": "true", "title": max_quality_label}, null, parent);
            const def_txt =
                max_res >= (15360 * 8640) ? "16K" :
                max_res >= (7680 * 4320) ? "8K" :
                max_res >= (2880 * 2160) ? "4K" :
                max_res >= (960 * 1080) ? "1080p" :
                max_res >= (640 * 720) ? "720p" : "SD";
            newElem("span", {"class": "vpp_meta_def_hd HD"}, def_txt, def_node);
        }
    }

    if (pref_rateEnable) {
        let likes = 0;
        let dislikes = 0;
        let views = 0;
        let liked = false;
        let disliked = false;

        try {
            const contents = (JSON.parse(params.watch_next_response)).contents.twoColumnWatchNextResults.results.results.contents;
            debug("contents", contents);
            for (let i = 0; i < contents.length; i++) {
                if (contents[i].hasOwnProperty("videoPrimaryInfoRenderer")) {
                    const buttons = contents[i].videoPrimaryInfoRenderer.videoActions.menuRenderer.topLevelButtons;
                    for (let button of buttons) {
                        if (button.toggleButtonRenderer?.defaultIcon?.iconType === "LIKE") {
                            likes = parseInt(button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/[^0-9]/g, "")) || 0;
                            liked = button.toggleButtonRenderer.isToggled || false;
                        }
                        else if (button.toggleButtonRenderer?.defaultIcon?.iconType === "DISLIKE") {
                            dislikes = parseInt(button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/[^0-9]/g, "")) || 0;
                            disliked = button.toggleButtonRenderer.isToggled || false;
                        }
                    }
                    views = parseInt(contents[i].videoPrimaryInfoRenderer.viewCount.videoViewCountRenderer.viewCount.simpleText.replace(/[^0-9]/g, "")) || 0;
                    break;
                }
            }
        } catch(e) {debug(e);}

        if ((likes != 0) || (dislikes != 0)) {
            const perc = 100 - Math.round(dislikes * 100.0 / (likes + dislikes));
            // Color scale from 50 (red) to 100 (green)
            const scaled = Math.max(0,(perc-50)/50);
            const r = Math.min(0xC0,Math.round(2*0xC0*(1-scaled)));
            const g = Math.min(0xA0,Math.round(2*0xA0*(scaled)));
            const b = 0;
            const rgb = (r << 16) | (g << 8) | b;
            const hex = "#" + rgb.toString(16).padStart(6, "0");

            newElem("div",
                    { "class": "vpp_meta_rate",
                      "style": "background:" + hex + " !important;",
                      "title": perc + "% likes: +" + Number(likes).toLocaleString() + "  -" + Number(dislikes).toLocaleString() + "\n" + Number(views).toLocaleString() + " views"
                    },
                    (liked ? "\uD83D\uDC4D " : disliked ? "\uD83D\uDC4E " : "") + perc,
                    parent);
            parent.setAttribute("vpp_meta_rate", "");
        }
    }
}


function def_rate(v_id, parent) {
    debug("Getting info for " + v_id);
    const oReq = new XMLHttpRequest();
    oReq.addEventListener("load", function(){callback(this.responseText, parent)});
    oReq.open("GET", `https://www.youtube.com/get_video_info?video_id=${v_id}&el=detailpage`);
    oReq.send();
}


function find_vid(img) {
    return (filter(img.src, "vi/", "/&?#") || filter(img.src, "vi_webp/", "/&?#"));
}


function find_plist(img) {
    let plist = null;
    const anc = innersearch(img, "ancestor-or-self::*[contains(@href,'&list=') and (.//*[contains(@class,'yt-pl-sidebar-content') or contains(@class,'ytd-thumbnail-overlay-side-panel-renderer')])]").snapshotItem(0);
    if (anc) {
        plist = filter(anc.href, "list=", "/&?#");
        if (plist == "WL") {
            plist = null;
        }
    }
    return plist;
}


function play(parent) {
    const playArea = newElem("div", {"class": "vpp_play_button_container"}, null, parent);
    const playNode = newElem("a", {"class": "vpp_play_button", "href": "javascript:;", "target": "_self", "title": "Скриптовый плеер"}, null, playArea);

    const play_handle = function(e) {
        e.stopPropagation();
        float_reset();

        const parpar = e.target.parentNode.parentNode;
        if (innersearch(parpar, ".//*[@id='vpp_now_playing']").snapshotLength > 0) {
            player.playerClose();
        }
        else {
            const img = innersearch(parpar, `.${basic_str2}`).snapshotItem(0);
            if (img) {
                player.playerShow(find_vid(img), find_plist(img), parpar);
                if (get_pref("playerPause")) {
                    ytpause();
                }
            }
            else {
                console.error("play(parent): img not found");
            }
        }
    }

    playNode.onclick = play_handle;
}


//==================================================================
// Main
debug("***YouTube Video Preview and Ratings Keyless***");

//insert styles
insertStyle(style_basic, "vpp_style_basic");

if (pref_playerEnable) {//hide overlay of playlist
    insertStyle(".yt-pl-thumb-overlay, ytd-thumbnail-overlay-hover-text-renderer {display:none !important;}", "vpp_style_list_overlay");//OLD,NEW
}

(function insertMenuBtn() {
    const par = document.getElementById("masthead-container");
    if (par) {
        newElem("span", {"id": "vpp_pref_button", "title": "Настройки скрипт-плеера"}, null, par).onclick = pref_popup_open;
    }
    else {
        new MutationObserver(function(mutations, observer) {
            observer.disconnect();
            insertMenuBtn();
        }).observe(document.getElementById("masthead"), {childList: true});
    }
})();

function processThumbs(thumbs) {
    for (let thumb of thumbs) {
        const parent = thumb.parentNode;
        const v_id = thumb.href?.match(/(?<=v=)[a-zA-Z0-9_-]*/)?.[0];
        if (v_id) {
            if (pref_defEnable || pref_rateEnable) {
                def_rate(v_id, parent);
            }
            if (pref_floatEnable) {
                parent.onmouseenter = float_open_delay;
                parent.onmouseleave = float_close_delay;
            }
            if (pref_playerEnable) {
                play(parent);
            }
        }
    }
}
processThumbs(Array.from(document.body.querySelectorAll("#thumbnail")));

new MutationObserver(function(mutations, observer) {
    for (let mutation of mutations) {
        const thumbs = Array.from(mutation.addedNodes).filter(node => (node.nodeType === Node.ELEMENT_NODE && node.id === "thumbnail"));
        processThumbs(thumbs);
    }
}).observe(document.body, {childList: true, subtree: true});


У меня это скрипт на главной странице ломает загрузку превью при прокрутке страницы вниз, в каналах все нормально. Проверял и в Violentvonkey и в Temperrmonkey и на 78 и на 88.

Отредактировано _zt (07-05-2021 20:35:54)

Отсутствует

 
  • Форумы
  •  » Скрипты
  •  » YouTube Video Preview and Ratings Keyless и его частичная русификация

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]