Youtube exact upload

Adds exact upload time to youtube videos

目前为 2021-03-27 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube exact upload
  3. // @name:de YouTube exakter Hochladezeitpunkt
  4. // @description Adds exact upload time to youtube videos
  5. // @description:de Fügt YouTube-Videos den exakten Hochladezeitpunkt mit Uhrzeit hinzu
  6. // @require https://cdn.jsdelivr.net/npm/hacktimer@1.1.3/HackTimer.min.js
  7. // @require http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js
  8. // @version 0.3
  9. // @match https://www.youtube.com/*
  10. // @grant none
  11. // @namespace https://greasyfork.org/users/94906
  12. // @license WTFPL
  13. // ==/UserScript==
  14.  
  15. // HackTimer is for making setInterval work in background tabs
  16. // moment is for formatting and comparing dates and times
  17.  
  18. (function() {
  19. 'use strict';
  20. console.log("YT EXACT UPLOAD LOADED");
  21. var DATE_PATTERN = "DD.MM.YYYY"; // https://momentjs.com/docs/#/parsing/string-format/
  22. var TIME_PATTERN = "HH:mm:ss [Uhr]"; // https://momentjs.com/docs/#/parsing/string-format/
  23. var DATETIME_COMBINE_PATTERN = " [um] ";
  24. var SCHEDULED_LIVESTREAM_START = "Livestream geplant für: ";
  25. var SCHEDULED_PREMIERE_START = "Premiere geplant für: ";
  26. var ONGOING_LIVESTREAM_START = "Aktiver Livestream seit ";
  27. var ONGOING_PREMIERE_START = "Aktive Premiere seit ";
  28. var ENDED_LIVESTREAM_START = "Livestream von ";
  29. var ENDED_PREMIERE_START = "Premiere von ";
  30. var DATETIME_UNTIL_PATTERN = " bis ";
  31. var SINCE = "Seit";
  32. var YT_API_KEY = "AIzaSyCuVr_6gb-vkfbtp6rM0qwWnRQKJXw167c";
  33. var BASE_URL = "https://www.googleapis.com/youtube/v3/videos?part=snippet,liveStreamingDetails,contentDetails,localizations,player,statistics,status&key=" + YT_API_KEY;
  34. var interval = null;
  35. var changeCheckTimer = null;
  36. var currentVideoId = null;
  37. //console.log("Language:" + document.getElementsByTagName("html")[0].getAttribute("lang"));
  38. function genUrl(){
  39. const urlParams = new URLSearchParams(window.location.search);
  40.  
  41. if(urlParams.get("v") != null){
  42. return BASE_URL + "&id=" + urlParams.get("v");
  43. }else {
  44. return "";
  45. }
  46. }
  47. function isUndefinedOrNull(obj) {
  48. return obj == undefined || obj == null;
  49. }
  50. function sleep(milliseconds) {
  51. return new Promise(resolve => setTimeout(resolve, milliseconds));
  52. }
  53. function formatMilliseconds(milliseconds, joinString, showDays, showHours, showMinutes, showSeconds, showMilliseconds, pad, hideDaysOnNull) {
  54. let result = '';
  55. let days = Math.floor(milliseconds / (1000 * 60 * 60 * 24));
  56. let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);
  57. let minutes = Math.floor((milliseconds / (1000 * 60)) % 60);
  58. let seconds = Math.floor((milliseconds / 1000) % 60);
  59. milliseconds = milliseconds % 1000;
  60. if (showDays) {
  61. if (days < 1 && hideDaysOnNull) {
  62. } else {
  63. if (result != '')
  64. result += joinString;
  65. if (pad) {
  66. if (days < 10)
  67. result += '0' + days;
  68. else
  69. result += days;
  70. } else
  71. result += days;
  72. }
  73. }
  74. if (showHours) {
  75. if (result != '')
  76. result += joinString;
  77. if (pad)
  78. result += ('0' + hours).slice(-2);
  79. else
  80. result += hours;
  81. }
  82. if (showMinutes) {
  83. if (result != '')
  84. result += joinString;
  85. if (pad)
  86. result += ('0' + minutes).slice(-2);
  87. else
  88. result += minutes;
  89. }
  90. if (showSeconds) {
  91. if (result != '')
  92. result += joinString;
  93. if (pad)
  94. result += ('0' + seconds).slice(-2);
  95. else
  96. result += seconds;
  97. }
  98. if (showMilliseconds) {
  99. if (result != '')
  100. result += joinString;
  101. if (pad)
  102. result += ('00' + milliseconds).slice(-3);
  103. else
  104. result += milliseconds;
  105. }
  106. return result;
  107. }
  108. function updateOngoing(durationInMilliseconds) {
  109. if (!isUndefinedOrNull(interval)) {
  110. clearInterval(interval);
  111. interval = null;
  112. }
  113. let duration = durationInMilliseconds;
  114. interval = setInterval(function() {
  115. duration += 500;
  116. document.getElementById("ongoing-video-duration").innerHTML = formatMilliseconds(duration, ':', true, true, true, true, false, true, true);
  117. }, 500);
  118. }
  119. async function updateLiveContent(premiere, livestream, data, mom) {
  120. var element = null;
  121. while (isUndefinedOrNull(element)) {
  122. element = document.getElementById("date");
  123. await sleep(200);
  124. }
  125. var durationInMilliseconds = null;
  126. var ongoing = false;
  127. var innerHTML = "<span id=\"dot\" class=\"style-scope ytd-video-primary-info-renderer\">•</span>";
  128. if (!premiere && !livestream) { // normal video
  129. if (mom.isSame(moment(), 'day')) // today
  130. innerHTML += mom.format(TIME_PATTERN);
  131. else
  132. innerHTML += mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  133. } else {
  134. if (isUndefinedOrNull(data.items[0].liveStreamingDetails.actualStartTime)) { // planned
  135. mom = moment(data.items[0].liveStreamingDetails.scheduledStartTime);
  136. if (mom.isSame(moment(), 'day')) { // today
  137. if (livestream)
  138. innerHTML += SCHEDULED_LIVESTREAM_START + mom.format(TIME_PATTERN);
  139. else if (premiere)
  140. innerHTML += SCHEDULED_PREMIERE_START + mom.format(TIME_PATTERN);
  141. else
  142. innerHTML += mom.format(TIME_PATTERN);
  143. } else {
  144. if (livestream)
  145. innerHTML += SCHEDULED_LIVESTREAM_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  146. else if (premiere)
  147. innerHTML += SCHEDULED_PREMIERE_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  148. else
  149. innerHTML += mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  150. }
  151. } else { // ongoing / ended
  152. mom = moment(data.items[0].liveStreamingDetails.actualStartTime);
  153. var endTime = null;
  154. if (!isUndefinedOrNull(data.items[0].liveStreamingDetails.actualEndTime))
  155. endTime = moment(data.items[0].liveStreamingDetails.actualEndTime);
  156. if (endTime == null) { // ongoing
  157. ongoing = true;
  158. durationInMilliseconds = moment.duration(moment().diff(mom)).asMilliseconds();
  159. if (mom.isSame(moment(), 'day')) { // today
  160. if (livestream)
  161. innerHTML += ONGOING_LIVESTREAM_START + mom.format(TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
  162. else if (premiere)
  163. innerHTML += ONGOING_PREMIERE_START + mom.format(TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
  164. else
  165. innerHTML += SINCE + " " + mom.format(TIME_PATTERN) + " (<span id=\"ongoing-video-duration\">" + formatMilliseconds(durationInMilliseconds, ':', true, true, true, true, false, true, true) + "</span>)";
  166. } else {
  167. if (livestream)
  168. 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>)";
  169. else if (premiere)
  170. 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>)";
  171. else
  172. 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>)";
  173. }
  174. } else { // ended
  175. if (mom.isSame(endTime, 'day')) { // start date and end date are the same
  176. if (mom.isSame(moment(), 'day')) { // today
  177. if (livestream)
  178. innerHTML += ENDED_LIVESTREAM_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
  179. else if (premiere)
  180. innerHTML += ENDED_PREMIERE_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
  181. else
  182. innerHTML += mom.format(TIME_PATTERN);
  183. } else {
  184. if (livestream)
  185. innerHTML += ENDED_LIVESTREAM_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
  186. else if (premiere)
  187. innerHTML += ENDED_PREMIERE_START + mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
  188. else
  189. innerHTML += mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(TIME_PATTERN);
  190. }
  191. } else {
  192. if (mom.isSame(moment(), 'day')) { // today
  193. if (livestream)
  194. innerHTML += ENDED_LIVESTREAM_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  195. else if (premiere)
  196. innerHTML += ENDED_PREMIERE_START + mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  197. else
  198. innerHTML += mom.format(TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  199. } else {
  200. if (livestream)
  201. 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);
  202. else if (premiere)
  203. 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);
  204. else
  205. innerHTML += mom.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN) + DATETIME_UNTIL_PATTERN + endTime.format(DATE_PATTERN + DATETIME_COMBINE_PATTERN + TIME_PATTERN);
  206. }
  207. }
  208. }
  209. }
  210. }
  211. var contentRating = data.items[0].contentDetails.contentRating;
  212. if (!isUndefinedOrNull(contentRating.ytRating) && contentRating.ytRating == 'ytAgeRestricted')
  213. innerHTML += " - FSK 18";
  214. element.innerHTML = innerHTML;
  215. if (ongoing)
  216. updateOngoing(durationInMilliseconds);
  217. return ongoing;
  218. }
  219. function updateLiveContentWithChangeCheck(premiere, livestream, data, mom) {
  220. var ongoing = updateLiveContent(premiere, livestream, data, mom);
  221. /*document.getElementsByClassName('html5-main-video')[0].addEventListener('ended', function() {
  222. fetch('https://www.youtube.com/get_video_info?video_id=' + currentVideoId).then(function(response) {
  223. return response.text();
  224. }).then(function(video_info) {
  225. try {
  226. let player_response = decodeURIComponent(video_info);
  227. let urlParams = new URLSearchParams(player_response);
  228. if (urlParams.get("player_response") != null) {
  229. player_response = urlParams.get("player_response");
  230. }
  231. player_response = JSON.parse(player_response);
  232. var premiere = !isUndefinedOrNull(player_response) && !player_response.videoDetails.isLiveContent;
  233. premiere = premiere && !isUndefinedOrNull(data.items[0].liveStreamingDetails);
  234. var livestream = !isUndefinedOrNull(player_response) && player_response.videoDetails.isLiveContent;
  235. var live = !isUndefinedOrNull(player_response) && player_response.videoDetails.isLive;
  236. if (ongoing)
  237. updateLiveContent(premiere, livestream, data, mom);
  238. } catch (ex) {
  239. console.error(ex);
  240. }
  241. }).catch(error => console.error("YOUTUBE EXACT UPLOAD ERROR: " + error, "\nget_video_info doesn't seem to work"));
  242. });*/
  243. }
  244. function getExactUploadDate() {
  245. var abort = false;
  246. const processEvent = async () => {
  247. await sleep(500);
  248. const urlParams = new URLSearchParams(window.location.search);
  249. if (urlParams.get("v") != null){
  250. let videoId = urlParams.get("v");
  251. if (videoId == currentVideoId) {
  252. abort = true;
  253. } else {
  254. currentVideoId = videoId;
  255. }
  256. }
  257. if ((YT_API_KEY != "" || typeof YT_API_KEY != "undefined") && !abort) {
  258. var url = genUrl();
  259. if (url != "") {
  260. fetch(url).then(function(response) {
  261. return response.json();
  262. }).then(function(data) {
  263. if (data.pageInfo.totalResults > 0) {
  264. const addTime = async () => {
  265. var mom = moment(data.items[0].snippet.publishedAt);
  266. console.log(mom);
  267. fetch('https://www.youtube.com/get_video_info?video_id=' + currentVideoId).then(function(response) {
  268. return response.text();
  269. }).then(function(video_info) {
  270. if (!isUndefinedOrNull(interval)) {
  271. clearInterval(interval);
  272. interval = null;
  273. }
  274. if (!isUndefinedOrNull(changeCheckTimer)) {
  275. clearInterval(changeCheckTimer);
  276. changeCheckTimer = null;
  277. }
  278. try {
  279. let player_response = decodeURIComponent(video_info);
  280. let urlParams = new URLSearchParams(player_response);
  281. if (urlParams.get("player_response") != null) {
  282. player_response = urlParams.get("player_response");
  283. }
  284. player_response = JSON.parse(player_response);// data.items[0].status.privacyStatus = "public" -> Öffentliches Video
  285. var premiere = !isUndefinedOrNull(player_response) && !player_response.videoDetails.isLiveContent;
  286. premiere = premiere && !isUndefinedOrNull(data.items[0].liveStreamingDetails);
  287. var livestream = !isUndefinedOrNull(player_response) && player_response.videoDetails.isLiveContent;
  288. var innerHTML = "<span id=\"dot\" class=\"style-scope ytd-video-primary-info-renderer\">•</span>";
  289. updateLiveContentWithChangeCheck(premiere, livestream, data, mom);
  290. } catch (ex) {
  291. console.error(ex);
  292. }
  293. }).catch(error => console.error("YOUTUBE EXACT UPLOAD ERROR: " + error, "\nget_video_info doesn't seem to work"));
  294. }
  295. addTime();
  296. }
  297. }).catch(error => console.error("YOUTUBE EXACT UPLOAD ERROR: " + error, "\nINVALID API KEY?"));
  298. }
  299. } else {
  300. if(!abort)
  301. console.error("YOUTUBE EXACT UPLOAD ERROR: Undefined api key");
  302. }
  303. }
  304. processEvent();
  305. }
  306. //getExactUploadDate();
  307. //document.addEventListener('click', getExactUploadDate);
  308. //document.addEventListener('yt-page-data-updated', getExactUploadDate);
  309. document.addEventListener('yt-navigate-finish', getExactUploadDate);
  310. })();