您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds exact upload time to youtube videos
// ==UserScript== // @name Youtube exact upload // @name:de YouTube exakter Hochladezeitpunkt // @description Adds exact upload time to youtube videos // @description:de Fügt YouTube-Videos den exakten Hochladezeitpunkt mit Uhrzeit hinzu // @require https://cdnjs.cloudflare.com/ajax/libs/luxon/3.5.0/luxon.min.js // @version 0.19 // @match https://www.youtube.com/* // @grant none // @namespace https://greasyfork.org/users/94906 // @license MIT // ==/UserScript== // luxon is for formatting and comparing dates and times (function () { "use strict"; console.log("YT EXACT UPLOAD LOADED"); //Pre-Define Variables to prevent warning of redaclaration of variables const YT_API_KEY = "YouTube API-Key"; let DATE_PATTERN, TIME_PATTERN, DATETIME_COMBINE_PATTERN, SCHEDULED_LIVESTREAM_START, SCHEDULED_PREMIERE_START, ONGOING_LIVESTREAM_START; let ONGOING_PREMIERE_START, ENDED_LIVESTREAM_START, ENDED_PREMIERE_START, DATETIME_UNTIL_PATTERN, SINCE, TODAY_AT; const AGE_RESTRICTED = " - FSK 18"; const SHOW_REFRESH = true; const REFRESH_TIMESTAMP = "⟳"; const SHOW_UNDERLINE_ON_TIMESTAMP = false; const BASE_URL = "https://www.googleapis.com/youtube/v3/videos?part=snippet,liveStreamingDetails,contentDetails,localizations,player,statistics,status&key=" + YT_API_KEY; luxon.Settings.defaultLocale = document.documentElement.lang; if (document.documentElement.lang.startsWith("de")) { DATE_PATTERN = "dd.MM.yyyy"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens TIME_PATTERN = "HH:mm:ss 'Uhr'"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens DATETIME_COMBINE_PATTERN = " 'um' "; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens SCHEDULED_LIVESTREAM_START = "Livestream geplant für: "; SCHEDULED_PREMIERE_START = "Premiere geplant für: "; ONGOING_LIVESTREAM_START = "Aktiver Livestream seit "; ONGOING_PREMIERE_START = "Aktive Premiere seit "; ENDED_LIVESTREAM_START = "Livestream von "; ENDED_PREMIERE_START = "Premiere von "; DATETIME_UNTIL_PATTERN = " bis "; SINCE = "Seit"; TODAY_AT = "Heute um "; } else if (document.documentElement.lang.startsWith("fr")) { DATE_PATTERN = "dd MMMM yyyy"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens TIME_PATTERN = "HH:mm:ss"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens DATETIME_COMBINE_PATTERN = " 'de' "; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens SCHEDULED_LIVESTREAM_START = "Direct planifié pour le "; SCHEDULED_PREMIERE_START = "Première planifiée pour le "; ONGOING_LIVESTREAM_START = "Direct en cours depuis "; ONGOING_PREMIERE_START = "Première en cours depuis "; ENDED_LIVESTREAM_START = "Direct diffusé le "; ENDED_PREMIERE_START = "Première diffusée le "; DATETIME_UNTIL_PATTERN = " à "; SINCE = "Depuis"; TODAY_AT = "Aujourd'hui à "; } else if (document.documentElement.lang.startsWith("it")) { DATE_PATTERN = "dd MMMM yyyy"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens TIME_PATTERN = "HH:mm:ss"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens DATETIME_COMBINE_PATTERN = " 'alle' "; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens SCHEDULED_LIVESTREAM_START = "Diretta pianificata per il: "; SCHEDULED_PREMIERE_START = "Premiere pianificata per il: "; ONGOING_LIVESTREAM_START = "Diretta attiva dalle "; ONGOING_PREMIERE_START = "Premiere attiva dalle "; ENDED_LIVESTREAM_START = "Diretta del "; ENDED_PREMIERE_START = " Premiere del "; DATETIME_UNTIL_PATTERN = " fino "; SINCE = "Dalle"; TODAY_AT = "Oggi alle "; } else { DATE_PATTERN = "dd.MM.yyyy"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens TIME_PATTERN = "HH:mm:ss"; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens DATETIME_COMBINE_PATTERN = " 'at' "; // https://moment.github.io/luxon/#/formatting?id=table-of-tokens SCHEDULED_LIVESTREAM_START = "Livestream scheduled for: "; SCHEDULED_PREMIERE_START = "Premiere scheduled for: "; ONGOING_LIVESTREAM_START = "Active Livestream since "; ONGOING_PREMIERE_START = "Active Premiere since "; ENDED_LIVESTREAM_START = "Livestream from "; ENDED_PREMIERE_START = "Premiere from "; DATETIME_UNTIL_PATTERN = " until "; SINCE = "Since"; TODAY_AT = "Today at "; } let interval = null; let changeCheckTimer = null; let currentVideoId = null; function getVideoId() { return new URLSearchParams(globalThis.location.search).get("v"); } function genUrl() { const videoId = getVideoId(); if (videoId != null) { return BASE_URL + "&id=" + videoId; } else { return ""; } } function sleep(milliseconds) { return new Promise((resolve) => setTimeout(resolve, milliseconds)); } function formatMilliseconds(milliseconds) { const dur = luxon.Duration.fromMillis(milliseconds).shiftTo( "hours", "minutes", "seconds", ); return [dur.hours, dur.minutes, dur.seconds].map((unit) => String(unit).padStart(2, "0") ).join(":"); } function updateOngoing(startTime) { if (interval) { clearInterval(interval); interval = null; } interval = setInterval(function () { const durationInMilliseconds = luxon.Interval.fromDateTimes( startTime, luxon.DateTime.now(), ).length("milliseconds"); const ongoingVideoDuration = document.getElementById( "ongoing-video-duration", ); ongoingVideoDuration.innerHTML = formatMilliseconds( durationInMilliseconds, ); if (ongoingVideoDuration.parentNode) { ongoingVideoDuration.parentNode.title = ongoingVideoDuration.parentNode.innerText; } }, 500); } async function updateLiveContent(premiere, livestream, data, dt) { let element = null; while (!element) { element = document.getElementById("primary-inner"); await sleep(200); } let durationInMilliseconds = null; let ongoing = false; let innerHTML = ""; if (!premiere && !livestream) { // normal video if (dt.hasSame(luxon.DateTime.now(), "day")) { // today innerHTML += `${TODAY_AT}${dt.toFormat(TIME_PATTERN)}`; } else { innerHTML += dt.toFormat( `${DATE_PATTERN}${DATETIME_COMBINE_PATTERN}${TIME_PATTERN}`, ); } } else { if (!data.items[0].liveStreamingDetails.actualStartTime) { // planned dt = luxon.DateTime.fromISO( data.items[0].liveStreamingDetails.scheduledStartTime, ); if (dt.hasSame(luxon.DateTime.now(), "day")) { // today if (livestream) { innerHTML += `${SCHEDULED_LIVESTREAM_START}${ dt.toFormat(TIME_PATTERN) }`; } else if (premiere) { innerHTML += `${SCHEDULED_PREMIERE_START}${ dt.toFormat(TIME_PATTERN) }`; } else { innerHTML += `${TODAY_AT}${dt.toFormat(TIME_PATTERN)}`; } } else { if (livestream) { innerHTML += `${SCHEDULED_LIVESTREAM_START}${ dt.toFormat( DATE_PATTERH + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } else if (premiere) { innerHTML += `${SCHEDULED_PREMIERE_START}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } else { innerHTML += `${TODAY_AT}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } } } else { // ongoing / ended const liveStreamingDetails = data.items[0].liveStreamingDetails; dt = luxon.DateTime.fromISO(liveStreamingDetails.actualStartTime); let endTime = null; if (liveStreamingDetails.actualEndTime) { endTime = luxon.DateTime.fromISO(liveStreamingDetails.actualEndTime); } if (endTime == null) { // ongoing ongoing = true; durationInMilliseconds = luxon.Interval.fromDateTimes( dt, luxon.DateTime.now(), ).length("milliseconds"); if (dt.hasSame(luxon.DateTime.now(), "day")) { // today if (livestream) { innerHTML += `${ONGOING_LIVESTREAM_START}${ dt.toFormat(TIME_PATTERN) } (<span id="ongoing-video-duration">${ formatMilliseconds(durationInMilliseconds) }</span>)`; } else if (premiere) { innerHTML += `${ONGOING_PREMIERE_START}${ dt.toFormat(TIME_PATTERN) } (<span id="ongoing-video-duration">${ formatMilliseconds(durationInMilliseconds) }</span>)`; } else { innerHTML += `${SINCE} ${ dt.toFormat(TIME_PATTERN) } (<span id="ongoing-video-duration">${ formatMilliseconds(durationInMilliseconds) }</span>)`; } } else { if (livestream) { innerHTML += `${ONGOING_LIVESTREAM_START}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) } (<span id="ongoing-video-duration">${ formatMilliseconds(durationInMilliseconds) }</span>)`; } else if (premiere) { innerHTML += `${ONGOING_PREMIERE_START}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) } (<span id="ongoing-video-duration">${ formatMilliseconds(durationInMilliseconds) }</span>)`; } else { innerHTML += `${SINCE} ${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) } (<span id="ongoing-video-duration">${ formatMilliseconds(durationInMilliseconds) }</span>)`; } } } else { // ended if (dt.hasSame(endTime, "day")) { // start date and end date are the same if (dt.hasSame(luxon.DateTime.now(), "day")) { // today if (livestream) { innerHTML += `${ENDED_LIVESTREAM_START}${ dt.toFormat(TIME_PATTERN) }${DATETIME_UNTIL_PATTERN}${endTime.toFormat(TIME_PATTERN)}`; } else if (premiere) { innerHTML += `${ENDED_PREMIERE_START}${ dt.toFormat(TIME_PATTERN) }${DATETIME_UNTIL_PATTERN}${endTime.toFormat(TIME_PATTERN)}`; } else { innerHTML += `${TODAY_AT}${dt.toFormat(TIME_PATTERN)}`; } } else { if (livestream) { innerHTML += `${ENDED_LIVESTREAM_START}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }${DATETIME_UNTIL_PATTERN}${endTime.toFormat(TIME_PATTERN)}`; } else if (premiere) { innerHTML += `${ENDED_PREMIERE_START}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }${DATETIME_UNTIL_PATTERN}${endTime.toFormat(TIME_PATTERN)}`; } else { innerHTML += `${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }${DATETIME_UNTIL_PATTERN}${endTime.toFormat(TIME_PATTERN)}`; } } } else { if (dt.hasSame(luxon.DateTime.now(), "day")) { // today if (livestream) { innerHTML += `${ENDED_LIVESTREAM_START}${ dt.toFormat(TIME_PATTERN) }${DATETIME_UNTIL_PATTERN}${ endTime.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } else if (premiere) { innerHTML += `${ENDED_PREMIERE_START}${ dt.toFormat(TIME_PATTERN) }${DATETIME_UNTIL_PATTERN}${ endTime.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } else { innerHTML += `${TODAY_AT}${ dt.toFormat(TIME_PATTERN) }${DATETIME_UNTIL_PATTERN}${ endTime.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } } else { if (livestream) { innerHTML += `${ENDED_LIVESTREAM_START}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }${DATETIME_UNTIL_PATTERN}${ endTime.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } else if (premiere) { innerHTML += `${ENDED_PREMIERE_START}${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }${DATETIME_UNTIL_PATTERN}${ endTime.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } else { innerHTML += `${ dt.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }${DATETIME_UNTIL_PATTERN}${ endTime.toFormat( DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN, ) }`; } } } } } } const contentRating = data.items[0].contentDetails.contentRating; if (contentRating.ytRating && contentRating.ytRating == "ytAgeRestricted") { innerHTML += AGE_RESTRICTED; } if (SHOW_REFRESH) { if (SHOW_UNDERLINE_ON_TIMESTAMP) { innerHTML += ' <span id="dot" class="style-scope ytd-video-primary-info-renderer"></span> <span style="color: var(--yt-spec-text-secondary); text-decoration: underline var(--yt-spec-text-secondary); cursor: pointer;" onclick="document.dispatchEvent(new Event(\'refresh-clicked\'));">' + REFRESH_TIMESTAMP + "</span>"; } else { innerHTML += ' <span id="dot" class="style-scope ytd-video-primary-info-renderer"></span> <span style="color: var(--yt-spec-text-secondary); cursor: pointer;" onclick="document.dispatchEvent(new Event(\'refresh-clicked\'));">' + REFRESH_TIMESTAMP + "</span>"; } } if (ongoing) updateOngoing(dt); const primaryInner = document.getElementById("primary-inner"); let dateTimeValueElem = document.getElementById("exact-date-time"); if (!dateTimeValueElem) { dateTimeValueElem = document.createElement("span"); dateTimeValueElem.id = "exact-date-time"; primaryInner.insertBefore(dateTimeValueElem, primaryInner.firstChild); } dateTimeValueElem.style.color = "white"; dateTimeValueElem.style.position = "absolute"; dateTimeValueElem.style.zIndex = "999"; dateTimeValueElem.innerHTML = innerHTML; dateTimeValueElem.title = dateTimeValueElem.innerText; return ongoing; } function getExactUploadDate(forceRefresh = false) { let abort = false; const processEvent = async () => { await sleep(500); const videoId = getVideoId(); if (videoId != null) { if (videoId == currentVideoId) { abort = true; } else { currentVideoId = videoId; } } if (forceRefresh) abort = false; if ((YT_API_KEY != "" || typeof YT_API_KEY != "undefined") && !abort) { const url = genUrl(); if (url != "") { try { const data = await fetch(url).then((response) => response.json()); if (data.pageInfo.totalResults > 0) { const addTime = async () => { const dt = luxon.DateTime.fromISO( data.items[0].snippet.publishedAt, ); console.log(dt); const payload = { context: { client: { clientName: "WEB", clientVersion: "2.20210614.06.00", originalUrl: globalThis.location.href, platform: "DESKTOP", clientFormFactor: "UNKNOWN_FORM_FACTOR", mainAppWebInfo: { graftUrl: "/watch?v=" + currentVideoId, webDisplayMode: "WEB_DISPLAY_MODE_BROWSER", isWebNativeShareAvailable: false, }, }, user: { lockedSafetyMode: false, }, request: { useSsl: true, }, }, videoId: currentVideoId, racyCheckOk: false, contentCheckOk: false, }; const video_info = await fetch( "https://www.youtube.com/youtubei/v1/player?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", /*InnerTube-API-Key*/ { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }, ).then((response) => response.json()); try { if (interval) { clearInterval(interval); interval = null; } if (changeCheckTimer) { clearInterval(changeCheckTimer); changeCheckTimer = null; } try { const premiere = !!(video_info && !video_info.videoDetails.isLiveContent) && !!data.items[0].liveStreamingDetails; const livestream = !!(video_info && video_info.videoDetails.isLiveContent); updateLiveContent(premiere, livestream, data, dt); } catch (ex) { console.error(ex); } } catch (error) { console.error( "YOUTUBE EXACT UPLOAD ERROR: " + error, "\nget_video_info doesn't seem to work", ); } }; addTime(); } } catch (error) { console.error( "YOUTUBE EXACT UPLOAD ERROR: " + error, "\nINVALID API KEY?", ); } } } else { if (!abort) { console.error("YOUTUBE EXACT UPLOAD ERROR: Undefined api key"); } } }; processEvent(); } function refreshEventListener() { getExactUploadDate(true); } document.addEventListener("refresh-clicked", refreshEventListener); function main() { const ytdPlayer = document.getElementById("ytd-player"); if (!ytdPlayer) { setTimeout(() => main(), 500); } else { ytdPlayer.addEventListener( "yt-player-updated", () => getExactUploadDate(), ); } } main(); if (getVideoId() != null) { getExactUploadDate(); } })();