Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save marktaiwan/2349cbcfc2835d23d3eea6ac32e51de2 to your computer and use it in GitHub Desktop.
Save marktaiwan/2349cbcfc2835d23d3eea6ac32e51de2 to your computer and use it in GitHub Desktop.
// ==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