您需要先安装一个扩展,例如 篡改猴、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://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.6 // @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 AGE_RESTRICTED = " - FSK 18"; var SHOW_REFRESH = true; var REFRESH_TIMESTAMP = "⟳"; var SHOW_UNDERLINE_ON_TIMESTAMP = false; 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 += AGE_RESTRICTED; if (SHOW_REFRESH) { if (SHOW_UNDERLINE_ON_TIMESTAMP) innerHTML += " - <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 style=\"color: var(--yt-spec-text-secondary); cursor: pointer;\" onclick=\"document.dispatchEvent(new Event('refresh-clicked'));\">" + REFRESH_TIMESTAMP + "</span>"; } 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(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); 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(); } 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); })();