Youtube Live Clock

顯示直播當下的時間

目前為 2023-03-25 提交的版本,檢視 最新版本

// ==UserScript==
// @name               Youtube Live Clock
// @name:zh-TW         Youtube Live Clock
// @namespace          https://greasyfork.org/scripts/453367
// @version            1.6.0
// @description        show present time of the livestream
// @description:zh-TW  顯示直播當下的時間
// @author             Derek
// @match              *://www.youtube.com/*
// @grant              none
// ==/UserScript==

//you can choose your ideal date format by changing the FORMAT's value below
const FORMAT = 1
/*
  1: 2022/10/31 06:37:10
  2: 10/31/2022 06:37:10
  3: 31/10/2022 06:37:10
  4: Mon 31/10/2022 06:37:10
  5: Monday 31/10/2022 06:37:10
*/

const $ = (element) => document.querySelector(element)
const $$ = (element) => document.querySelectorAll(element)

const abbr = {
  week: { Sun: 'Sunday', Mon: 'Monday', Tue: 'Tuesday', Wed: 'Wednesday', Thu: 'Thursday', Fri: 'Friday', Sat: 'Saturday' },
  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' }
}

const twoDigit = (num) => num.toString().padStart(2, '0')

const timeFormat = (time) => {
  let second = time % 60
  let minute = Math.floor((time % 3600) / 60)
  let hour = Math.floor(time / 3600)
  return hour > 0 ? `${hour}:${twoDigit(minute)}:${twoDigit(second)}` : `${minute}:${twoDigit(second)}`
}

const dateFormat = (presentTime) => {
  const [week, , day, year, time] = presentTime.toString().split(' ')
  const month = twoDigit(presentTime.getMonth() + 1)
  return {
    1: ` (${year}/${month}/${day} ${time})`,
    2: ` (${month}/${day}/${year} ${time})`,
    3: ` (${day}/${month}/${year} ${time})`,
    4: ` (${week} ${day}/${month}/${year} ${time})`,
    5: ` (${abbr.week[week]} ${day}/${month}/${year} ${time})`
  } [FORMAT]
}

let liveData = null
let progressBar = null
let clockElement = null

const waitProgressBar = () => {
  return new Promise((resolve, reject) => {
    const startTime = Date.now()
    const checkInterval = setInterval(() => {
      progressBar = $('.ytp-progress-bar')
      if (progressBar !== null) {
        clearInterval(checkInterval)
        resolve()
      }
      if (Date.now() - startTime > 10000) {
        clearInterval(checkInterval)
        reject('Wait time exceeded')
      }
    }, 500)
  })
}

const getLiveClock = () => {
  if (!clockElement) {
    clockElement = document.createElement('span')
    clockElement.setAttribute('class', 'present-time')
    $('.ytp-time-display').childNodes[1].appendChild(clockElement)
  }
  return clockElement
}

const updateLiveTime = () => {
  try {
    if (liveData) {
      const progressTime = progressBar.getAttribute('aria-valuenow')
      return liveData[0].endDate ? dateFormat(new Date(Date.parse(liveData[0].startDate) + progressTime * 1000)) : timeFormat(progressTime)
    } else return ''
  } catch (error) {
    console.log('Youtube Live Clock: can\'t get liveData', error)
    return ''
  }
}

const startLiveClock = async () => {
  try {
    await waitProgressBar()
    liveData = JSON.parse($('.ytd-player-microformat-renderer').textContent).publication
    $('.ytp-live-badge').style = 'margin-left: 10px'
    const observer = new MutationObserver(() => { getLiveClock().textContent = updateLiveTime() })
    observer.observe(progressBar, { attributes: true })
  } catch (error) {
    console.log('Youtube Live Clock: error starting clock', error)
  }
}

const main = () => {
  if (window.location.href.includes('/watch?v=')) startLiveClock()
}

document.addEventListener('yt-navigate-finish', main)