Created
July 13, 2017 06:38
-
-
Save marktaiwan/2349cbcfc2835d23d3eea6ac32e51de2 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 Derpibooru WebM Volume Toggle (Experimental) | |
// @description Audio toggle for WebM clips | |
// @version 1.0.17 | |
// @author Marker | |
// @namespace https://github.com/marktaiwan/ | |
// @updateURL https://openuserjs.org/meta/mark.taiwangmail.com/Derpibooru_WebM_Volume_Toggle.meta.js | |
// @match https://derpibooru.org/* | |
// @match https://trixiebooru.org/* | |
// @match https://www.derpibooru.org/* | |
// @match https://www.trixiebooru.org/* | |
// @grant none | |
// @noframes | |
// @require https://openuserjs.org/src/libs/soufianesakhi/node-creation-observer.js | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
/* ================ Configs ================ */ | |
const SERVE_WEBM_THUMB = true; | |
const PAUSE_IN_BACKGROUND = true; | |
const ICON_SIZE = '3em'; | |
/* ========================================= */ | |
function initCSS() { | |
var styleElement = document.createElement('style'); | |
styleElement.id = 'derpibooru-volume-toggle-css'; | |
styleElement.type = 'text/css'; | |
styleElement.innerHTML = `/* Generated by Derpibooru WebM Volume Toggle */ | |
.video-container { | |
position: relative; | |
} | |
#image_target .volume-toggle-button { | |
opacity: 0; | |
font-size: ${ICON_SIZE}; | |
} | |
.video-container .volume-toggle-button, #image_target:hover .volume-toggle-button { | |
opacity: 0.4; | |
} | |
.video-container .volume-toggle-button:hover, #image_target .volume-toggle-button:hover { | |
opacity: 0.8; | |
} | |
.volume-toggle-button { | |
position: absolute; | |
top: 0px; | |
left: 5px; | |
margin: 2px; | |
z-index: 5; | |
font-size: 2em; | |
color: #000; | |
cursor: pointer; | |
text-shadow: | |
1px 1px 2px #fff, | |
-1px 1px 2px #fff, | |
1px -1px 2px #fff, | |
-1px -1px 2px #fff; | |
transition: opacity 0.1s; | |
} | |
.volume-toggle-button.fa-volume-off { | |
padding-right: 15px; | |
} | |
.volume-toggle-button.fa-volume-up { | |
padding-right: 0px; | |
} | |
.image-container.has-webm-overlay .volume-toggle-button { | |
top: 20px; | |
} | |
`; | |
document.head.appendChild(styleElement); | |
} | |
function checkDecode(video, delay, resolve) { | |
if (video.webkitAudioDecodedByteCount > 0 || video.mozHasAudio) { | |
resolve(video); | |
} else { | |
// 2 seconds wait time | |
if (delay < 2048) { | |
delay *= 2; | |
setTimeout(() => checkDecode(video, delay, resolve), delay); | |
} | |
} | |
} | |
function createListener(video, resolve) { | |
return function () { | |
if (video.dataset.listenerAttached !== undefined) { | |
return; | |
} else { | |
video.dataset.listenerAttached = '1'; | |
} | |
if (typeof video.webkitAudioDecodedByteCount !== 'undefined' || typeof video.mozHasAudio !== 'undefined') { | |
// wait for video decode | |
var delay = 4; | |
checkDecode(video, delay, resolve); | |
} else { | |
// Only IE, Edge, and Safari supports this, wtf? | |
if (video.audioTracks && video.audioTracks.length) { | |
resolve(video); | |
} | |
} | |
}; | |
} | |
function ifHasAudio(video) { | |
return new Promise((resolve, reject) => { | |
video.addEventListener('loadeddata', createListener(video, resolve), {'once': true}); | |
if (video.readyState >= video.HAVE_CURRENT_DATA) { | |
(createListener(video, resolve))(); | |
} | |
}); | |
} | |
function getParent(element, selector) { | |
do { | |
element = element.parentElement; | |
} while (element !== null && !element.matches(selector)); | |
return element; | |
} | |
function toggle(video) { | |
var container = getParent(video, '.video-container'); | |
var button = container.querySelector('.volume-toggle-button'); | |
if (container.dataset.isMuted == '1') { | |
button.classList.add('fa-volume-up'); | |
button.classList.remove('fa-volume-off'); | |
video.muted = false; | |
container.dataset.isMuted = '0'; | |
} else { | |
button.classList.add('fa-volume-off'); | |
button.classList.remove('fa-volume-up'); | |
video.muted = true; | |
container.dataset.isMuted = '1'; | |
} | |
} | |
function createToggleButton(video) { | |
var container = getParent(video, '.image-show, .image-container'); | |
// Ignore the really small thumbnails | |
if (container.matches('.thumb_tiny')) { | |
return; | |
} | |
var button = document.createElement('div'); | |
button.classList.add('volume-toggle-button'); | |
button.classList.add('fa'); | |
if (container.matches('.video-container')) { | |
if (container.dataset.isMuted != '1') { | |
button.classList.add('fa-volume-up'); | |
video.muted = false; | |
} else { | |
button.classList.add('fa-volume-off'); | |
video.muted = true; | |
} | |
} else { | |
container.classList.add('video-container'); | |
// videos defaults to mute | |
container.dataset.isMuted = '1'; | |
button.classList.add('fa-volume-off'); | |
} | |
container.appendChild(button); | |
button.addEventListener('click', function (event) { | |
event.stopPropagation(); | |
toggle(video); | |
}); | |
} | |
function staggeredPlayback(videoList) { | |
} | |
initCSS(); | |
NodeCreationObserver.onCreation('.image-show video, .image-container video', function (video) { | |
ifHasAudio(video).then(createToggleButton); | |
}); | |
if (SERVE_WEBM_THUMB) { | |
let list = document.querySelectorAll('.js-spoiler-info-overlay:not(.hidden)'); | |
for (let ele of list) { | |
var anchor = ele.nextElementSibling; | |
var img = anchor.firstElementChild.firstElementChild; | |
if (ele.innerHTML != 'WebM' || getParent(ele, '#duplicate_reporting') !== null || !img.getAttribute('src').endsWith('.gif')) { | |
continue; | |
} | |
var videoElement = document.createElement('video'); | |
// videoElement.setAttribute('autoplay', ''); | |
videoElement.setAttribute('loop', ''); | |
videoElement.setAttribute('muted', ''); | |
videoElement.setAttribute('playsinline', ''); | |
videoElement.setAttribute('preload', 'auto'); | |
var webmSource = document.createElement('source'); | |
webmSource.setAttribute('src', img.getAttribute('src').slice(0, -4) + '.webm'); | |
webmSource.setAttribute('type', 'video/webm'); | |
var mp4Source = document.createElement('source'); | |
mp4Source.setAttribute('src', img.getAttribute('src').slice(0, -4) + '.mp4'); | |
mp4Source.setAttribute('type', 'video/mp4'); | |
videoElement.appendChild(webmSource); | |
videoElement.appendChild(mp4Source); | |
ele.parentElement.classList.add('has-webm-overlay'); | |
anchor.removeChild(anchor.querySelector('picture')); | |
anchor.appendChild(videoElement); | |
videoElement.muted = true; | |
if (!PAUSE_IN_BACKGROUND || !document.hidden) { | |
if (videoElement.readyState == videoElement.HAVE_ENOUGH_DATA) { | |
videoElement.play() | |
.then(() => { | |
console.log('video first play: already have data'); | |
}) | |
.catch((error) => { | |
console.log('Caught exception: ', error); | |
}); | |
} else { | |
videoElement.addEventListener('canplaythrough', (e) => { | |
if (PAUSE_IN_BACKGROUND && document.hidden) { | |
return; | |
} | |
let video = e.target; | |
console.log('canplaythrough event triggered'); | |
video.play() | |
.then(() => { | |
console.log('video first play: received enough data'); | |
}) | |
.catch((error) => { | |
console.log('Caught exception: ', error); | |
}); | |
}, {'once': true}); | |
} | |
} | |
} | |
} | |
if (PAUSE_IN_BACKGROUND) { | |
if (document.hidden) { | |
let videosList = document.querySelectorAll('video'); | |
for (let video of videosList) { | |
if (!video.paused) { | |
video.pause(); | |
console.log('video paused: script ran in background'); | |
} | |
} | |
} | |
document.addEventListener('visibilitychange', () => { | |
let videosList = document.querySelectorAll('video'); | |
if (document.hidden) { | |
for (let video of videosList) { | |
if (!video.paused) { | |
video.pause(); | |
console.log('video paused: page visibility change'); | |
} | |
} | |
} else { | |
for (let video of videosList) { | |
console.log(`Video paused: ${video.paused}, readyState: ${video.readyState}`); | |
if (video.paused && video.readyState == video.HAVE_ENOUGH_DATA) { | |
video.play().then(() => { | |
console.log('video played: already have data'); | |
}); | |
} else { | |
video.load(); | |
video.addEventListener('canplaythrough', (e) => { | |
if (document.hidden) { | |
return; | |
} | |
console.log('canplaythrough event triggered'); | |
video.play().then(() => { | |
console.log('video played: received enough data'); | |
}); | |
}, {'once': true}); | |
} | |
} | |
} | |
}); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment