您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
YouTube Helper API.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/549881/1663198/YouTube%20Helper%20API.js
// ==UserScript== // @name YouTube Helper API // @author ElectroKnight22 // @namespace electroknight22_helper_api_namespace // @version 0.0.2 // @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 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, }; return publicApi; })();