您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Universal video control, Control of the first video element of the page, advance and retract and multiply speed.
// ==UserScript== // @name video play util // @namespace https://gist.github.com/zqcccc/32eb827d35b8134f32f9dd3f70d9fb26 // @version 0.0.11 // @description Universal video control, Control of the first video element of the page, advance and retract and multiply speed. // @author zqcccc // @match https://www.disneyplus.com/* // @match https://www.bilibili.com/video/* // @match https://jable.tv/videos/* // @match https://dsxys.live/player/* // @match https://web.telegram.org/k/* // @match https://www.youtube.com/watch* // @match https://kaiwu.lagou.com/* // @match https://ddys.pro/* // @match https://v.qq.com/x/* // @match https://frontendmasters.com/courses/* // @include /^https:\/\/ddys.+$/ // @include /^https:\/\/www.youtube.com\/watch.*/ // @match https://www.iole.tv/vodplay/* // @match https://sxyprn.com/post/* // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com // @grant none // @license MIT // ==/UserScript== ; (function () { 'use strict' let hasFirstInit = false let config = { selectVideoIndex: 0 } function initConfig() { config = { selectVideoIndex: 0 } } function getVideoElement() { document.documentElement.background = '#fff' const videoElements = document.querySelectorAll('video') if (!hasFirstInit) { for (let i = 0; i < videoElements.length; i++) { const videoElement = videoElements[i] if (videoElement.src) { hasFirstInit = true config.selectVideoIndex = i break } } } return videoElements[config.selectVideoIndex] || window.$0 // chrome 自己指定 } function attachKeydownEvent() { let ClosureConfig = config function addEvent() { document.documentElement.addEventListener('keydown', keydownHandle) } function removeEvent() { document.documentElement.removeEventListener('keydown', keydownHandle) } function keydownHandle (e) { if (ClosureConfig !== config) { removeEvent() return } const videoElement = getVideoElement() if (!videoElement) removeEvent() switch (e.code) { case 'Period': videoElement.currentTime += 1 break case 'Comma': videoElement.currentTime -= 2 break case 'Equal': videoElement.playbackRate += 0.1 break case 'Minus': videoElement.playbackRate -= 0.1 break default: break } } let hasAddEvent = false function check() { const videoElement = getVideoElement() if (videoElement) { hasAddEvent = true emptyHandles() addEvent() } } let intervalTimes = 0 const timer = setInterval(() => { if (hasAddEvent || intervalTimes >= 120) { clearInterval(timer) } else { check() } intervalTimes++ }, 1000) return removeEvent } function observeVideo() { const v = getVideoElement() if (v) { const ob = new MutationObserver((mutations, observer) => { mutations.forEach((mut) => { console.log( '%c mut: ', 'font-size:12px;background-color: #553E2E;color:#fff;', mut ) if (mut.type === 'attributes' && mut.attributeName === 'src') { console.log('src changed') setTimeout(() => { emptyHandles() addRemove(attachKeydownEvent()) }, 3000) } }) }) ob.observe(v, { attributes: true, attributeOldValue: true }) return () => { ob.disconnect() } } else { console.log('no video element') } } var waitRemoveHandles = [] function emptyHandles() { for (let i = waitRemoveHandles.length - 1; i >= 0; i--) { waitRemoveHandles.pop()?.() } } function insertPanel() { document.querySelector('#panel-control')?.remove() const panel = document.createElement('div') const videoElements = getVideoElements() panel.style.cssText = ` display: flex; flex-direction: column; align-items: center; justify-content: center; position: fixed; z-index: 100; width: 200px; height: 200px; background: rgba(0,0,0,0.4); color: #fff; ` panel.style.left = '0' panel.style.top = '50vh' panel.draggable = 'true' panel.setAttribute('id', 'panel-control') panel.innerHTML = `<style> #panel-control { opacity: 0.1 } #panel-control:hover { opacity: 1 } #panel-control button { background: rgba(0,0,0,0.3); border: none; color: #fff; } #refresh-panel { user-select: none; cursor: pointer; position: absolute; right: 10px; top: 10px; } </style> <div class="info"></div> ${videoElements.map((videoElement, index) => `<button class="video-select" data-index="${index}"> <div>${index}:</div> <div>${videoElement.src}</div> </button>`).join('')} <button class="get-info">info</button> <button class="add-speed">speed+0.1</button> <button class="minus-speed">speed-0.1</button> <button class="forward">+1s</button> <button class="backward">-2s</button> <button id="refresh-panel">↻</button> ` panel.querySelectorAll('.video-select').forEach((button, index) => { button.addEventListener('click', (e) => { // const index = Number(e.target.dataset.index) config.selectVideoIndex = index const videoElement = getVideoElement() if (videoElement) { panel.querySelector('.info').innerHTML = getVideoInfoText(videoElement) } } ) }) panel.querySelector('.get-info')?.addEventListener('click', () => { const video = getVideoElement() if (video) { const infoDiv = panel.querySelector('.info') infoDiv.innerHTML = getVideoInfoText(video) } }) panel.querySelector('.add-speed')?.addEventListener('click', () => { const video = getVideoElement() video && (video.playbackRate += 0.1) }) panel.querySelector('.minus-speed')?.addEventListener('click', () => { const video = getVideoElement() video && (video.playbackRate -= 0.1) }) panel.querySelector('.forward')?.addEventListener('click', () => { const video = getVideoElement() video && (video.currentTime += 1) }) panel.querySelector('.backward')?.addEventListener('click', () => { const video = getVideoElement() video && (video.currentTime -= 2) }) panel.querySelector('#refresh-panel')?.addEventListener('click', () => { init() }) let canDrag = false, lastX, lastY, panelLeft, panelTop panel.addEventListener('dragstart', (e) => { canDrag = true lastX = e.pageX lastY = e.pageY const rect = panel.getBoundingClientRect() panelLeft = rect.left panelTop = rect.top }) panel.addEventListener('drag', (e) => { if (canDrag) { const disX = e.pageX - lastX panel.style.left = panelLeft + disX + 'px' const disY = e.pageY - lastY panel.style.top = panelTop + disY + 'px' } }) panel.addEventListener('dragend', () => (canDrag = false)) panel.addEventListener('dragover', function (e) { e.preventDefault() }) document.documentElement.appendChild(panel) } function addRemove(fn) { waitRemoveHandles.push(fn) } function getVideoElements() { const videos = document.querySelectorAll('video') return Array.from(videos) } function getVideoInfoText(video) { const { src, duration, currentTime, playbackRate } = video return ` <div>src: ${src}</div> <div>playbackRate: ${playbackRate}</div> <div>duration: ${duration}</div> <div>currentTime: ${currentTime}</div> ` } function init() { emptyHandles() initConfig() const cancel = observeVideo() addRemove(cancel) insertPanel() addRemove(attachKeydownEvent()) } init() console.log('init player util') console.log('%c init player util: ', 'font-size:12px;background-color: #3F7CFF;color:#fff;', 'hello ') })()