show duration for livestreams and present time for archives
< Feedback on Youtube Live Clock
我發現每次頁面更改會呼叫 main() 的
可是,作者應該沒有想過 main() 會重覆呼叫
導致以下的代碼不斷產生新observer,一次valuenow的改變會重覆呼叫
const observer = new MutationObserver(() => { liveClock.textContent = updateLiveTime() })
observer.observe(progressBar, { characterData: true, attributeFilter: ['aria-valuenow'] })
(上面的一次valuenow的改變會重覆呼叫不同observer的updateLiveTime()問題,是問題,但跟原先的問題沒直接關係)
我猜想問題是這樣
切換後的 yt-navigate-finish 的 main 當中,waitElements 全部已有,第一個mutationchange已經等待完成
但當中的videoData元素還是舊的,要再晚一點時間才有新元素替換。 (或者新舊元素同時存在,舊元素在頁面前面,還未移除)
以下是不考慮性能問題的簡單修改。請參考
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一致
已於最新版本修正以下問題
1. 切換影片時時間無法正確顯示
2. observer重複產生
感謝CY大的幫忙,如果還有發現其他問題再麻煩通知我
已於最新版本修正以下問題1. 切換影片時時間無法正確顯示2. observer重複產生感謝CY大的幫忙,如果還有發現其他問題再麻煩通知我
感謝。看來應該沒問題的。如有,之後再回報。
另外,想問一下為什麼
let liveData = JSON.parse(videoData.textContent)
要在每次 aria-valuenow
更新時執行。感覺在waitElements裡面得到videoData後做一次就夠了
另外,想問一下為什麼
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元素
(當然實際上未有上述情況,不修正也可以)
現在在每次切換影片後都會停止監聽並監聽正確的元素了
感謝CY大幫忙點出許多我沒注意到的問題
Youtube Live Clock的腳本未有考慮影片切換 (包括縮小播放器,切換再放大)
由直播影片切換成一般影片,時間會重覆顯示
videoData (及其他元素) 應在影片切換後更新 (例如偵測 頁面的yt-navigate-finish 或 影片的durationchange )