Last active
March 1, 2024 03:38
-
-
Save tomlau10/446881c3cfca2639ccda688d07df1cf9 to your computer and use it in GitHub Desktop.
Fix stream video incorrect thumbnail offset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name stream video thumbnail offset fix | |
// @namespace https://gist.github.com/tomlau10 | |
// @version 0.4.3 | |
// @license MIT | |
// @description Fix stream video incorrect thumbnail offset | |
// @author Tom Lau | |
// @include https://dood.*/* | |
// @match https://filemoon.sx/* | |
// @match https://streamwish.to/* | |
// @updateURL https://gist.github.com/tomlau10/446881c3cfca2639ccda688d07df1cf9/raw/stream_video_thumbnail_fix.user.js | |
// @downloadURL https://gist.github.com/tomlau10/446881c3cfca2639ccda688d07df1cf9/raw/stream_video_thumbnail_fix.user.js | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// prevent console.clear and restore console.log | |
Object.assign(unsafeWindow.console, window.console); | |
unsafeWindow.console.clear = () => {}; | |
const parseTimeText = (text) => text.split(':').reduce( | |
(s, v, i, a) => s + parseInt(v) * Math.pow(60, a.length-1 - i), | |
0 | |
); | |
const controllers = [ | |
{ // doodstream | |
checkPlatform: () => { | |
const link = document.querySelector('.vjs-brand-container-link'); | |
return link && link.href.indexOf('doodstream') >= 0; | |
}, | |
getTimeThumb: () => document.querySelector('.vjs-thumbnail'), | |
getTimeText: () => document.querySelector('.vjs-thumbnail-text'), | |
getTotalSecs: () => { | |
const progressBar = document.querySelector('.vjs-progress-holder'); | |
if (!progressBar || !progressBar.ariaValueText) { | |
return; | |
} | |
return parseTimeText(progressBar.ariaValueText.split(' of ')[1]); | |
}, | |
getThumbImage: (timeThumb) => timeThumb, | |
setBlank: (timeThumb) => { | |
const clip = `rect(0px, 0px, 0px, 0px)`; | |
if (timeThumb.style.clip === clip) { | |
return; | |
} | |
timeThumb.style.clip = clip; | |
}, | |
setPos: (timeThumb, left, top) => { | |
if (timeThumb.style.left === left && timeThumb.style.top === top) { | |
return; | |
} | |
timeThumb.style.left = left; | |
timeThumb.style.top = top; | |
timeThumb.style.clip = ''; | |
}, | |
}, | |
{ // filemoon | |
checkPlatform: () => { | |
const link = document.querySelector('link[rel=apple-touch-icon]'); | |
return link && link.href.indexOf('filemoon') >= 0; | |
}, | |
getTimeThumb: () => document.querySelector('.jw-time-thumb'), | |
getTimeText: () => document.querySelector('.jw-time-tip > .jw-time-time'), | |
getTotalSecs: () => { | |
const timeSlider = document.querySelector('.jw-slider-time'); | |
return parseFloat(timeSlider && timeSlider.ariaValueMax); | |
}, | |
getThumbImage: (timeThumb) => { | |
if (!timeThumb.style.backgroundImage) { | |
return; | |
} | |
if (!timeThumb.backgroundImage) { | |
timeThumb.backgroundImage = new Image(); | |
timeThumb.backgroundImage.src = timeThumb.style.backgroundImage.slice(4, -1).replace(/"/g, ''); | |
} | |
return timeThumb.backgroundImage; | |
}, | |
setBlank: (timeThumb) => { | |
if (!timeThumb.style.backgroundImage) { | |
return; | |
} | |
timeThumb.style.backgroundImage = ''; | |
}, | |
setPos: (timeThumb, left, top) => { | |
const backgroundPosition = `${left} ${top}`; | |
if (timeThumb.style.backgroundPosition === backgroundPosition) { | |
return; | |
} | |
timeThumb.style.backgroundPosition = backgroundPosition; | |
}, | |
}, | |
{ // streamwish | |
checkPlatform: () => { | |
const meta = document.querySelector('meta[name=keywords]'); | |
return meta && meta.content.toLowerCase().indexOf('streamwish') >= 0; | |
}, | |
getTimeThumb: () => document.querySelector('.jw-time-thumb'), | |
getTimeText: () => document.querySelector('.jw-time-tip > .jw-time-time'), | |
getTotalSecs: () => { | |
const durationText = document.querySelector(".jw-text-duration"); | |
return durationText && parseTimeText(durationText.innerHTML); | |
}, | |
getThumbImage: (timeThumb) => { | |
if (!timeThumb.style.backgroundImage) { | |
return; | |
} | |
if (!timeThumb.backgroundImage) { | |
timeThumb.backgroundImage = new Image(); | |
timeThumb.backgroundImage.src = timeThumb.style.backgroundImage.slice(4, -1).replace(/"/g, ''); | |
} | |
return timeThumb.backgroundImage; | |
}, | |
setBlank: (timeThumb) => { | |
if (!timeThumb.style.backgroundImage) { | |
return; | |
} | |
timeThumb.style.backgroundImage = ''; | |
}, | |
setPos: (timeThumb, left, top) => { | |
const backgroundPosition = `${left} ${top}`; | |
if (timeThumb.style.backgroundPosition === backgroundPosition) { | |
return; | |
} | |
timeThumb.style.backgroundPosition = backgroundPosition; | |
}, | |
}, | |
]; | |
const interval = setInterval(() => { | |
// check platform | |
const controller = controllers.find(c => c.checkPlatform()); | |
if (!controller) { | |
return; | |
} | |
// get time thumb and time text html node | |
const timeThumb = controller.getTimeThumb(); | |
const timeText = controller.getTimeText(); | |
if (!timeThumb || !timeText) { | |
return; | |
} | |
// get video legnth | |
const totalSecs = controller.getTotalSecs(); | |
if (!totalSecs) { | |
return; | |
} | |
// get background size | |
const thumbImage = controller.getThumbImage(timeThumb); | |
if (!thumbImage || !thumbImage.height) { | |
return; | |
} | |
const singleWidth = 200, singleHeight = 112; | |
const tnRows = thumbImage.height / singleHeight; | |
const tnCols = thumbImage.width / singleWidth; | |
const tnTotal = tnRows * tnCols; | |
// observe time thumb and time text changes | |
const observer = new MutationObserver((mutations, owner) => { | |
// parse time text | |
const secs = parseTimeText(timeText.textContent); | |
// calculate index | |
const progress = secs / totalSecs; | |
const index = Math.min(Math.floor(progress * (tnTotal + 1) + 0.05) - 1, tnTotal - 1); | |
if (index < 0) { | |
// show as blank if before 1st thumbnail | |
controller.setBlank(timeThumb); | |
return; | |
} | |
// update background position | |
const width = Math.floor(index % tnCols) * -singleWidth; | |
const height = Math.floor(index / tnRows) * -singleHeight; | |
const left = `${width}px`; | |
const top = `${height}px`; | |
controller.setPos(timeThumb, left, top); | |
}); | |
observer.observe(timeThumb, { attributes: true }); | |
observer.observe(timeText, { childList: true }); | |
clearInterval(interval); | |
}, 1000); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment