Youtube exact upload

Adds exact upload time to youtube videos

Stan na 04-04-2021. Zobacz najnowsza wersja.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==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        http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js
// @version        0.4
// @match          https://www.youtube.com/*
// @grant          none
// @namespace      https://greasyfork.org/users/94906
// @license        WTFPL
// ==/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 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] ";
  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 YT_API_KEY = "YouTube API-Key";
  var BASE_URL = "https://www.googleapis.com/youtube/v3/videos?part=snippet,liveStreamingDetails,contentDetails,localizations,player,statistics,status&key=" + YT_API_KEY;
  var interval = null;
  var changeCheckTimer = null;
  var currentVideoId = null;
  //console.log("Language:" + document.getElementsByTagName("html")[0].getAttribute("lang"));
  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("date");
      await sleep(200);
    }
    var durationInMilliseconds = null;
    var ongoing = false;
    var innerHTML = "<span id=\"dot\" class=\"style-scope ytd-video-primary-info-renderer\">•</span>";
    if (!premiere && !livestream) { // normal video
      if (mom.isSame(moment(), 'day')) // today
        innerHTML += 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 += 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 += 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 += 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 += 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 += " - FSK 18";
    element.innerHTML = innerHTML;
    if (ongoing)
      updateOngoing(durationInMilliseconds);
    return ongoing;
  }
  function updateLiveContentWithChangeCheck(premiere, livestream, data, mom) {
    var ongoing = updateLiveContent(premiere, livestream, data, mom);
    /*document.getElementsByClassName('html5-main-video')[0].addEventListener('ended', function() {
      fetch('https://www.youtube.com/get_video_info?video_id=' + currentVideoId).then(function(response) {
        return response.text();
      }).then(function(video_info) {
        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);
          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 live = !isUndefinedOrNull(player_response) && player_response.videoDetails.isLive;
          if (ongoing)
          	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"));
    });*/
  }
  function getExactUploadDate() {
    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 ((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);
                fetch('https://www.youtube.com/get_video_info?video_id=' + currentVideoId).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
                    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>";
                    updateLiveContentWithChangeCheck(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();
  }
  //getExactUploadDate();
  //document.addEventListener('click', getExactUploadDate);
  //document.addEventListener('yt-page-data-updated', getExactUploadDate);
  document.addEventListener('yt-navigate-finish', getExactUploadDate);
})();