Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iframe youtube api #2856

Open
rakirox opened this issue Dec 23, 2014 · 30 comments
Open

Iframe youtube api #2856

rakirox opened this issue Dec 23, 2014 · 30 comments
Assignees

Comments

@rakirox
Copy link

rakirox commented Dec 23, 2014

Hi there I'm new to this js/html->desktop app world and I'm trying to have some youtube iframe in my app. This is the error I get with some videos:

*This video contains content from . It is restricted from playback on certain sites

First I thought it was some country/block from youtube. Then I realize it was not. I did run an iframe (with the same videoID) on my server and worked well. So I started doing my research and found this issue #1376 which looks exact the same problem I have. As @szwacz said it looks like the youtube API can't get the HEADER_REFERER and blocks the video.

I did a work arround doing something like this: myserver.com?videoID=somevideo and it's working. But I don't really like this method looks bad from my perspective so any ideas? let me know If you need code or anything.

I do use this example: https://developers.google.com/youtube/iframe_api_reference#Getting_Started
one of the videos which are being blocked: pT68FS3YbQ4

Thanks for your time and sorry for my bad english

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@cTn-dev
Copy link

cTn-dev commented Jan 10, 2016

Hi @rakirox , i know that this ticket is super old, but have you by any chance figured out a better way of solving the "It is restricted from playback on certain sites" problem ?

@rakirox
Copy link
Author

rakirox commented Jan 12, 2016

Hi @cTn-dev
Indeed, my working around is a crossover Js (I don't remember if it's the correct term). I did put my youtube Iframe on my server side and expose its controller so I can access to it by putting an Iframe in my app from my server

APP->Iframe (to my server) -> (that has) YoutubeIframe
and in my server I did a JS that can be accessed from my APP and execute commands to stop, play, pause, change video,etc.. let me see if I did upload to git my project I don't remember

@cTn-dev
Copy link

cTn-dev commented Jan 13, 2016

Take your time, i would really like to see how you solved it, i am also wondering if you are able to listen to onPlayerStateChange event with this approach, because if you could, that would be amazing.

Thanks

@rakirox
Copy link
Author

rakirox commented Jan 15, 2016

Hey @cTn-dev , sorry I have been busy at work, I don't have the git in this computer, but this weekend I'll upload it.
And yes, the XSS let you do this kind of things https://en.wikipedia.org/wiki/Cross-site_scripting
just be aware of the security risks.
I'll let you know here when I upload my project

@Skhmt
Copy link

Skhmt commented Jan 21, 2016

So any way to do this without XSS?

I think a fix for this requires modifying chromium for nw.js.

I tried a sort of hack, creating the iframe, then running:

var iframe = document.getElementById("player");
iframe.contentWindow.window.document.__defineGetter__('referrer', function () {
    return "https://www.youtube.com";
} );
iframe.src = "https://www.youtube.com/embed/u9Dg-g7t2l4?enablejsapi=1"

No luck :( Setting the referrer doesn't mean it gets sent.

Another documented case:
http://www.davidtan.org/embedded-youtube-videos-restricted-playback-on-certain-sites-when-no-http-referrer-sent/

@Skhmt
Copy link

Skhmt commented Feb 6, 2016

@rakirox did you get a chance to upload your project?

@Skhmt
Copy link

Skhmt commented Feb 9, 2016

Ok well here's how I got it working via XSS. I couldn't get it working with any combination of anything else.

Parent page in nw.js has something like: <iframe id="myframe" src="http://foobar.github.io/youtube.html"></iframe>

Then in a script tag or something of the parent page:
var frame = document.getElementById("myframe").contentWindow;
You can then call frame.foobar(); or whatever from the iframe's global scripts.

As I couldn't really send events up to the nw.js app from within the frame, I just polled the getPlayerState() every half a second or so.

The frame page's scripts looked like this (the youtube api of course being included earlier):

var ytPlayer;
function onYouTubeIframeAPIReady() {
    ytPlayer = new YT.Player('ytPlayer', {
        videoId: 'vxIOUJ7by6U',
        playerVars: { fs: 0, rel: 0, modestbranding: 1, iv_load_policy: 3, controls: 0 },
        events: { }
    } );
}

and the body is this:

<body style="width: 100vw; height: 100vh; padding: 0; margin: 0;">
    <div id="ytPlayer" style="width: 100%; height: 100%"></div>
</body>

I have the frame page hosted on my github.io site because it doesn't need any serverside scripting and doesn't need a youtube/google API token.

@cTn-dev
Copy link

cTn-dev commented Feb 10, 2016

You can send events to the app from the iframe through parent.postMessage(data, '*')
It works surprisingly well.

@Skhmt
Copy link

Skhmt commented Feb 10, 2016

Doesn't it have to be in the same domain?

@cTn-dev
Copy link

cTn-dev commented Feb 10, 2016

Not as far i know, i have the html/js that uses the iframe api on a web server, then in my nwjs app i use an iframe to load the content from the webserver that contains the youtube player, and i use parent.postMessage to pass messages between the player and my app.

@Skhmt
Copy link

Skhmt commented Feb 10, 2016

The server doesn't have the same host as the app, right? Like you're not running both from your desktop?

@rakirox
Copy link
Author

rakirox commented Feb 11, 2016

sorry for being so late but I have been busy at work.
Good news I found the project I told you guys I had my repo in private. https://github.com/rakirox/Devmonsters-Coop
Bad News I don't have th server side code or the android code :s @Skhmt @cTn-dev

I'll try to find the code part. Sorry for my bad JS code but in that time I was jerking arround with that project

@rakirox
Copy link
Author

rakirox commented Feb 11, 2016

@rakirox
Copy link
Author

rakirox commented Feb 11, 2016

as far as I remember I used to load in this way
https://github.com/rakirox/Devmonsters-Coop/blob/master/js/jPlayer/demo.js#L211
I think the code explains itself

@rakirox
Copy link
Author

rakirox commented Feb 11, 2016

and yes... I have miaw variables...

@Skhmt
Copy link

Skhmt commented May 12, 2016

Thanks Rakirox, sorry about the late response.

This bug still exists in nwjs v0.14.5 LTS (tested just now).

@frankhale
Copy link
Contributor

frankhale commented May 12, 2016

Reference:

This video contains content from *. It is restricted from playback on certain sites

Okay so I just saw this issue and I struggled with it for a while when I was writing my YouTube player for Electron. The way I went around it was to patch Chromium to allow me to set the HTTP Referrer via a command line switch. This worked really well and solved the problem but I never ended up submitting a pull request to Electron to add this feature.

The second method I used to get around this problem has nothing to do with setting the HTTP Referrer but instead put my app behind a traditional web app (in this case Express) and magically the "Restricted playback..." error goes away. So rather than patching Chromium I just elected to change the architecture of my app. I no longer just load code from the file system, I instead start an Express server when I run NW.js and serve my app in a traditional web app fashion. My app is solely a YouTube player so any videos that won't play is a very important issue for me to fix and this has solved it for me.

https://github.com/frankhale/toby

@Skhmt
Copy link

Skhmt commented May 23, 2016

So if I understand this correctly, your main html page runs an express server then your main html page navigates a full size iframe to the page you just hosted locally, and that fixes the problem?

@frankhale
Copy link
Contributor

My main index.html starts an Express server, then I have a webview that loads up the default route of my Express server. The page loaded in the webview contains the YouTube API iframe. In the cases where I have observed blocked content (eg. videos from VEVO) the problem goes away and the videos are viewable.

@Christywl
Copy link
Contributor

Is this issue still reproduced on the latest nwjs build?

@Skhmt
Copy link

Skhmt commented Nov 6, 2017

Issue still exists on nw.js 0.26.3.
This video won't play:

<html><body>
	<iframe width="420" height="315"
	src="https://www.youtube.com/embed/wIft-t-MQuE">
	</iframe>
</body></html>

@Christywl
Copy link
Contributor

I reproduce this issue with the sample on nwjs-sdk-v0.26.3.

@Skhmt
Copy link

Skhmt commented Jan 31, 2018

It's been two years but I found another workaround besides XSS.

The basic idea is to refresh the youtube iframe after loading so the referer header is set to the youtube page itself rather than null, which is causing the issue at hand. This is a less ugly hack than spinning up a server or using a hosted page somewhere else to serve an iframe, but it's still kinda janky.

In the example below, onReady is called after the iframe is ready, then the hack is to refresh the iframe, then onReady is called again. So it's "actually" ready the second time.

let refreshed = false;
function onYouTubeIframeAPIReady() {
	ytPlayer = new YT.Player('ytPlayer', {
		videoId: 'XIMLoLxmTDw', // 10 hr black screen
		playerVars: {
			fs: 0,
		},
		events: {
			'onReady': () => {
				// refresh the page once before using it to set the HTTP Referer to itself
				if (!refreshed) {
					document.getElementById('ytPlayer').contentWindow.location.reload();
					refreshed = true;
				}
				else {
					// player is now ready for use
				}
			}
		},
	});
}

@frankhale
Copy link
Contributor

This is a less ugly hack than spinning up a server or using a hosted page somewhere else to serve an iframe, but it's still kinda janky.

Totally agree. Yeah I'm spinning up a server to literally get around this issue in my own app. I know that on Electron they added an API so you can modify HTTP headers so this is no longer an issue there. I don't know if there is anything like that planned for NW.js. I'd think that modifying headers is sort of a niche thing that a lot of people probably wouldn't need, maybe I'm wrong.

@lePouns
Copy link

lePouns commented Feb 12, 2018

Hi everyone and thks @frankhale for your hack

Unfortunately, reloading the iframe remove the player's listeners, so I don't get the onReady and onStateChange events

Any tricks to solve that ?

Thks in advance

@Skhmt
Copy link

Skhmt commented Feb 13, 2018

It works for me, the code example I posted was copy-pasted from a working implementation.

@lePouns
Copy link

lePouns commented Feb 13, 2018

Problem is that i need to know when the iframe is fully reloaded.

Because adding the "onStateChange" listener right after the contentWindow.location.reload(); doesnt work because the iframe is not loaded.

So for away, this is the only trick i found but thats not clean

setTimeout(function(){
		player.addEventListener("onStateChange", "onPlayerStateChange");
		player_ready = true;
	}, 1500);

Any idea on how to detect the iframe was fully reloaded ?

Thks

@stefanderaadt
Copy link

I got my player working like this:

			var youtube = document.getElementById('youtube')
			var ready = false;

                        //iframe ready event
			youtube.onload = function(){
				if (!ready){
					youtube.contentWindow.location.reload();
					ready = true
				}

			}

@lePouns
Copy link

lePouns commented Feb 27, 2018

Thanks guys

I got it working in the past days and suddenly, a new problem occured. The onReady event isnt't triggered anymore ... Didn't change anything before this issue happend

Does anyone else have the problem ?

Thks
Gilles

@sirisian
Copy link

sirisian commented Feb 2, 2021

By the way this can be fixed trivially using:

chrome.webRequest.onBeforeSendHeaders.addListener(function(details) {
	for (let index in details.requestHeaders) {
		if (details.requestHeaders[index].name === "Referer") {
			details.requestHeaders[index].value = referer;
			return { requestHeaders: details.requestHeaders};
		}
	}
	details.requestHeaders.push({ name: "Referer", value: 'https://example.com' });
	return { requestHeaders: details.requestHeaders };
}, { urls: ["<all_urls>"] }, ["blocking", "requestHeaders", "extraHeaders"]);

Could probably restrict the URLs maybe, but this allowed videos that said "Video unavailable" to start working. The big picture is edit all outgoing requests to have a Referer defined. Youtube is happy with any Referer set.

I will say that nw.js probably should handle this as silly as it sounds. This error is rather cryptic and for some developers might not know what to search to find the solution as it only happens on a few youtube videos. (With no documented reason I don't think).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants