Ezt a szkriptet nem ajánlott közvetlenül telepíteni. Ez egy könyvtár más szkriptek számára, amik tartalmazzák a // @require https://update.greasyfork.org/scripts/549881/1663214/YouTube%20Helper%20API.js
hivatkozást.
// ==UserScript==
// @name YouTube Helper API
// @author ElectroKnight22
// @namespace electroknight22_helper_api_namespace
// @version 0.0.3
// @license MIT
// @description YouTube Helper API.
// ==/UserScript==
/*jshint esversion: 11 */
window.youtubeHelperApi = (function () {
'use strict';
const player = {
container: null,
api: null,
videoElement: null,
isFullscreen: false,
isTheater: false,
};
const video = {
id: '',
title: '',
channel: '',
channelId: '',
rawDescription: '',
rawUploadDate: '',
rawPublishDate: '',
uploadDate: null,
publishDate: null,
lengthSeconds: 0,
viewCount: 0,
likeCount: 0,
isLive: false,
isFamilySafe: false,
thumbnails: [],
playingLanguage: null,
originalLanguage: null,
};
const chat = {
container: null,
iFrame: null,
isCollapsed: false,
};
const page = {
isIframe: window.top !== window.self,
isMobile: window.location.hostname === 'm.youtube.com',
type: 'unknown',
};
let detectedAds = false;
function fallbackGetPlayerApi() {
updatePageType();
if (page.type === 'shorts') return document.querySelector('#shorts-player');
if (page.type === 'watch') return document.querySelector('#movie_player');
return document.querySelector('.inline-preview-player');
}
function getOptimalResolution(targetResolutionString, usePremium = true) {
const QUALITIES = {
highres: { p: 4320, label: '8K' },
hd2160: { p: 2160, label: '4K' },
hd1440: { p: 1440, label: '1440p' },
hd1080: { p: 1080, label: '1080p' },
hd720: { p: 720, label: '720p' },
large: { p: 480, label: '480p' },
medium: { p: 360, label: '360p' },
small: { p: 240, label: '240p' },
tiny: { p: 144, label: '144p' },
};
const PREMIUM_INDICATOR = 'Premium';
try {
if (!targetResolutionString || !QUALITIES[targetResolutionString])
throw new Error(`Invalid target resolution: ${targetResolutionString}`);
const videoQualityData = player.api.getAvailableQualityData();
const availableQualities = [...new Set(videoQualityData.map((q) => q.quality))];
const targetValue = QUALITIES[targetResolutionString].p;
const bestQualityString = availableQualities
.filter((q) => QUALITIES[q] && QUALITIES[q].p <= targetValue)
.sort((a, b) => QUALITIES[b].p - QUALITIES[a].p)[0];
if (!bestQualityString) return null;
let normalCandidate = null,
premiumCandidate = null;
for (const quality of videoQualityData) {
if (quality.quality === bestQualityString && quality.isPlayable) {
if (usePremium && quality.qualityLabel?.trim().endsWith(PREMIUM_INDICATOR)) premiumCandidate = quality;
else normalCandidate = quality;
}
}
return premiumCandidate || normalCandidate;
} catch (error) {
console.error('Error when resolving optimal quality:', error);
return null;
}
}
function setPlaybackResolution(targetResolution, ignoreAvailable = false, usePremium = true) {
try {
if (!player.api?.getAvailableQualityData) return;
if (!usePremium && ignoreAvailable) {
player.api.setPlaybackQualityRange(targetResolution);
} else {
const optimalQuality = getOptimalResolution(targetResolution, usePremium);
if (optimalQuality)
player.api.setPlaybackQualityRange(optimalQuality.quality, optimalQuality.quality, usePremium ? optimalQuality.formatId : null);
}
} catch (error) {
console.error('Error when setting resolution:', error);
}
}
function dispatchHelperApiReadyEvent() {
if (!player.api) return;
// Pass the youtube player to consumer scripts with a custom event.
const event = new CustomEvent('yt-api-helper-api-ready', { detail: Object.freeze({ ...player }) });
document.dispatchEvent(event);
}
function updateVideoLanguage() {
const getAudioTrackId = (track) => Object.values(track ?? {}).find((p) => p?.id)?.id ?? null;
const availableTracks = player.api.getAvailableAudioTracks();
if (availableTracks.length === 0) return;
const renderer = player.getPlayerResponse()?.captions?.playerCaptionsTracklistRenderer;
const originalAudioId = renderer?.audioTracks?.[renderer?.defaultAudioTrackIndex]?.audioTrackId;
const playingAudioTrack = player.getAudioTrack();
const originalAudioTrack = availableTracks.find((track) => getAudioTrackId(track) === originalAudioId);
video.playingLanguage = playingAudioTrack;
video.originalLanguage = originalAudioTrack;
}
function updatePlayerState(event) {
const useFallback = !event?.target?.player_;
player.container = event?.target;
player.api = event?.target?.player_;
if (useFallback) {
player.api = fallbackGetPlayerApi();
player.container = player.api.parentElement;
}
player.videoElement = player.container.querySelector('video');
if (!player.api) return;
const playerResponseObject = player.api.getPlayerResponse();
video.id = playerResponseObject.videoDetails?.videoId;
video.title = playerResponseObject.videoDetails?.title;
video.channel = playerResponseObject.videoDetails?.author;
video.channelId = playerResponseObject.videoDetails?.channelId;
video.rawDescription = playerResponseObject.videoDetails?.shortDescription;
video.rawUploadDate = playerResponseObject.microformat?.playerMicroformatRenderer?.uploadDate;
video.rawPublishDate = playerResponseObject.microformat?.playerMicroformatRenderer?.publishDate;
video.uploadDate = video.rawUploadDate ? new Date(video.rawUploadDate) : null;
video.publishDate = video.rawPublishDate ?new Date(video.rawPublishDate) : null;
video.lengthSeconds = parseInt(playerResponseObject.videoDetails?.lengthSeconds ?? '0', 10);
video.viewCount = parseInt(playerResponseObject.videoDetails?.viewCount ?? '0', 10);
video.likeCount = parseInt(playerResponseObject.microformat?.playerMicroformatRenderer?.likeCount ?? '0', 10);
video.isLive = playerResponseObject.videoDetails?.isLiveContent;
video.isFamilySafe = playerResponseObject.microformat?.playerMicroformatRenderer?.isFamilySafe;
video.thumbnails = playerResponseObject.microformat?.playerMicroformatRenderer?.thumbnail?.thumbnails;
updateVideoLanguage();
checkAdPresense();
dispatchHelperApiReadyEvent(); // Dispatch an event so consumer scripts can react.
}
function updateFullscreenState() {
player.isFullscreen = !!document.fullscreenElement;
}
function updateTheaterState(event) {
player.isTheater = !!event?.detail?.enabled;
}
function updateChatState(event) {
chat.iFrame = event.target ?? document.querySelector('ytd-watch-flexy');
chat.container = chat.iFrame?.parentElement ?? document.querySelector('#chat-container');
chat.isCollapsed = event.detail ?? true;
}
function updatePageType() {
const knownPagePathnames = {
homepage: '/',
profile: '/@',
watch: '/watch',
shorts: '/shorts',
live: '/live',
results: '/results',
chat: '/live_chat',
};
page.type =
knownPagePathnames[Object.keys(knownPagePathnames).find((key) => window.location.pathname.startsWith(knownPagePathnames[key]))];
}
function checkIsIframe() {
if (page.isIframe) {
document.dispatchEvent(new Event('yt-helper-api-detected-iframe'));
}
}
function checkAdPresense() {
try {
const progressState = player.api.getProgressState();
const reportedContentDuration = progressState.duration;
const realContentDuration = player.api.getDuration() ?? -1;
const durationMismatch = Math.trunc(realContentDuration) !== Math.trunc(reportedContentDuration);
const hasAds = durationMismatch;
if (hasAds) document.dispatchEvent(new CustomEvent('yt-helper-api-ad-detected'));
if (hasAds !== detectedAds)
document.dispatchEvent(
new CustomEvent('yt-helper-api-ad-state-changed', { detail: Object.freeze({ adState: hasAds }) }),
);
detectedAds = hasAds;
return detectedAds;
} catch (error) {
console.error('Error during ad check:', error);
return false;
}
}
function addPlayerStateListeners() {
const PLAYER_UPDATE_EVENT = page.isMobile ? 'state-navigateend' : 'yt-player-updated';
document.addEventListener(PLAYER_UPDATE_EVENT, updatePlayerState);
document.addEventListener('fullscreenchange', updateFullscreenState);
document.addEventListener('yt-set-theater-mode-enabled', updateTheaterState);
}
function addChatStateListeners() {
document.addEventListener('yt-chat-collapsed-changed', updateChatState);
}
function addNavigationListeners() {
document.addEventListener('yt-navigate-finish', updatePageType);
}
function initialize() {
checkIsIframe();
updatePlayerState();
addNavigationListeners();
addPlayerStateListeners();
addChatStateListeners();
}
initialize();
const publicApi = {
getPlayer: () => ({ ...player }),
getVideo: () => ({ ...video }),
getChat: () => ({ ...chat }),
getYoutubePage: () => ({ ...page }),
getAdState: () => detectedAds,
checkAdPresense: checkAdPresense,
getOptimalResolution: getOptimalResolution,
setPlaybackResolution: setPlaybackResolution,
};
return publicApi;
})();