Youtube Live Clock

show duration for livestreams and present time for archives

当前为 2023-11-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube Live Clock
  3. // @name:zh-TW Youtube Live Clock
  4. // @namespace https://greasyfork.org/scripts/453367
  5. // @version 1.6.3
  6. // @description show duration for livestreams and present time for archives
  7. // @description:zh-TW 顯示直播及直播存檔當下的時間
  8. // @author Derek
  9. // @match *://www.youtube.com/*
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. //you can choose your ideal date format by changing the FORMAT's value below
  14. const FORMAT = 1
  15. /*
  16. 1: 2022/10/31 06:37:10
  17. 2: 10/31/2022 06:37:10
  18. 3: 31/10/2022 06:37:10
  19. 4: Mon 31/10/2022 06:37:10
  20. 5: Monday 31/10/2022 06:37:10
  21. */
  22.  
  23. const $ = (element) => document.querySelector(element)
  24.  
  25. const abbr = {
  26. week: { Sun: 'Sunday', Mon: 'Monday', Tue: 'Tuesday', Wed: 'Wednesday', Thu: 'Thursday', Fri: 'Friday', Sat: 'Saturday' },
  27. month: { Jan: 'January', Feb: 'February', Mar: 'March', Apr: 'April', May: 'May', Jun: 'June', Jul: 'July', Aug: 'August', Sep: 'September', Oct: 'October', Nov: 'November', Dec: 'December' }
  28. }
  29.  
  30. const twoDigit = (num) => num.toString().padStart(2, '0')
  31.  
  32. const timeFormat = (time) => {
  33. const second = time % 60
  34. const minute = Math.floor((time / 60) % 60)
  35. const hour = Math.floor(time / 3600)
  36. return hour > 0 ? `${hour}:${twoDigit(minute)}:${twoDigit(second)}` : `${minute}:${twoDigit(second)}`
  37. }
  38.  
  39. const dateFormat = (presentTime) => {
  40. const [week, , day, year, time] = presentTime.toString().split(' ')
  41. const month = twoDigit(presentTime.getMonth() + 1)
  42. return {
  43. 1: ` (${year}/${month}/${day} ${time})`,
  44. 2: ` (${month}/${day}/${year} ${time})`,
  45. 3: ` (${day}/${month}/${year} ${time})`,
  46. 4: ` (${week} ${day}/${month}/${year} ${time})`,
  47. 5: ` (${abbr.week[week]} ${day}/${month}/${year} ${time})`
  48. } [FORMAT]
  49. }
  50.  
  51. let liveBadge = null
  52. let timeDisplay = null
  53. let progressBar = null
  54.  
  55. const waitElements = () => {
  56. return new Promise((resolve) => {
  57. const observer = new MutationObserver(() => {
  58. liveBadge = $('.ytp-live-badge')
  59. timeDisplay = $('.ytp-time-display')
  60. progressBar = $('.ytp-progress-bar')
  61. if (liveBadge && timeDisplay && progressBar) {
  62. observer.disconnect()
  63. resolve()
  64. }
  65. })
  66. observer.observe(document.body, { attributes: false, childList: true, subtree: true })
  67. })
  68. }
  69.  
  70. const getLiveClock = () => {
  71. let clockElement = $('#present-time')
  72. if (!clockElement) {
  73. clockElement = document.createElement('span')
  74. clockElement.setAttribute('id', 'present-time')
  75. timeDisplay.childNodes[1].appendChild(clockElement)
  76. }
  77. return clockElement
  78. }
  79.  
  80. const updateLiveTime = () => {
  81. const startDate = $('[itemprop="startDate"]')
  82. if (!startDate) { return '' }
  83.  
  84. const startDateString = startDate.getAttribute('content')
  85. if (new Date().getTime() < new Date(startDateString).getTime()) { return '' }
  86.  
  87. const endDate = $('[itemprop="endDate"]')
  88. const progressTime = progressBar.getAttribute('aria-valuenow')
  89.  
  90. return endDate ? dateFormat(new Date(Date.parse(startDateString) + progressTime * 1000)) : timeFormat(progressTime)
  91. }
  92.  
  93. const main = async () => {
  94. if (window.location.href.includes('/watch?v=')) {
  95. await waitElements()
  96. liveBadge.style = 'margin-left: 10px'
  97. const liveClock = getLiveClock()
  98. const observer = new MutationObserver(() => { liveClock.textContent = updateLiveTime() })
  99. observer.observe(progressBar, { attributes: true })
  100. }
  101. }
  102.  
  103. document.addEventListener('yt-navigate-finish', main)