Youtube exact upload

Adds exact upload time to youtube videos

当前为 2022-09-08 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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://cdn.jsdelivr.net/npm/[email protected]/HackTimer.min.js
// @require        https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment-with-locales.min.js
// @version        0.15
// @match          https://www.youtube.com/*
// @grant          none
// @namespace      https://greasyfork.org/users/94906
// @license        MIT
// ==/UserScript==

// HackTimer is for making setInterval work in background tabs
// moment is for formatting and comparing dates and times

(function() {
  'use strict';
  console.log("YT EXACT UPLOAD LOADED");
  var YT_API_KEY = "YouTube API-Key";
  var AGE_RESTRICTED = " - FSK 18";
  var SHOW_REFRESH = true;
  var REFRESH_TIMESTAMP = "⟳";
  var SHOW_UNDERLINE_ON_TIMESTAMP = false;
  var BASE_URL = "https://www.googleapis.com/youtube/v3/videos?part=snippet,liveStreamingDetails,contentDetails,localizations,player,statistics,status&key=" + YT_API_KEY;
  var lang = document.getElementsByTagName("html")[0].getAttribute("lang");
  moment.locale(lang);
  if (lang.startsWith("de")) {
    var DATE_PATTERN = "DD.MM.YYYY"; // https://momentjs.com/docs/#/parsing/string-format/
    var TIME_PATTERN = "HH:mm:ss [Uhr]"; // https://momentjs.com/docs/#/parsing/string-format/
    var DATETIME_COMBINE_PATTERN = " [um] "; // https://momentjs.com/docs/#/parsing/string-format/
    var SCHEDULED_LIVESTREAM_START = "Livestream geplant für: ";
    var SCHEDULED_PREMIERE_START = "Premiere geplant für: ";
    var ONGOING_LIVESTREAM_START = "Aktiver Livestream seit ";
    var ONGOING_PREMIERE_START = "Aktive Premiere seit ";
    var ENDED_LIVESTREAM_START = "Livestream von ";
    var ENDED_PREMIERE_START = "Premiere von ";
    var DATETIME_UNTIL_PATTERN = " bis ";
    var SINCE = "Seit";
    var TODAY_AT = "Heute um ";
  } else if (lang.startsWith("fr")) {
    var DATE_PATTERN = "DD MMMM. YYYY"; // https://momentjs.com/docs/#/parsing/string-format/
    var TIME_PATTERN = "HH:mm:ss"; // https://momentjs.com/docs/#/parsing/string-format/
    var DATETIME_COMBINE_PATTERN = " [à] "; // https://momentjs.com/docs/#/parsing/string-format/
    var SCHEDULED_LIVESTREAM_START = "Livestream planifié pour le ";
    var SCHEDULED_PREMIERE_START = "Premiere planifié pour le ";
    var ONGOING_LIVESTREAM_START = "Diffusion lancée il y a "; // TODO: for a "Active Livestream since" that started in 2019, 6 months, 2 weeks (often webcams) -> Diffusion lancée le 18 déc. 2019; for a "Active Livestream since" that started now -> Diffusion lancée il y a 2 minutes
    var ONGOING_PREMIERE_START = "Diffusion lancée il y a "; // TODO: for a "Active Livestream since" that started in 2019, 6 months, 2 weeks (often webcams) -> Diffusion lancée le 18 déc. 2019; for a "Active Livestream since" that started now -> Diffusion lancée il y a 2 minutes
    var ENDED_LIVESTREAM_START = "Diffusé en direct il y a "; // TODO: "Livestream from "; for a "Livestream from " since one day or more -> Diffusée en direct le 3 août 2022; "Livestream from "; for a "Livestream from " since less than one day -> Diffusé en direct il y a 76 minutes and Diffusé en direct il y a 14 heures
    var ENDED_PREMIERE_START = " Sortie le ";
    var DATETIME_UNTIL_PATTERN = " until ";
    var SINCE = "Diffusée en direct le";
    var TODAY_AT = "Aujourd'hui à ";
  } else if (lang.startsWith("it")) {
    var DATE_PATTERN = "DD MMMM YYYY"; // https://momentjs.com/docs/#/parsing/string-format/
    var TIME_PATTERN = "HH:mm:ss"; // https://momentjs.com/docs/#/parsing/string-format/
    var DATETIME_COMBINE_PATTERN = " [alle] "; // https://momentjs.com/docs/#/parsing/string-format/
    var SCHEDULED_LIVESTREAM_START = "Diretta pianificata per il: ";
    var SCHEDULED_PREMIERE_START = "Premiere pianificata per il: ";
    var ONGOING_LIVESTREAM_START = "Diretta attiva dalle ";
    var ONGOING_PREMIERE_START = "Premiere attiva dalle ";
    var ENDED_LIVESTREAM_START = "Diretta del ";
    var ENDED_PREMIERE_START = " Premiere del ";
    var DATETIME_UNTIL_PATTERN = " fino ";
    var SINCE = "Dalle";
    var TODAY_AT = "Oggi alle ";
  } else {
    var DATE_PATTERN = "DD.MM.YYYY"; // https://momentjs.com/docs/#/parsing/string-format/
    var TIME_PATTERN = "HH:mm:ss"; // https://momentjs.com/docs/#/parsing/string-format/
    var DATETIME_COMBINE_PATTERN = " [at] "; // https://momentjs.com/docs/#/parsing/string-format/
    var SCHEDULED_LIVESTREAM_START = "Livestream scheduled for: ";
    var SCHEDULED_PREMIERE_START = "Premiere scheduled for: ";
    var ONGOING_LIVESTREAM_START = "Active Livestream since ";
    var ONGOING_PREMIERE_START = "Active Premiere since ";
    var ENDED_LIVESTREAM_START = "Livestream from ";
    var ENDED_PREMIERE_START = "Premiere from ";
    var DATETIME_UNTIL_PATTERN = " until ";
    var SINCE = "Since";
    var TODAY_AT = "Today at ";
  }
  var interval = null;
  var changeCheckTimer = null;
  var currentVideoId = null;
  function genUrl(){
    const urlParams = new URLSearchParams(window.location.search);

    if(urlParams.get("v") != null){
      return BASE_URL + "&id=" + urlParams.get("v");
    }else {
      return "";
    }
  }
  function isUndefinedOrNull(obj) {
    return obj == undefined || obj == null;
  }
  function sleep(milliseconds) {
    return new Promise(resolve => setTimeout(resolve, milliseconds));
  }
  function formatMilliseconds(milliseconds, joinString, showDays, showHours, showMinutes, showSeconds, showMilliseconds, pad, hideDaysOnNull) {
    let result = '';
    let days = Math.floor(milliseconds / (1000 * 60 * 60 * 24));
    let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);
    let minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
    let seconds = Math.floor((milliseconds / 1000) % 60);
    milliseconds = milliseconds % 1000;
    if (showDays) {
      if (days < 1 && hideDaysOnNull) {
      } else {
        if (result != '')
          result += joinString;
        if (pad) {
          if (days < 10)
            result += '0' + days;
          else
            result += days;
        } else
          result += days;
      }
    }
    if (showHours) {
      if (result != '')
      	result += joinString;
      if (pad)
      	result += ('0' + hours).slice(-2);
      else
        result += hours;
    }
    if (showMinutes) {
      if (result != '')
      	result += joinString;
      if (pad)
      	result += ('0' + minutes).slice(-2);
      else
        result += minutes;
    }
    if (showSeconds) {
      if (result != '')
      	result += joinString;
      if (pad)
      	result += ('0' + seconds).slice(-2);
      else
        result += seconds;
    }
    if (showMilliseconds) {
      if (result != '')
      	result += joinString;
      if (pad)
      	result += ('00' + milliseconds).slice(-3);
      else
        result += milliseconds;
    }
    return result;
  }
  function updateOngoing(durationInMilliseconds) {
    if (!isUndefinedOrNull(interval)) {
      clearInterval(interval);
      interval = null;
    }
    let duration = durationInMilliseconds;
    interval = setInterval(function() {
      duration += 500;
      document.getElementById("ongoing-video-duration").innerHTML = formatMilliseconds(duration, ':', true, true, true, true, false, true, true);
    }, 500);
  }
 	async function updateLiveContent(premiere, livestream, data, mom) {
    var element = null;
    while (isUndefinedOrNull(element)) {
      element = document.getElementById("primary-inner");
      await sleep(200);
    }
    var durationInMilliseconds = null;
    var ongoing = false;
    var innerHTML = "";
    if (!premiere && !livestream) { // normal video
      if (mom.isSame(moment(), 'day')) // today
        innerHTML += TODAY_AT + mom.format(TIME_PATTERN);
      else
        innerHTML += mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
    } else {
      if (isUndefinedOrNull(data.items[0].liveStreamingDetails.actualStartTime)) { // planned
        mom = moment(data.items[0].liveStreamingDetails.scheduledStartTime);
        if (mom.isSame(moment(), 'day')) { // today
          if (livestream)
          	innerHTML += SCHEDULED_LIVESTREAM_START + mom.format(TIME_PATTERN);
          else if (premiere)
            innerHTML += SCHEDULED_PREMIERE_START + mom.format(TIME_PATTERN);
          else
            innerHTML += TODAY_AT + mom.format(TIME_PATTERN);
        } else {
          if (livestream)
          	innerHTML += SCHEDULED_LIVESTREAM_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
          else if (premiere)
            innerHTML += SCHEDULED_PREMIERE_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
          else
            innerHTML += TODAY_AT + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
        }
      } else { // ongoing / ended
        mom = moment(data.items[0].liveStreamingDetails.actualStartTime);
        var endTime = null;
        if (!isUndefinedOrNull(data.items[0].liveStreamingDetails.actualEndTime))
          endTime = moment(data.items[0].liveStreamingDetails.actualEndTime);
        if (endTime == null) { // ongoing
          ongoing = true;
          durationInMilliseconds = moment.duration(moment().diff(mom)).asMilliseconds();
          if (mom.isSame(moment(), 'day')) { // today
            if (livestream)
            	innerHTML += ONGOING_LIVESTREAM_START + mom.format(TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
            else if (premiere)
            	innerHTML += ONGOING_PREMIERE_START + mom.format(TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
            else
              innerHTML += SINCE + " " + mom.format(TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
          } else {
            if (livestream)
            	innerHTML += ONGOING_LIVESTREAM_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
            else if (premiere)
              innerHTML += ONGOING_PREMIERE_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
            else
              innerHTML += SINCE + " " + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
          }
        } else { // ended
          if (mom.isSame(endTime, 'day')) { // start date and end date are the same
            if (mom.isSame(moment(), 'day')) { // today
              if (livestream)
                innerHTML += ENDED_LIVESTREAM_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
              else if (premiere)
                innerHTML += ENDED_PREMIERE_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
              else
                innerHTML += TODAY_AT + mom.format(TIME_PATTERN);
            } else {
              if (livestream)
                innerHTML += ENDED_LIVESTREAM_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
              else if (premiere)
                innerHTML += ENDED_PREMIERE_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
              else
                innerHTML += mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
            }
          } else {
            if (mom.isSame(moment(), 'day')) { // today
              if (livestream)
                innerHTML += ENDED_LIVESTREAM_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
              else if (premiere)
                innerHTML += ENDED_PREMIERE_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
              else
                innerHTML += TODAY_AT + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
            } else {
              if (livestream)
                innerHTML += ENDED_LIVESTREAM_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
              else if (premiere)
                innerHTML += ENDED_PREMIERE_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
              else
                innerHTML += mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
            }
          }
        }
      }
    }
    var contentRating = data.items[0].contentDetails.contentRating;
    if (!isUndefinedOrNull(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(durationInMilliseconds);
    let dislikeButtonText = document.getElementsByTagName('yt-formatted-string');
    for (let i = 0; i < dislikeButtonText.length; i++) {
      if (dislikeButtonText[i].innerHTML == 'Mag ich nicht' || dislikeButtonText[i].innerHTML.toLowerCase() == 'dislike') {
        if (dislikeButtonText[i].hasOwnProperty('remove'))
        	dislikeButtonText[i].remove();
        else
          dislikeButtonText[i].parentNode && dislikeButtonText[i].parentNode.removeChild(dislikeButtonText[i]); // Polyfill
        break;
      }
    }
    let 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.innerHTML = innerHTML;
    return ongoing;
  }
  function getExactUploadDate(forceRefresh = false) {
    var abort = false;
    const processEvent = async () => {
      await sleep(500);
    	const urlParams = new URLSearchParams(window.location.search);
      if (urlParams.get("v") != null){
        let videoId = urlParams.get("v");
        if (videoId == currentVideoId) {
        	abort = true;
        } else {
        	currentVideoId = videoId;
        }
      }
      if (forceRefresh)
        abort = false;
      if ((YT_API_KEY != "" || typeof YT_API_KEY != "undefined") && !abort) {
        var url = genUrl();
        if (url != "") {
          fetch(url).then(function(response) {
            return response.json();
          }).then(function(data) {
            if (data.pageInfo.totalResults > 0) {
              const addTime = async () => {
                var mom = moment(data.items[0].snippet.publishedAt);
                console.log(mom);
                let payload = {
                  context: {
                    client: {
                      clientName: 'WEB',
                      clientVersion: '2.20210614.06.00',
                      originalUrl: window.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
                };
                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(function(response) {
                  return response.text();
                }).then(function(video_info) {
                  if (!isUndefinedOrNull(interval)) {
                    clearInterval(interval);
                    interval = null;
                  }
                  if (!isUndefinedOrNull(changeCheckTimer)) {
                    clearInterval(changeCheckTimer);
                    changeCheckTimer = null;
                  }
                  try {
                    /*let player_response = decodeURIComponent(video_info);
                    let urlParams = new URLSearchParams(player_response);
                    if (urlParams.get("player_response") != null) {
                      player_response = urlParams.get("player_response");
                    }
                    player_response = JSON.parse(player_response);// data.items[0].status.privacyStatus = "public" -> Öffentliches Video*/
                    let player_response = JSON.parse(video_info);
                    var premiere = !isUndefinedOrNull(player_response) && !player_response.videoDetails.isLiveContent;
                    premiere = premiere && !isUndefinedOrNull(data.items[0].liveStreamingDetails);
                    var livestream = !isUndefinedOrNull(player_response) && player_response.videoDetails.isLiveContent;
                    var innerHTML = "<span id=\"dot\" class=\"style-scope ytd-video-primary-info-renderer\">•</span>";
                    updateLiveContent(premiere, livestream, data, mom);
                  } 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);
  }
  //getExactUploadDate();
  //document.addEventListener('click', getExactUploadDate);
  //document.addEventListener('yt-page-data-updated', getExactUploadDate);
  //document.addEventListener('yt-navigate-finish', getExactUploadDate);
  document.addEventListener('refresh-clicked', refreshEventListener);
  //<video style="width: 853px; height: 480px; left: 0px; top: 0px;" tabindex="-1" class="video-stream html5-main-video" src="blob:https://www.youtube.com/0976da77-cfd4-4922-ad9e-383d88a12200"></video>
  /*function main() {
    let videoStream = document.getElementsByClassName('video-stream');
    if (videoStream.length < 1) {
      setTimeout(() => main(), 500);
    } else {
      console.log('video-stream:', videoStream[0]);
      // videoStream[0].addEventListener('loadeddata', (event) => console.log(`Loaded ${event.target.src}`));
      //videoStream[0].addEventListener('loadeddata', (event) => getExactUploadDate());
      videoStream[0].addEventListener('durationchange', (event) => getExactUploadDate());
    }
  }*/
  function main() {
    let ytdPlayer = document.getElementById('ytd-player');
    if (!ytdPlayer) {
      setTimeout(() => main(), 500);
    } else {
      ytdPlayer.addEventListener('yt-player-updated', (event) => getExactUploadDate());
    }
  }
  main();
  if (new URLSearchParams(window.location.search).get("v") != null) {
    getExactUploadDate();
  }
})();