01 January 2021
Last weekend was the perfect time to hack something that bugs me for a while. At home, one of my RaspberryPI is serving all my media via an Apache server. It’s simple and reliable. The main use is to stream content to the Chromecast. The workflow isn’t amazing but suits me:
.mp4
So far it’s ok, but it gets tricky when it comes to stream an .mkv
file because this format isn’t natively supported by Google Chrome. Opening it will start the download instead of showing the video player. In this case, the workflow is to:
Play
After writing it down, it doesn’t sound like a big hassle, but damn it’s annoying. Especially if you plan to watch a ton of short videos.
The solution in mind: hack the template engine of Apache that generates the HTML indexes to inject the Cast script.
Apache indexes: look at this beauty
Sounds fairly simple and easy, but after some research it isn’t. The only customisation that Apache allows is adding a style file. Back to square one.
What about a single page app! Let’s imagine, place a HTML file somewhere on your Apache folder. By being served from the same domain, it can make requests to other folders and parse the index.html
then recreate the directory navigation. It sounds ideal: a simple html file which does all the magic, and can be placed on any server without touching the Apache config.
Along the way, everything seemed fine. The parsing was simple to do, the style was under my control and the list of files was filtered to show video files only.
Then comes the Chromecast integration. Back in the days (2015, the early days of the Chromecast) my pet projects consisted of adding the Chromecast functionality to my favourite french TV channels, by a simple script injection. So it felt like the Cast part would be a piece of cake. Well…
In the meantime the Cast API has changed. It’s still retro compatible but the place is no longer familiar to my memories. My anger reached the sky when the basic tutorial from the docs wasn’t working on my environment. Alas, my old code from back in the days wasn’t working either. It was clunky, sometimes it was working, sometimes it did not. It’s only after reading the docs again that my attention stopped on:
Warning: Chrome sender apps need to support HTTPS to maintain Cast compatibility, as Chrome is deprecating support for the Presentation API on insecure origins.
Obviously my Apache server isn’t running on HTTPS. My first thought was to abandon here, sadly I’m stubborn as f***. Here are the other directions I tried to run into:
Running my Apache server on HTTPS without certs?
NO, the Cast API boots but never streams. It might consider that the domain is not secure enough.
Use Let’s encrypt to generate a cert and use it on my server?
NO, forget about it, I’m too lazy for that bullshit. It’s a RaspberryPI on my local network, not a production platform. Plus I would have to go through all of this mess again if I reset my PI.
Let my Apache server accept CORS requests so the SPA can request it while being hosted on HTTPS?
NO, it’s not allowed to do requests to a HTTP server from HTTPS. Frustrating, but makes completely sense, duh!
Using an iframe, stored on a valid secure domain, to send the URL to stream via message?
NO, the Cast won’t start without a genuine user click.
Same but with an icon, integrated on the main UI so the user can click on and start the stream?
NO, the Cast won’t start because the parent window isn’t ‘secure’. I reached that conclusion after embedding a YouTube video on a HTTP domain: the Cast option isn’t displayed.
Single page app hosted on HTTP to browse the server, with video link opening a new tab to a page ready to cast your media?
Yes, finally yes. If you think about it, it’s just streamlining the original workflow. It’s pretty simple and actually the cast part can be reused by anyone.
If you want to stream a URL to a Chromecast without hassle: fire a new tab to https://maxwellito.github.io/apachecast/cast.html#[URL_TO_CAST]
. There’s just one big button. Once the receiver is selected, the stream starts.
The Apachecast source code is available on GitHub.
So what lessons to get from this:
Remember that Chrome considers localhost
differently than other domains while on HTTP. It lets you use HTTPS reserved features (like Cast API and PWA) even if it doesn’t meet the security requirements. Which might make you feel that everything works, until you test in real conditions.
The API could be more helpful and provide the real reasons why things don’t work, and not just No cast extension found
(especially since no extension is longer required). There are so many failing reasons but without information it felt like debugging a web page on IE6.
Next time I will install Plex and shut the fuck up.