您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
"S"キー連打でYouTubeのスクリーンショット保存
当前为
// ==UserScript== // @name CapTube // @namespace https://github.com/segabito/ // @description "S"キー連打でYouTubeのスクリーンショット保存 // @include https://www.youtube.com/* // @include https://youtube.com/* // @version 0.0.2 // @grant none // @license public domain // @noframes // ==/UserScript== (function() { let previewContainer = null; const addStyle = function(styles, id) { var elm = document.createElement('style'); elm.type = 'text/css'; if (id) { elm.id = id; } var text = styles.toString(); text = document.createTextNode(text); elm.appendChild(text); var head = document.getElementsByTagName('head'); head = head[0]; head.appendChild(elm); return elm; }; const createWebWorker = function(func) { const src = func.toString().replace(/^function.*?\{/, '').replace(/}$/, ''); const blob = new Blob([src], {type: 'text\/javascript'}); const url = URL.createObjectURL(blob); return new Worker(url); }; const DataUrlConv = (function() { const sessions = {}; const func = function(self) { self.onmessage = function(e) { const dataURL = e.data.dataURL; const sessionId = e.data.sessionId; const bin = atob(dataURL.split(',')[1]); const buf = new Uint8Array(bin.length); for (let i = 0, len = buf.length; i < len; i++) { buf[i] = bin.charCodeAt(i); } const blob = new Blob([buf.buffer], {type: 'image/png'}); const objectURL = URL.createObjectURL(blob); self.postMessage({objectURL, sessionId}); }; }; const worker = createWebWorker(func); worker.addEventListener('message', (e) => { const sessionId = e.data.sessionId; if (!sessions[sessionId]) { return; } (sessions[sessionId])(e.data.objectURL); }); return { toObjectURL: function(dataURL) { return new Promise(resolve => { const sessionId = 'id:' + Math.random(); sessions[sessionId] = resolve; worker.postMessage({dataURL, sessionId}); }); } }; })(); const __css__ = (` #CapTubePreviewContainer { position: fixed; padding: 16px 0 0 16px; width: 90%; bottom: 100px; left: 5%; z-index: 10000; pointer-events: none; transform: translateZ(0); /*background: rgba(192, 192, 192, 0.4);*/ border: 1px solid #ccc; -webkit-user-select: none; user-select: none; } #CapTubePreviewContainer:empty { display: none; } #CapTubePreviewContainer canvas { display: inline-block; width: 256px; margin-right: 16px; margin-bottom: 16px; outline: solid 1px #ccc; outline-offset: 4px; transform: translateZ(0); transition: 1s opacity linear, 1s margin-right linear; } #CapTubePreviewContainer canvas.is-removing { opacity: 0; margin-right: -272px; /*width: 0;*/ } `).trim(); addStyle(__css__); const getVideoId = function() { var id = ''; location.search.substring(1).split('&').forEach(function(item){ if (item.split('=')[0] === 'v') { id = item.split('=')[1]; } }); return id; }; const toSafeName = function(text) { text = text.trim() .replace(/</g, '<') .replace(/>/g, '>') .replace(/\?/g, '?') .replace(/:/g, ':') .replace(/\|/g, '|') .replace(/\//g, '/') .replace(/\\/g, '¥') .replace(/"/g, '”') .replace(/\./g, '.') ; return text; }; const getVideoTitle = function() { var videoId = getVideoId(); var title = document.querySelector('.watch-title'); var authorName = toSafeName(document.querySelector('.yt-user-info a').text); var titleText = toSafeName(title.textContent); titleText = titleText + ' - by ' + authorName + ' (v=' + videoId + ')'; return titleText; }; const createCanvasFromVideo = function(video) { console.time('createCanvasFromVideo'); const width = video.videoWidth; const height = video.videoHeight; const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const context = canvas.getContext('2d'); context.drawImage(video, 0, 0); console.timeEnd('createCanvasFromVideo'); return canvas; }; const getFileName = function(video) { const title = getVideoTitle(); const currentTime = video.currentTime; const min = Math.floor(currentTime / 60); const sec = (currentTime % 60 + 100).toString().substr(1, 6); const time = `${min}_${sec}`; return `${title}@${time}.png`; }; /* const createBlobLinkElement = function(canvas, fileName) { console.time('createBlobLinkElement'); console.time('canvas.toDataURL'); const dataURL = canvas.toDataURL('image/png'); console.timeEnd('canvas.toDataURL'); console.time('createObjectURL'); const bin = atob(dataURL.split(',')[1]); const buf = new Uint8Array(bin.length); for (let i = 0, len = buf.length; i < len; i++) { buf[i] = bin.charCodeAt(i); } const blob = new Blob([buf.buffer], {type: 'image/png'}); const url = window.URL.createObjectURL(blob); console.timeEnd('createObjectURL'); const link = document.createElement('a'); link.setAttribute('download', fileName); link.setAttribute('target', '_blank'); link.setAttribute('href', url); console.timeEnd('createBlobLinkElement'); return link; }; */ const createBlobLinkElementAsync = function(canvas, fileName) { //console.time('createBlobLinkElement'); console.time('canvas to DataURL'); const dataURL = canvas.toDataURL('image/png'); console.timeEnd('canvas to DataURL'); console.time('dataURL to objectURL'); return DataUrlConv.toObjectURL(dataURL).then(objectURL => { console.timeEnd('dataURL to objectURL'); const link = document.createElement('a'); link.setAttribute('download', fileName); link.setAttribute('target', '_blank'); link.setAttribute('href', objectURL); //console.timeEnd('createBlobLinkElement'); return Promise.resolve(link); }); }; const saveScreenShot = function() { const video = document.querySelector('.html5-main-video'); if (!video) { return; } const canvas = createCanvasFromVideo(video); const fileName = getFileName(video); const create = () => { createBlobLinkElementAsync(canvas, fileName).then(link => { document.body.appendChild(link); setTimeout(() => { link.click(); setTimeout(() => { link.remove(); }, 1000); }, 0); }); }; if (window.requestIdleCallback) { window.requestIdleCallback(create); } else { setTimeout(create, 0); } if (previewContainer) { previewContainer.appendChild(canvas); setTimeout(() => { canvas.classList.add('is-removing'); setTimeout(() => { canvas.remove(); }, 2000); }, 1500); } }; const setPlaybackRate = function(v) { const video = document.querySelector('.html5-main-video'); if (!video) { return; } video.playbackRate = v; }; const togglePlay = function() { const video = document.querySelector('.html5-main-video'); if (!video) { return; } if (video.paused) { video.play(); } else { video.pause(); } }; const seekBy = function(v) { const video = document.querySelector('.html5-main-video'); if (!video) { return; } const ct = Math.max(video.currentTime + v, 0); video.currentTime = ct; }; let isVerySlow = false; const onKeyDown = (e) => { const key = e.key.toLowerCase(); switch (key) { case 'd': setPlaybackRate(0.1); isVerySlow = true; break; case 's': saveScreenShot(); break; } }; const onKeyUp = (e) => { //console.log('onKeyUp', e); const key = e.key.toLowerCase(); switch (key) { case 'd': setPlaybackRate(1); isVerySlow = false; break; } }; const onKeyPress = (e) => { const key = e.key.toLowerCase(); switch (key) { case 'w': togglePlay(); break; case 'a': seekBy(isVerySlow ? -0.5 : -5); break; } }; const initDom = function() { const div = document.createElement('div'); div.id = 'CapTubePreviewContainer'; document.body.appendChild(div); previewContainer = div; }; const initialize = function() { initDom(); window.addEventListener('keydown', onKeyDown); window.addEventListener('keyup', onKeyUp); window.addEventListener('keypress', onKeyPress); }; initialize(); })();