Прошло уже 10 лет с моего предыдущего разбора, не побоюсь этого слова, говнософта, как появилась очередная проблема, с которой мне захотелось разобраться и устранить её.

TLDR

С чего всё начиналось

Некоторое время назад случилось так, что телевизор стал единственным доступным способом вывода звука с моего ПК, и по сути, занял роль второго монитора. Ну а раз он теперь оккупирован компьютером, то встал вопрос — а как же мне смотреть на ютубе документалочки во время засыпания? Так как я слишком ленивый чтобы заранее составлять плейлисты, мне нужно было управление с телефона, через официальное приложение YouTube. Постоянно переключаться между HDMI и YouTube App на ТВ совсем не входило в мои интересы, я приступил к поиску решения, которое было найдено очень быстро — у платформы уже есть готовый интерфейс для ТВ в вебе, только была незадача: при открытии в браузере оно просто редиректит на главную.
Решение же этой проблемы не заставило себя долго ждать, для хрома имеется куча расширений, которые позволяют решить проблему с редиректом. В итоге мой выбор остановился на Youtube TV On PC, т.к. оно работало и имело относительно неплохой рейтинг. Но всё было не так красочно, как я мечтал...

Появление проблем

Далее по тексту под TV имеется ввиду веб версия YouTube TV

С первой проблемой я столкнулся спустя, наверное, неделю — приложение не могло подключиться к TV. А ещё сама загрузка TV могла занимать несколько минут. Но после этого всё успешно работало, пока снова не начиналось аналогичное поведение.
Я не сильно придавал этому значения, так как у меня используются другие расширения, в т.ч. и для блокировки рекламы, а в то время YouTube как раз активно замедлял работу сервиса для таких пользователей.

Со второй проблемой я столкнулся спустя несколько месяцев, когда вместо ПК использовал ноутбук — видео начало адски тормозить и хром начал выедать процессор. Связано это было с тем, что на YouTube у меня видео включались в максимальном качестве, а в большинстве случаев это 4k60. На эту проблему я тоже успешно забил, т.к. спустя несколько дней я опять вернулся к ПК, и проблем это не доставляло. До тех пор, пока у меня провайдер не решил то, что мне и 10Mbps хватит. Но и тогда проблема решилась разбирательством с провайдером.

Время шло, число сменилось, ни черта не изменилось

В общем, пострадал я таким образом около полугода, и мне это поднадоело. Потому что помимо ранее озвученных проблем, была ещё одна, самая бесючая — зависала не одна вкладка, зависал весь хром, даже диспетчер задач хрома зависал.
Желания разбираться в том, как же отлаживать ПО в Windows у меня не было никакого, но проблему нужно было как-то устранить. Решил через ProcessExplorer найти тот самый процесс, который подвисает и посмотреть в его стектрейс. Процесс был найден, на верхушке стека было что-то про ...crash.... Ну вот, думаю я, ты и попался. Если что-то где-то крэшится, значит что-то должно падать в логи. Включаю логгирование в хроме, жду неделю, ловлю опять зависание, и... И в логах абсолютно ничего нет. Мониторил ещё несколько месяцев в надежде поймать что-нибудь.

Последняя капля

В очередной раз всё подвисло на несколько минут, а т.к. я в это время был занят работой, меня это очень сильно разозлило, и я решил окончательно покончить с этим.
Думаю, а что будет если пристрелить процесс этого расширения? Shift+Esc, End process, зависание! Всё, подлец, попался!
Учитывая мою ленивость, я, наверное, должен был пойти и установить другое расширение, но нет, тут уже было дело принципа, нужно докопаться до истины. Поэтому, скачиваю CRX архив, наливаю кружку кваса и вперёд. А то мало ли, вдруг там майнер крутился всё это время, но оказалось всё гораздо проще и скучнее.

Исходники

Открыв расширение в архиваторе, моему взору предстал background.js следующего содержимого:

const userAgent = "Mozilla/5.0 (SMART-TV; LINUX; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) 69.0.3497.106.1/5.5 TV Safari/537.36";
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
	chrome.webRequest.onBeforeSendHeaders.addListener(function (details) {
		for (var i = 0; i < details.requestHeaders.length; ++i) {
			if (details.requestHeaders[i].name === "User-Agent") {
				details.requestHeaders[i].value = userAgent;
				break;
			}
		}
		return { requestHeaders: details.requestHeaders };
	}, { urls: ['*://www.youtube.com/tv*'] }, ['blocking', 'requestHeaders']);

	if (changeInfo.status == "complete" && tab.url.includes("youtube") && tab.url.includes("watch?v=")) {
		chrome.tabs.executeScript(tabId, {
			file: "/hd.js"
		});
	}
});

И тут я понимаю весь %%%ец происходящего, и откуда зависания, и откуда 4k60 везде где только можно. А теперь по порядку...

  1. Расширение добавляет хэндлер для всех событий всех табов! Да, у расширения как оказалось, в манифесте не указано ограничение по доменам, и в самом addListener не указан filter, который бы позволил отфильтровать ненужные события, как минимум по URL и типу события.

  2. Внутри хэндлера, который, я напомню, вызывается для всех табов и для всех типов событий, которых как минимум 12 штук, добавляется уже нужный обработчик webRequest.onBeforeSendHeaders, который хотя бы вызывается только со страницы www.youtube.com/tv.

  3. Проверяется что URL таба, от которого пришло событие содержит youtube и watch?v=, и выполняет скрипт, который и включает максимальное качество в плеере.

Если очень грубо посчитать, то мы имеем то, что при открытии браузера с 50 вкладками (не знаю как для вас, а для меня это нормальная ситуация), у нас добавляется примерно 200 хэндлеров для webRequest.onBeforeSendHeaders. Это если посчитать события смены url, favicon, заголовка страницы и статуса загрузки. Далее можно ещё добавить события, которые происходят во время пользования браузером, а как я выше говорил, зависания случались где-то спустя неделю после запуска. А ещё есть сайты, вроде VK, где при входящих сообщениях меняется favicon каждые несколько секунд.

hd.js не представляет особого интереса
var scriptTag = document.getElementById('ytTvHd');
if (scriptTag) {
	scriptTag.remove();
}
var script = document.createElement('script');
script.id = "ytTvHd";
script.type = 'text/javascript';
script.textContent = "var ytvPlayer = document.getElementById('movie_player') || document.querySelector('.html5-video-player');ytvPlayer.setPlaybackQualityRange('highres');" +
	"document.getElementsByTagName('body')[0].onkeydown = function(e) {if (e.keyCode == 428) {ytvPlayer.requestFullscreen();}};";
document.body.appendChild(script);

Выводы

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

const userAgent = "Mozilla/5.0 (SMART-TV; LINUX; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) 69.0.3497.106.1/5.5 TV Safari/537.36";

chrome.webRequest.onBeforeSendHeaders.addListener(function (details) {
	for (var i = 0; i < details.requestHeaders.length; ++i) {
		if (details.requestHeaders[i].name === "User-Agent") {
			details.requestHeaders[i].value = userAgent;
			break;
		}
	}
	return { requestHeaders: details.requestHeaders };
}, { urls: ['*://www.youtube.com/tv*'] }, ['blocking', 'requestHeaders']);

Вероятно, для решения моей проблемы подойдет любое другое расширение для смены HTTP-заголовков, но сколько ещё секретов они в себе могут таить...

Комментарии (6)


  1. ninJo
    03.05.2024 21:13
    +3

    Напиши в комментах автору расширения об этом, может исправят


    1. POPSuL Автор
      03.05.2024 21:13
      +3

      В начале года кто-то уже написал в отзывах про тормоза. Но я добавил ещё свой, со ссылочкой на эту статью, может быть автор прочитает хотя бы с переводчиком.


  1. sergeym69
    03.05.2024 21:13
    +2

    chrome.webRequest.onBeforeSendHeaders.addListener

    И должно вызываться только один раз в background, и после этого этот handler будет работать для всех вкладок, потому как работает он не для вкладок, а для всех сетевых вызовов


    1. POPSuL Автор
      03.05.2024 21:13
      +8

      Ну так в конечном итоге я именно так и переписал.

      В исходном виде сами хэндлеры добавлялись "бездумно", на кажый(!) "чих" любого(!) таба.


  1. A_L_I_E_N
    03.05.2024 21:13
    +4

    Ехал handler через handler,

    Видит handler: в handler - handler.

    Сунул handler в handler handler.

    Handler handler handler handler.

    Много handler-ов .. бывает! ))


  1. VADemon
    03.05.2024 21:13

    Интересно, с ChatGPT/Копилотами будет хуже или лучше? #lowcode #nocode

    В HTTP/2 хедеры вроде с маленькой буквы, не знаю, как в этом API. Маленькая оптимизация (хотя я не уверен о зоне действия Listener): раз уже используется closure, сохранять индекс найденного UA в прошлый раз и сначала проверять его, а не по всем проходить.