- // ==UserScript==
- // @name YouTube ScreenShoter
- // @namespace http://tampermonkey.net/
- // @version 1.0.4
- // @description Screenshots current YT frame in full quality
- // @author DoctorDeathDDracula & Sticky
- // @match *://*.youtube.com/*
- // @license MIT
- // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
- // @grant none
- // @run-at document-start
- // @noframes
- // ==/UserScript==
-
-
- (function() {
- 'use strict';
-
- function YouTubeScreenShoter() {}
-
- YouTubeScreenShoter.prototype.storage = {
- selectors: {
- reactionButton: 'ytd-toggle-button-renderer',
- screenshotButton: "#__my-screenshot-button",
- reactionPanel: "#top-level-buttons-computed"
- },
- ids: {
- screenshotButton: '__my-screenshot-button'
- },
- icons: {
- iconBlackSVG: '<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" sodipodi:docname="iconB.svg" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" id="svg8" version="1.1" viewBox="0 0 6.3499999 6.35" height="24" width="24"><defs id="defs2"/><sodipodi:namedview inkscape:window-maximized="1" inkscape:window-y="-8" inkscape:window-x="-8" inkscape:window-height="1017" inkscape:window-width="1920" units="px" showgrid="false" inkscape:document-rotation="0" inkscape:current-layer="layer1" inkscape:document-units="mm" inkscape:cy="9.5831076" inkscape:cx="11.629344" inkscape:zoom="22.4" inkscape:pageshadow="2" inkscape:pageopacity="0.0" borderopacity="1.0" bordercolor="#666666" pagecolor="#ffffff" id="base"/><metadata id="metadata5"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><g inkscape:label="Слой 2" id="layer2" inkscape:groupmode="layer"/><g id="layer1" inkscape:groupmode="layer" inkscape:label="Слой 1"><path id="path10" d="m 0.86839843,2.5970013 0.007815,-1.72490613 1.71861757,0.006879" style="fill:none;stroke:#000;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path style="fill:none;stroke:#000;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 5.4816006,2.5933023 5.4737906,0.8684026 3.7551742,0.8752816" id="path10-5"/><path style="fill:none;stroke:#000;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 0.86810411,3.7567748 0.007815,1.7214547 1.71884519,-0.00686" id="path10-55"/><path id="path10-5-2" d="m 5.4819176,3.7604659 -0.00781,1.7214488 -1.7188442,-0.00685" style="fill:none;stroke:#000;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/></g></svg>',
- iconWhiteSVG: '<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" sodipodi:docname="iconW.svg" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" id="svg8" version="1.1" viewBox="0 0 6.3499999 6.35" height="24" width="24"><defs id="defs2"/><sodipodi:namedview inkscape:window-maximized="1" inkscape:window-y="-8" inkscape:window-x="-8" inkscape:window-height="1017" inkscape:window-width="1920" units="px" showgrid="false" inkscape:document-rotation="0" inkscape:current-layer="layer1" inkscape:document-units="mm" inkscape:cy="9.5831076" inkscape:cx="11.629344" inkscape:zoom="22.4" inkscape:pageshadow="2" inkscape:pageopacity="0.0" borderopacity="1.0" bordercolor="#666666" pagecolor="#ffffff" id="base"/><metadata id="metadata5"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g inkscape:label="Слой 2" id="layer2" inkscape:groupmode="layer"/><g id="layer1" inkscape:groupmode="layer" inkscape:label="Слой 1"><path id="path10" d="m 0.86839843,2.5970013 0.007815,-1.72490613 1.71861757,0.006879" style="fill:none;stroke:#fff;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><path style="fill:none;stroke:#fff;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 5.4816006,2.5933023 5.4737906,0.8684026 3.7551742,0.8752816" id="path10-5"/><path style="fill:none;stroke:#fff;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 0.86810411,3.7567748 0.007815,1.7214547 1.71884519,-0.00686" id="path10-55"/><path id="path10-5-2" d="m 5.4819176,3.7604659 -0.00781,1.7214488 -1.7188442,-0.00685" style="fill:none;stroke:#fff;stroke-width:.52916667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/></g></svg>'
- },
- styles: '.__my-screenshot-animation {opacity: 0 !important}'
-
- };
-
- YouTubeScreenShoter.prototype.init = function() {
- document.addEventListener('DOMContentLoaded', this.onDOMContentLoaded.bind(this));
- this.setTriggerOnElement(this.storage.selectors.reactionPanel, 'addedNodes', this.onNewPanel.bind(this));
- };
-
- YouTubeScreenShoter.prototype.cc = function(tag, options, parent, init) {
- options = options || {};
- var children = options.children || [];
- delete options.children;
- var element = Object.assign(document.createElement(tag), options);
- for (var i = 0; i < children.length; i++) element.appendChild(children[i]);
- if (typeof init == 'function') init.call(element);
- return parent && parent.nodeType === Node.ELEMENT_NODE ? parent.appendChild(element) : element;
- };
-
- YouTubeScreenShoter.prototype.ss = function(selector, searchIn, all) {
- searchIn = searchIn || document;
- return all ? searchIn.querySelectorAll(selector) : searchIn.querySelector(selector);
- };
-
- YouTubeScreenShoter.prototype.onDOMContentLoaded = function() {
- this.addStyles();
- };
-
- YouTubeScreenShoter.prototype.addStyles = function() {
- this.cc('style', {
- textContent: this.storage.styles
- }, document.head);
- };
-
- YouTubeScreenShoter.prototype.onNewPanel = function(panel) {
- this.initPanel(panel);
- this.setTriggerOnElement(this.storage.selectors.screenshotButton, 'removedNodes', (function() {
- this.initPanel(panel);
- }).bind(this), false, panel);
- };
-
- YouTubeScreenShoter.prototype.initPanel = function(panel) {
- if (panel.children.length !== 0) {
- var myButton = this.ss(this.storage.selectors.screenshotButton, panel);
- if (!myButton) {
- var reactionButton = this.ss(this.storage.selectors.reactionButton, panel);
- if (reactionButton && reactionButton.nextElementSibling) {
- reactionButton.nextElementSibling.after(this.generateButton());
- }
- }
- }
- };
-
- YouTubeScreenShoter.prototype.setTriggerOnElement = function(selector, action, callback, once, searchIn) {
- var observer = new MutationObserver(function(mutations) {
- for (var mi = 0; mi < mutations.length; mi++) {
- var mutation = mutations[mi];
- var addedNodes = mutation[action] || [];
- for (var ani = 0; ani < addedNodes.length; ani++) {
- var node = addedNodes[ani];
- var element = node.matches && node.matches(selector) ? node : (node.querySelector ? node.querySelector(selector) : null);
- if (element) {
- if (once) {
- observer.disconnect();
- return callback(element);
- } else {
- callback(element);
- }
- }
- }
- }
- });
-
- observer.observe(searchIn || document, {
- attributes: false,
- childList: true,
- subtree: true
- });
-
- return observer;
- };
-
- YouTubeScreenShoter.prototype.generateButton = function() {
- return this.cc("button", {
- id: this.storage.ids.screenshotButton,
- style: "appearance:none;border:none;outline:none;background-color:#0000;padding:0;display:flex;cursor:pointer;",
- children: [
- this.cc("div", {
- style: "padding:6px;",
- children: [
- this.SVGPacker(document.firstElementChild.getAttribute("dark") == 'true' ? this.storage.icons.iconWhiteSVG : this.storage.icons.iconBlackSVG)
- ]
- }),
- this.cc("div", {
- children: [
- this.cc("div", {
- style: this.packInlineStyle(this.getNoneDefaultStyle(this.ss("#text", this.ss(this.storage.selectors.reactionButton)))),
- textContent: "PRTSC"
- }, false, function() {
- this.style.width = "";
- this.style.padding = "10px 10px 0px 0px";
- })
- ]
- })
- ],
- onclick: (function(event) {
- var video = this.ss("video");
- var canvas = this.cc("canvas", {
- width: video.videoWidth,
- height: video.videoHeight
- });
- var context = canvas.getContext('2d');
-
- context.drawImage(video, 0, 0, canvas.width, canvas.height);
-
- if (event.ctrlKey) {
- this.cc("a", {
- download: "download",
- href: canvas.toDataURL("image/png")
- }).click();
- } else {
- if ('ClipboardItem' in window) {
- canvas.toBlob(this.onCopyImage.bind(this));
- } else {
- alert('Sorry, your browser does not support coping, try to download it by pressing screenshot button with ctrl key pressed.')
- }
- }
- }).bind(this)
- });
- };
-
- YouTubeScreenShoter.prototype.onCopyImage = function(blob) {
- navigator.clipboard.write([new window.ClipboardItem({
- "image/png": blob
- })]);
- };
-
- YouTubeScreenShoter.prototype.getComputedStyle = window.getComputedStyle.bind(window);
-
- YouTubeScreenShoter.prototype.getNoneDefaultStyle = function(node) {
- var styles = [];
- var supportElement = this.cc(node.tagName, {
- visible: false
- }, document.body);
- var elementStyles = this.getComputedStyle(node);
- var defaultStyles = this.getComputedStyle(supportElement);
- var keys = Object.keys(defaultStyles);
- for (var i = 0; i < keys.length; i++) {
- if (elementStyles[keys[i]] !== defaultStyles[keys[i]] && defaultStyles[keys[i]] !== '') styles.push([keys[i], elementStyles[keys[i]]]);
- }
- supportElement.remove();
- return styles;
- };
-
- YouTubeScreenShoter.prototype.packInlineStyle = function(style) {
- return style.reduce(function(pre, cur) {
- return pre + ";" + cur[0] + ":" + cur[1];
- }, "") + ";";
- };
-
- YouTubeScreenShoter.prototype.SVGPacker = function(SVGXML) {
- return this.cc("div", {
- innerHTML: SVGXML
- }).firstChild;
- };
-
- var YTSS = new YouTubeScreenShoter();
- YTSS.init();
-
- })();
-