您需要先安装一个扩展,例如 篡改猴、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();
- }
- })();