Youtube Live Clock

show duration for livestreams and present time for archives

< Feedback on Youtube Live Clock

Review: Good - script works

Youtube Live Clock的腳本未有考慮影片切換 (包括縮小播放器,切換再放大)

由直播影片切換成一般影片,時間會重覆顯示

videoData (及其他元素) 應在影片切換後更新 (例如偵測 頁面的yt-navigate-finish 或 影片的durationchange )

我發現每次頁面更改會呼叫 main() 的

可是,作者應該沒有想過 main() 會重覆呼叫

導致以下的代碼不斷產生新observer,一次valuenow的改變會重覆呼叫

  const observer = new MutationObserver(() => { liveClock.textContent = updateLiveTime() })
  observer.observe(progressBar, { characterData: true, attributeFilter: ['aria-valuenow'] })
§
Posted: 06-01-2025
Edited: 06-01-2025

(上面的一次valuenow的改變會重覆呼叫不同observer的updateLiveTime()問題,是問題,但跟原先的問題沒直接關係)

我猜想問題是這樣

切換後的 yt-navigate-finish 的 main 當中,waitElements 全部已有,第一個mutationchange已經等待完成

但當中的videoData元素還是舊的,要再晚一點時間才有新元素替換。 (或者新舊元素同時存在,舊元素在頁面前面,還未移除)

§
Posted: 06-01-2025
Edited: 06-01-2025

以下是不考慮性能問題的簡單修改。請參考



let liveBadge = null
let videoData = null
let timeDisplay = null
let progressBar = null
let progressBarObserver = null
let liveData = null
let liveClock = null

const refreshElements = () => {
  videoData = $('#microformat script')
  liveBadge = $('.ytp-chrome-bottom .ytp-live-badge')
  timeDisplay = $('.ytp-chrome-bottom .ytp-time-display')
  progressBar = $('.ytp-chrome-bottom .ytp-progress-bar')
  liveData = videoData ? JSON.parse(videoData.textContent) : null
  liveClock = getLiveClock()
}

const waitElements = () => {
  return new Promise((resolve) => {
    const observer = new MutationObserver(() => {
      refreshElements()

      if (liveBadge && timeDisplay && progressBar && videoData) {
        observer.disconnect()
        resolve()
      }
    })
    observer.observe(document.body, { attributes: false, childList: true, subtree: true })
  })
}

const getLiveClock = () => {
  if (timeDisplay) {
    let clockElement = $('#present-time')
    if (!clockElement) {
      clockElement = document.createElement('span')
      clockElement.setAttribute('id', 'present-time')
      timeDisplay.insertBefore(clockElement, timeDisplay.childNodes[1])
    }
    return clockElement
  }
  return null
}

const updateLiveTime = () => {
  if (!liveData || !liveData.publication) return ''

  liveData = liveData.publication[0]
  const progressTime = progressBar.getAttribute('aria-valuenow')
  return liveData.endDate ? dateFormat(new Date(Date.parse(liveData.startDate) + progressTime * 1000)) : timeFormat(progressTime)
}

const main = async () => {
  await waitElements()
  liveBadge.style = 'margin-left: 10px'
  liveClock = getLiveClock()
  if (liveClock) liveClock.textContent = updateLiveTime()
  if (!progressBarObserver) progressBarObserver = new MutationObserver(() => { if (liveClock) liveClock.textContent = updateLiveTime() })
  progressBarObserver.observe(progressBar, { characterData: true, attributeFilter: ['aria-valuenow'] })
}

setInterval(() => {
  refreshElements()
}, 100)

document.addEventListener('yt-navigate-finish', (event) => {
  const url = event.detail.endpoint.commandMetadata.webCommandMetadata.url
  if (url.startsWith('/watch?v=') || url.startsWith('/live/')) main()
})


考慮性能的話,在waitElements中,videoData的videoId跟頁面的videoId不一致時棄掉。直到兩個videoId一致

DerekHuangTác giả
§
Posted: 07-01-2025

已於最新版本修正以下問題
1. 切換影片時時間無法正確顯示
2. observer重複產生
感謝CY大的幫忙,如果還有發現其他問題再麻煩通知我

已於最新版本修正以下問題1. 切換影片時時間無法正確顯示2. observer重複產生感謝CY大的幫忙,如果還有發現其他問題再麻煩通知我

感謝。看來應該沒問題的。如有,之後再回報。

另外,想問一下為什麼

let liveData = JSON.parse(videoData.textContent)

要在每次 aria-valuenow 更新時執行。感覺在waitElements裡面得到videoData後做一次就夠了

DerekHuangTác giả
§
Posted: 07-01-2025

另外,想問一下為什麼

let liveData = JSON.parse(videoData.textContent)

要在每次 aria-valuenow 更新時執行。感覺在waitElements裡面得到videoData後做一次就夠了

這是我一開始用來解決切換影片時時間顯示錯誤的方法,不過似乎隨著更新失效了
現在改在一開始判斷影片是否切換,確實如你所說得到videoData後直接處理就好,也不會消耗額外資源
已於剛剛修正了,非常感謝CY大的幫助

謝謝解釋。

感覺現在 1.7.2 應該沒問題了


只是有一個點有些在意,雖然暫時沒有這樣的現象出現

  let liveClock = getLiveClock()
  liveClock.textContent = updateLiveTime()

  if (!observer) {
    observer = new MutationObserver(() => { liveClock.textContent = updateLiveTime() })
    observer.observe(progressBar, { characterData: true, attributeFilter: ['aria-valuenow'] })
  }

如果頁面改變,導致YouTube的元素重新生成的話,

getLiveClock() 會正確生成新元素

但observer還是有的,所以不會重生生成 observer。

舊observer的那個local variable 還是原本的liveClock元素

(當然實際上未有上述情況,不修正也可以)

DerekHuangTác giả
§
Posted: 07-01-2025

現在在每次切換影片後都會停止監聽並監聽正確的元素了
感謝CY大幫忙點出許多我沒注意到的問題

Post reply

Đăng nhập để bình luận