|
// ==UserScript== |
|
// @name VideoPlaybackRate.user.js |
|
// @author Shusui Moyatani |
|
// @description Change speed of video |
|
// @namespace https://gist.github.com/syusui-s/VideoPlaybackRate.user.js |
|
// @version 3.0.2 |
|
// @match http*://*/* |
|
// @grant none |
|
// @run-at context-menu |
|
// @downloadURL https://gist.github.com/syusui-s/5db8676b21db522cedbc1028c75dddb6/raw/VideoPlaybackRate.user.js |
|
// @updateURL https://gist.github.com/syusui-s/5db8676b21db522cedbc1028c75dddb6/raw/VideoPlaybackRate.user.js |
|
// ==/UserScript== |
|
|
|
(async function() { |
|
'use strict'; |
|
const el = (name, ...args) => { |
|
const [props, c] = args[0].constructor === Object ? args : [{}, args[0]]; |
|
const children = c instanceof Array ? c : c != null ? [c] : []; |
|
const e = name ? document.createElement(name) : document.createDocumentFragment(); |
|
[...Object.entries(props || {})].forEach(([p, v]) => { |
|
if (p === 'class' || p === 'className') e.className = v; |
|
else if (p.startsWith('on')) e.addEventListener(p.slice(2), v); |
|
else if (p === 'style' && typeof v === 'object') Object.assign(e.style, v); |
|
else e[p] = v; |
|
}); |
|
const add = (c) => { |
|
let r; |
|
if (c instanceof HTMLElement || c instanceof DocumentFragment) r = c; |
|
else if (c instanceof Array) return c.forEach(add); |
|
else if (c != null) r = document.createTextNode(c); |
|
else return; |
|
e.appendChild(r); |
|
}; |
|
children.forEach(add); |
|
return e; |
|
}; |
|
|
|
const showPlaybackRateWindow = ({ setPlaybackRate }) => { |
|
const found = document.body.querySelector('#videoPlaybackRate'); |
|
if (found) found.remove(); |
|
|
|
const inputStyle = { |
|
padding: '0.25rem', |
|
outline: '1px #fff solid', |
|
border: 'none', |
|
color: '#fff', |
|
background: '#222', |
|
borderRadius: '5px', |
|
}; |
|
|
|
const buttonStyle = { |
|
...inputStyle, |
|
cursor: 'pointer', |
|
}; |
|
|
|
const playbackRateEl = el('div', { |
|
style: { |
|
textAlign: 'center', |
|
fontWeight: 'bold', |
|
fontSize: '2rem', |
|
height: '2rem', |
|
width: '4rem', |
|
} |
|
}, ['1']); |
|
|
|
const updatePlaybackRate = (value) => { |
|
playbackRateEl.textContent = value; |
|
inputEl.value = value; |
|
setPlaybackRate(value); |
|
}; |
|
|
|
const inputEl = el('input', { |
|
type: 'range', |
|
value: '1', |
|
min: '0.25', |
|
max: '4', |
|
step: '0.25', |
|
onchange: (ev) => updatePlaybackRate(ev.currentTarget.value), |
|
}); |
|
|
|
const speedButton = (value) => |
|
el('button', { style: { ...buttonStyle, flex: '1' }, onclick: () => updatePlaybackRate(value) }, value); |
|
|
|
const elem = el( |
|
'div', |
|
{ |
|
id: 'videoPlaybackRate', |
|
style: { |
|
display: 'flex', |
|
gap: '1rem', |
|
alignItems: 'center', |
|
position: 'fixed', |
|
color: '#fff', |
|
background: '#222', |
|
border: '1px solid #444', |
|
zIndex: '10000', |
|
top: '0', |
|
left: 'calc(50vw - 200px)', |
|
width: '480px', |
|
overflow: 'auto', |
|
padding: '0.5rem', |
|
borderRadius: '0.5rem', |
|
}, |
|
}, |
|
[ |
|
playbackRateEl, |
|
el('div', { style: { display: 'flex', flexDirection: 'column', flex: 1 } }, [ |
|
inputEl, |
|
el('div', { style: { display: 'flex', gap: '0.5rem', alignItems: 'center' } }, [ |
|
speedButton(0.50), |
|
speedButton(0.75), |
|
speedButton(1.00), |
|
speedButton(1.25), |
|
speedButton(1.50), |
|
speedButton(1.75), |
|
speedButton(2.00), |
|
speedButton(2.25), |
|
speedButton(2.50), |
|
speedButton(3.00), |
|
]), |
|
]), |
|
el('button', { |
|
style: { |
|
height: '2.5rem', |
|
width: '2.5rem', |
|
lineHeight: '2.5rem', |
|
fontSize: '1.5rem', |
|
textAlign: 'center', |
|
verticalAlign: 'middle', |
|
background: 'transparent', |
|
border: '1px solid #888', |
|
borderRadius: '5px', |
|
color: '#888', |
|
cursor: 'pointer', |
|
}, |
|
onclick: () => elem.remove(), |
|
}, 'X'), |
|
], |
|
); |
|
document.body.appendChild(elem); |
|
}; |
|
|
|
showPlaybackRateWindow({ |
|
setPlaybackRate: (playbackRate) => { |
|
const videos = [...document.querySelectorAll('video')]; |
|
videos.forEach((video) => { |
|
video.playbackRate = playbackRate; |
|
}); |
|
}, |
|
}); |
|
})(); |