4

There is an iframe on this page I want to get a screenshot from the video tag, so I have to reach video tag that in the iframe tag.

When I open the console and run this code:

 const videoElement = document.getElementsByTagName('iframe')[0]
                 .contentWindow.document.getElementsByTagName('video')[0];

//Extracting picture from video tag
    const canvas = document.createElement('canvas');
        canvas.width = videoElement.videoWidth;
        canvas.height = videoElement.videoHeight;
        canvas.getContext('2d').drawImage(videoElement, 0, 0, canvas.width, canvas.height);

This error has been thrown:

Uncaught DOMException: Blocked a frame with origin "https://developers.google.com" from accessing a cross-origin frame.
    at <anonymous>:1:57

Also, I checked this question

My question is how can I get a screenshot from YouTube Player API?

Muhammed Ozdogan
  • 2,909
  • 3
  • 25
  • 38
  • You say that you checked the question you linked to, but it directly addresses your concern - you **cannot** interact directly with the content of an iframe that is from a different origin. If you could, that would wreck the security of the internet. Also, why not just use the official API, which allows you to get thumbnails / screenshots? - https://stackoverflow.com/a/2108248 – Joshua T May 24 '19 at 23:08
  • I need to take a screenshot from a specific time lets say video length is 100 second, I would able to take a screenshot at 44'th second. But thumbnails are static – Muhammed Ozdogan May 24 '19 at 23:40
  • That makes sense, but that doesn't change the fact that you simply can't do with that embeds by themselves (your main question). Your options are basically - A) Download the video and use something like FFMPEG to extract the frame, B) use something like [puppeteer](https://blog.georgi-yanev.com/projects/youtube-timestamp-screenshot/) to run a headless Chrome and screenshot the video, or C) use a screenshot-api service like [URL2PNG](https://www.url2png.com/) and pass it the embed URL as the page to screenshot. – Joshua T May 25 '19 at 07:14
  • Option A looks perfect apart of downloading the video I think vlc can play youtube video from network stream if I could open vlc from command line with url parameter time parameter it can produce thumbnails dynamicaly: https://www.videosolo.com/tutorials/download-video-with-vlc.html – Muhammed Ozdogan May 25 '19 at 09:07
  • @Joshua T Can you check out this question: https://askubuntu.com/questions/796575/how-to-force-command-line-vlc-to-stream-youtube-videos-of-specific-quality I mean vlc can open youtube conections If I could extract the frames as images with vlc it would be great. – Muhammed Ozdogan May 25 '19 at 09:16
  • I would check out [this doc page](https://wiki.videolan.org/How_to_create_thumbnails/) for a much better example. This code should work in bash - `vlc "https://www.youtube.com/watch?v=bHQqvYy5KYo" --rate=1 --video-filter=scene --vout=dummy --start-time=60 --stop-time=61 --scene-ratio=30 --scene-format=png --scene-prefix=screengrab_ vlc://quit` – Joshua T May 25 '19 at 09:45
  • Joshua T I could not achieve to run that code it gives multiple errors even in a video that in my file system did you run it? – Muhammed Ozdogan May 25 '19 at 16:36
  • Also what about the webrtc if I get screenshot with webrtc while video full screen mode can it capture contains of Iframe because when I try it with html2canvas library it shows blank the content of iframe – Muhammed Ozdogan May 25 '19 at 16:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/193928/discussion-between-joshua-t-and-muhammed-ozdogan). – Joshua T May 25 '19 at 16:48

1 Answers1

8

As far as I could find, there is no way to take Screenshots from YouTube Player API as it is based on iFrame. If you want to make them in your own app (not just a browser extension), this operation will be forbidden by CORS (the cause of of the exception that you get).

The only workaround is to put YouTube video as a source into video HTML element using data that you can obtain from YouTube. This code should be handy to get the source urls for video:

class YoutubeVideo {

    constructor(video_id, callback) {
        return (async () => {
            // You should also redirect those requests
            // through your own API that would permit CORS
            const response = await fetch(`https://www.youtube.com/get_video_info?video_id=${video_id}`, {
                headers: { 'Content-Type' : 'text/plain'}
            });
            const video_info = await response.text();
            
            let video = this.decodeQueryString(video_info);
            if (video.status === 'fail') {
                return callback(video);
            }
            
            if (video.url_encoded_fmt_stream_map) 
                video.source = this.decodeStreamMap(video.url_encoded_fmt_stream_map);

            return callback(video);
        })();
    }

    decodeQueryString(queryString) {
        var key, keyValPair, keyValPairs, r, val, _i, _len;
        r = {};
        keyValPairs = queryString.split("&");
        for (_i = 0, _len = keyValPairs.length; _i < _len; _i++) {
            keyValPair = keyValPairs[_i];
            key = decodeURIComponent(keyValPair.split("=")[0]);
            val = decodeURIComponent(keyValPair.split("=")[1] || "");
            r[key] = val;
        }
        return r;
    }

    decodeStreamMap(url_encoded_fmt_stream_map) {
        var quality, sources, stream, type, urlEncodedStream, _i, _len, _ref;
        sources = {};
        _ref = url_encoded_fmt_stream_map.split(",");
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            urlEncodedStream = _ref[_i];
            stream = this.decodeQueryString(urlEncodedStream);
            type = stream.type.split(";")[0];
            quality = stream.quality.split(",")[0];
            stream.original_url = stream.url;
            stream.url = "" + stream.url + "&signature=" + stream.sig;
            sources["" + type + " " + quality] = stream;
        }
        return sources;
    }
}

The object passed to the callback in the constructor would have source property that contains source links for all the available video types and qualities, you can examine them better at your browser's console. Nevertheless, not all YouTube videos can be handled that way, I have met files with further restrictions when all you can get is forbidden error or empty sources.

Resource that helped me to find this solution: https://github.com/endlesshack/youtube-video

Resource that works based on this solution: http://youtubescreenshot.com/

Proof of concept simple Web App based on expressjs server: https://github.com/RinSer/YouCut

Serj DuKareff
  • 101
  • 1
  • 4