Make YouTube embed continue to play on layout change?

Hello Hype Forum friends!

I am trying to get a YouTube video embed working so that it continues playing when the layout has changed from mobile to desktop size on a desktop browser, or when someone changes the device orientation on mobile. Unfortunately, I have not been able to get this to work.

In looking for solutions, I’ve found info on how to stop audio of a YouTube video embed when the layout switches. But nothing on how to make the video (and audio) continue when the layout switches.

I was thinking that I might be able to get it to work if I turn the HTML Widget into a symbol.
So, I made a test Hype file that emulates the setup that I have on the website I’m building. It has 2 layouts (mobile and desktop) and 3 scenes:

  • The first scene has a YouTube video embed that is just a regular HTML Widget Element.
  • The second scene takes the HTML Widget Element and makes it into a symbol.
  • The third scene presents the HTML Widget Element as a persistent symbol.

My findings are that on mobile: scene one and scene two keep the video playing when the device orientation is changed (but the device orientation can only be changed once before the vid stops playing). However, none of these solutions worked as intended on desktop (turning the HTML Element into a symbol did not make it so that the YouTube vid would keep playing when the browser was resized to a different layout).

Is this even possible to do? I’d just like visitors to the site to be able to watch the embedded YouTube video as it seamlessly continues to play, even if they resize their browser or change their device orientation back and forth (just like it is if they watch a YouTube video on the YouTube app or directly on a web browser. How can I recreate this behavior in Hype?

Here is the test Hype File I’ve been working with:
NN Responsive Vid Playback Tests.hype.zip (83.3 KB)

I would so appreciate any thoughts on how I might be able to get this to work!
All the best.

1 Like

I was under the impression that a persistent symbol does the job as long you add it to every scene/layout.

If it stops you could always embed it enabling the Youtube API and just triggering play. If it completely resets the persistent symbol is indeed not working as intended/advertised.

1 Like

Hi MaxZieb!

Thanks so much for your input.

I was also thinking that making the YouTube element a persistent symbol should take care of it (I even have it set to display on top during scene transition). But unfortunately it still doesn’t work - it always resets on layout change (I tested it on both mobile and desktop browsers and neither work).

I briefly looked into the YouTube API and it looks a little daunting. Is that something you have worked with and how would I implement it? Would I still need to embed the code in an HTML Element? It’s just not something I’ve worked with before.

1 Like

I thought I posted something on this issue before but cannot find it or if I solved it.

I know my idea at the time was to use the youtube API, and in ( all) layout onloads , monitor the ‘onStateChange’ and capture the current video time.

Then on ( all) layout unloads and within a timeout, have the player seekTo that time and play.

I did find one old file and can see there were still issues,

A bit of a gap in playing and testing in preview constantly get CORS issues.

1 Like

Thanks so much for your input Mark!

Too bad to know that even with using the YouTube API and seeking time to play, gaps and issues were still there. Judging from your past experience with this, it seems like there is likely going to be no good way to make this sort of playback work seamlessly with no gaps. Bummer! However great to know as it has saved me lots of time in exploring that path.

Just still confused as to why persistent symbols work so well with other elements and animations, but mysteriously restart with a video embed.

Still, thanks so much for the very helpful input.

1 Like

I think it is more the youtube embed recognising a changes and doing it.
There may be a trick out there somewhere …

1 Like

I also thought this had been covered before, but I don't think there's a solution I found:

Persistent symbols themselves are elements within the scene. When the element moves out of its parent div in preparation for moving into another parent, the browser does all sorts of cleanup that may make the video stop playing unfortunately.

1 Like

This calls for a solution that uses the dimensions of a Hype container and scales an external DIV (that could contain the Videoplayer, auto generated by JS or manually appended) to size on layout or scene changes. That would fix the problem. Only downside, as far as I can imagine, it would have the video sit always on top.

1 Like

Hi Jonathan,

Many thanks for the explanation on why the vid stops playing. Although just to be clear, I don’t need the vid to keep playing across scene changes (there is only one scene in the whole site), I just need the vid to keep playing when the layout changes.

On mobile (where the site is built in a portrait orientation), when the vid is clicked and starts playing full screen, the normal inclination is for the user to switch to a landscape orientation in order to better view the vid. This orientation change stops the vid playback.

Then on desktop, when the browser is resized and the new layout is loaded, the vid stops and the audio keeps playing. I can stop the audio playback (by inserting code that wipes the inner HTML clean on layout unload), and I don’t really mind the vid stopping on desktop.

It’s mainly on mobile that is the issue (where a user would expect to be able to seamlessly change device orientations while continuing to watch a vid).

The fact that mobile and desktop browsers handle this vid playback differently seems to add another layer of trickiness in finding a solution. Nonetheless, many thanks for the input!

Thanks Max!

I’d have no issue with the video always sitting on top - in fact it would be desirable.

My main question would be:
How tricky would it be to manually append this external DIV (and video player) and have it work well with Hype’s layout switches?

Have you done this sort of external appending of elements before outside of Hype and would it be potentially opening a can of worms or not so bad? I’d be concerned that it might break something else or be difficult to control the layout of the various “layouts”. Just not something I’ve done before and wondering how bad it would be?

Thanks so much for the input!

(note: a layout is actually another scene under-the-hood, it just has special ways to switch to it)

I don't think this would be too bad. The approach I'd take would be to have a dummy div on the scene/layout itself, and then On Scene Load you could identify this, and move or make the div existing outside of Hype to match its position and fill in the contents if need be. You can also respond to the HypeLayoutRequest callback and on a slight delay use that as a cue to change positions. I've made an example here:

MatchYouTubeToDummyView.hype.zip (63.5 KB)

Basically all you need to do is add a class name of "dummyView" to any elements that you want to use this view. Then for any that should be the same, make sure their Inner HTML is identical.

Then just add this code to the Head HTML:

<script>

function matchDummyViewCallback(hypeDocument, element, event) {
	// first, determine if this scene uses a dummy view
	var sceneElement = document.querySelector('#'+hypeDocument.documentId()+' > .HYPE_scene[style*="block"]'); // thanks @MaxZieb
	var dummyView = null;
	var dummyViews = sceneElement.getElementsByClassName("dummyView");
	if(dummyViews.length > 0) {
		dummyView = dummyViews[0];
	}
	
	// next, retrieve our topView if it exists
	var topView = document.getElementById("topView");
	
	// if we have a top view but no dummy view, then remove the topView.
	// Then return as we are done.
	if(dummyView == null && topView != null) {
		topView.innerHTML = "";
		topView.parentNode.removeChild(topView);
		return;
	}
	
	// if we have a dummy view but no top view, then let's make the top view
	if(dummyView != null && topView == null) {
		topView = document.createElement("div");
		topView.id = "topView";
		topView.style.position = "absolute";
		topView.style.zIndex = "1000000";
		document.body.appendChild(topView);
	}
	
	// match the position of the dummy view to the top view
	var dummyViewFrame = dummyView.getBoundingClientRect();
	topView.style.top = "" + (dummyViewFrame.top + window.pageYOffset) + "px";
	topView.style.left = "" + (dummyViewFrame.left + window.pageXOffset) + "px";
	topView.style.width = "" + dummyViewFrame.width + "px";
	topView.style.height = "" + dummyViewFrame.height + "px";
	
	// copy over the inner HTML if it is different than what we have
	// we use a cache (global) for comparison as it could be that this later gets changed
	if(dummyView.innerHTML != window.topViewCachedInnerHTML) {
		window.topViewCachedInnerHTML = dummyView.innerHTML;
		topView.innerHTML = dummyView.innerHTML;
	}
	
	// change opacity of dummyView to 0 since we don't need to show it
	// we don't want to clear out the inner HTML though because it is used for comparison
	dummyView.style.opacity = "0";
	dummyView.style.pointerEvents = "none";
	
}

function resizeCallback(hypeDocument, element, event) {
	window.setTimeout(function () {
		matchDummyViewCallback(hypeDocument, element, event);
	}, 0);
}

if("HYPE_eventListeners" in window === false) {
		window.HYPE_eventListeners = Array();
}

window.HYPE_eventListeners.push({"type":"HypeSceneLoad", "callback":matchDummyViewCallback});
window.HYPE_eventListeners.push({"type":"HypeLayoutRequest", "callback":resizeCallback});


</script>
6 Likes

… and if you want to get really fancy one could use the MutationObserver you suggested once to me. Probable downside… compability. Nice work!

1 Like

Wow, that’s just awesome.

Thanks so much @jonathan - this works like a charm!!

Also thanks for the clarification on layouts actually being scenes under the hood - always nice to know what’s actually going on, so thanks for that.
And thanks for the instructive code commentary as well (likewise, many thanks for your input and assistance as well, @MaxZieb!).

Thanks again guys! :slight_smile:

3 Likes

Ok, so I wanted to do a quick follow up.
I tried implementing the suggestions into my project, but unfortunately my results were mixed. It seems the ‘DummyView’ code makes my flexible layout setting get wacky when the video is playing and the browser is resized. It’s seems like the video placement gets popped out of place.

The great news is, the video now plays seamlessly when scenes (layouts) change. And if the video is not playing, browser resizing seems to have no effect and things stay in place as intended.

Here is a link to view a 45 second screen vid demonstrating the issue. In the vid, I start and stop the YouTube video while resizing the browser. By the end of the clip you can see how the video container has dislodged and sits over the title header, blocking everything below.

I’ve also attached the Hype file I’m working with below. Do either of you have any thoughts on how I can integrate this awesome code fix while at the same time not break my responsive layout settings?

The full site (the one that has the video restart issue) is currently up at https://www.nowherenation.net/ if you’d like to check it out for reference.

Thanks again for the help. I would so love to get this working so that the video plays seamlessly and the layout also stays intact. Is this possible?

Here is the Hype file:
OmicronLandingPage5.hype.zip (1.4 MB)

Oh I think what is happening is the code isn’t accounting for the scroll of the page, and that is messing up the boundaries. The fix is to change these two lines:

topView.style.top = "" + dummyViewFrame.top + "px";
topView.style.left = "" + dummyViewFrame.left + "px";

to:

topView.style.top = "" + (dummyViewFrame.top + window.pageYOffset) + "px";
topView.style.left = "" + (dummyViewFrame.left + window.pageXOffset) + "px";

I’ve corrected the code above and example doc; let me know if that works for you.

1 Like