Jon Ellis

The musings of an "engineer"

Jon Ellis

Jellyfin Electron

Since my last blog post about writing a simple Electron app to wrap the Emby web interface so I could use my keyboards media keys to control the Emby playback, I have started using Jellyfin instead of Emby. I was somewhat expecting to have to deal with problematic differences during the switch, and I event kept my Emby instance running just in case I wanted to reverse my decision.

I think that if you're already using a large number of the Emby features that it has to offer - like a fully integrated iOS app, the Emby Theatre Electron app (which has a different interface than the main web interface for some reason), then you may be better off sticking with Emby and taking out a subscription. However, an iOS app that simply wraps the Jellyfin web interface is fine by me. Now, about that Electron app.

Existing Jellyfin desktop clients

There are a couple of similar sounding projects that I assumed would make my Electron app superfluous.

Jellyfin Desktop

I'm sure Jellyfin Desktop was named Jellyfin Theatre Electron until very recently.

This app behaved really strangely when I tried to use it on macOS. Half the time I couldn't get the window to stay still, and the only way to move it around was expand the window from one side, and shrink it back down on the other.

A way to make the Jellyfin header bar draggable is to add this CSS snippet to the Custom CSS under the General settings:

Jellyfin custom CSS
.skinHeader { -webkit-app-region: drag; }
.skinHeader button { -webkit-app-region: no-drag; }

It makes the header bar draggable, but prevents the buttons within it from also being draggable, so you don't accidentally drag the window while clicking buttons. It does not have any other effects when rendered in a normal browser.

This repository had not been updated since August 2019, so it looked pretty dead. As I wanted to get my media key integration back again, I didn't fancy trying to get involved with a currently broken, perhaps abandoned project when I had the majority of something that I could get going with. There was another project to check out first too.

Jellyfin React Client

Jellyfin React Client seemed quite interesting - not really how I envisioned a desktop client to work - why build an entirely new interface when Jellyfin has a perfectly functional web interface. Why not channel improvements into that single interface?

Anyway, this project was also pretty old, and had been developed as far as allowing a user to log in to a Jellyfin server, then nothing more. Perhaps this would be a good project to get involved with in future once I get something up and running ASAP?

Converting Emby Electron to Jellyfin Electron

As I had not found a ready-to-go replacement for a desktop app for Jellyfin, and had already written a functioning Electron app wrapper around the Emby web interface, I decided to convert the private Emby Electron application to Jellyfin Electron. In theory that should be pretty straight forward - especially as Jellyfin was essentially an older version of Emby. And even better, it might be useful to someone else without stepping on someone's toes!

I decided to start a new project to implement the Jellyfin Electron app, rather than just find/replacing Emby with Jellyfin. I was certain that I would be able to make some improvements as I implemented each piece of the app, but would be able to liberally copy and paste bits that did not need to change. I had already identified a few when I read through the code when I wrote the blog post about it.

The main change I made was to cease extending the BrowserWindow class for my window instances. This is mostly because the docs say you shouldn't do it (though I found no problem with it on macOS at least). Instead the window objects wrap a BrowerWindow instance and pass through any events and function invocations that were previously available directly on the extended window object.

I also realised that I could simply use the config object in the various places that needed it, and the same instance (or at least the same config variables) would be returned and set using its functions. This meant I didn't have to pass the config object down though to where it was used.

I altered the playback events to use a single playback-command event and pass the command as an argument, rather than implement multiple playback events for playback-command-nextTrack etc.

I separated out the javascript from the server window and included it separately instead of including it in the main html of the page.

One of the major changes I made was to properly pull in all the dependencies - especially the styling ones from the Jellyfin Web repository. This gave me the logos with and without text which I could later use to build the app icons for various platforms using electron-icon-maker, where previously I had downloaded the logo manually, converted it and added it to the repository.

This repository also gave me the various styles needed by the interfaces that aren't loaded directly from the Jellyfin instance - the server URL entry, about and preferences windows.

A so far missing feature from the original Emby Electron project was the validation that the Server URL entered in the server window is actually a Jellyfin server. It seems that the Ping API command is not implemented in the Jellyfin server. I'm sure I can find something else to check soon.

I also updated the notification images, as I realised they were not reliable at finding an image to display in the notification, even though Jellyfin was able to display one in the player in the web interface. I realised that I was missing the case where the currently playing track did not have an image itself, Jellyfin would attempt to load the image from its associated album.

Releasing

I thought it might be a good idea to tell the community about the existence of the project, so I found the Jellyfin forums - which unfortunately seem a little sparse. I later found out the main Jellyfin community is found elsewhere.

The first (and so far only) person interested in the project on the forums attempted to build it on Ubuntu 16.04, but it unfortunately failed. He or she also advised that my readme could be better to include additional information that I had overlooked - being so close to the project. After a bit of to-and-fro-ing, I realised that there was a problem gathering the jellyfin-web dependencies. For some reason npm install 'github:jellyfin/jellyfin-web' failed to pull in the dist or src directories on Ubuntu, but received the dist directory on macOS.

I decided to try to work around it by building the jellyfin-web dependency separately. As the jellyfin-web code was built using Yarn, I decided that it may be simpler to switch my project to Yarn as well. After switching to Yarn, I at least got the src directory, so could build it as part of a postinstall step in the Jellyfin Electron's package.json file.

Now I have all three platforms ready for testing (Windows, Linux and macOS), I'll ensure they at least run on each before I reach out to the Jellyfin developers about this project to see if it might be of interest to others.

Screenshots