YouTube Screenshot Button

Adiciona um botão para tirar uma captura de tela do fotograma atual do vídeo no YouTube.

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

// ==UserScript==
// @name            YouTube Screenshot Button
// @namespace       https://criskkky.carrd.co/
// @version         1.0.3
// @description     Adds a button to take a screenshot of the current video frame on YouTube.
// @description:es  Añade un botón para tomar una captura de pantalla del fotograma actual del vídeo en YouTube.
// @description:pt  Adiciona um botão para tirar uma captura de tela do fotograma atual do vídeo no YouTube.
// @description:fr  Ajoute un bouton pour prendre une capture d'écran de l'image vidéo actuelle sur YouTube.
// @description:it  Aggiunge un pulsante per scattare uno screenshot del fotogramma video corrente su YouTube.
// @description:uk  Додає кнопку для знімка поточного кадру відео на YouTube.
// @description:ru  Добавляет кнопку для снятия скриншота текущего кадра видео на YouTube.
// @description:ro  Adaugă un buton pentru a face un screenshot al cadrului video curent de pe YouTube.

// @author          https://criskkky.carrd.co/
// @supportURL      https://github.com/criskkky/YT-SS-Button/issues
// @icon            https://raw.githubusercontent.com/criskkky/YT-SS-Button/main/static/icon.svg
// @copyright       https://github.com/criskkky/

// @grant           none
// @match           *://*.youtube.com/*
// ==/UserScript==

// v1.0.3: update selectors for YouTube's latest player UI
// (".ytp-right-controls" now splits into ".ytp-right-controls-right" in some layouts).

(() => {

    const BUTTON_SELECTOR = '.ytp-screenshot-button';
    let ensureTimer = null;

    function isWatchPage() {
        return window.location.href.includes('watch?v=');
    }

    function getControlsContainer() {
        // YouTube recently introduced a "right-controls-left/right" split.
        // Prefer inserting into the actual right-side container when present.
        const player = document.querySelector('#movie_player');
        const root = player || document;

        return (
            root.querySelector('.ytp-right-controls-right') ||
            root.querySelector('.ytp-right-controls') ||
            document.querySelector('.ytp-right-controls-right') ||
            document.querySelector('.ytp-right-controls')
        );
    }

    function createButton() {
        const screenshotButton = document.createElement('button');
        screenshotButton.className = 'ytp-button ytp-screenshot-button';
        screenshotButton.setAttribute('title', 'Take screenshot');
        screenshotButton.setAttribute('aria-label', 'Take screenshot');

        const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svgElement.setAttribute('viewBox', '0 0 487 487');
        svgElement.setAttribute('width', '24');
        svgElement.setAttribute('height', '24');
        svgElement.setAttribute('fill', '#ffffff');

        const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        pathElement.setAttribute(
            'd',
            'M308.1,277.95c0,35.7-28.9,64.6-64.6,64.6s-64.6-28.9-64.6-64.6s28.9-64.6,64.6-64.6S308.1,242.25,308.1,277.95z M440.3,116.05c25.8,0,46.7,20.9,46.7,46.7v122.4v103.8c0,27.5-22.3,49.8-49.8,49.8H49.8c-27.5,0-49.8-22.3-49.8-49.8v-103.9 v-122.3l0,0c0-25.8,20.9-46.7,46.7-46.7h93.4l4.4-18.6c6.7-28.8,32.4-49.2,62-49.2h74.1c29.6,0,55.3,20.4,62,49.2l4.3,18.6H440.3z M97.4,183.45c0-12.9-10.5-23.4-23.4-23.4c-13,0-23.5,10.5-23.5,23.4s10.5,23.4,23.4,23.4C86.9,206.95,97.4,196.45,97.4,183.45z M358.7,277.95c0-63.6-51.6-115.2-115.2-115.2s-115.2,51.6-115.2,115.2s51.6,115.2,115.2,115.2S358.7,341.55,358.7,277.95z'
        );
        svgElement.appendChild(pathElement);
        screenshotButton.appendChild(svgElement);

        screenshotButton.addEventListener('click', () => {
            const video =
                document.querySelector('#movie_player video.html5-main-video') ||
                document.querySelector('video');
            if (video) captureFrame(video);
        });

        return screenshotButton;
    }

    function addScreenshotButton() {
        const controls = getControlsContainer();
        if (!controls) return false;

        if (document.querySelector(BUTTON_SELECTOR)) {
            return true;
        }

        const screenshotButton = createButton();
        controls.insertBefore(screenshotButton, controls.firstChild);
        return true;
    }

    function captureFrame(video) {
        const canvas = document.createElement('canvas');
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

        const videoTitle = document.title.replace(/\s-\sYouTube$/, '').trim();
        const filename = `${videoTitle}.png`;

        const link = document.createElement('a');
        link.href = canvas.toDataURL('image/png');
        link.download = filename;
        link.click();
    }

    function ensureButtonEventually() {
        if (ensureTimer) {
            clearTimeout(ensureTimer);
            ensureTimer = null;
        }

        if (!isWatchPage()) return;

        let attempts = 0;
        const maxAttempts = 40; // ~20s at 500ms

        const tick = () => {
            if (!isWatchPage()) return;

            const ok = addScreenshotButton();
            if (ok) return;

            attempts += 1;
            if (attempts >= maxAttempts) return;

            ensureTimer = setTimeout(tick, 500);
        };

        tick();
    }

    function initialize() {
        document.addEventListener('yt-navigate-finish', ensureButtonEventually);

        // Initial setup
        ensureButtonEventually();
    }

    initialize();
})();