Awesome FOSS Logo
Discover awesome open source software
Launched 🚀🧑‍🚀

Rhymote: a Remote for Rhythmbox

Rhythmbox logo + Flutter logo

tldr; Shouting into the void about a Proof of Concept (PoC) of mine called Rhymote, which is a Rhythmbox plugin + Mobile app meant to make it easier to control Rhythmbox running on some other device (usually your computer) from a phone.

I recently made a project I really enjoy called Rhymote, which consists of a Rhythmbox plugin and a Flutter-based mobile app called Rhymote. The project is super early but I sprinkled a tiny bit of ambition in the design which is why I wanted to release it, partly as an example of more complex, recently written python-powered Rhythmbox plugin. That said, it’s probably not the best example of either of those things.

At the time of this posting, the project is really in PoC stage – so only direct insecure (hopefully local) connections over HTTP and basic controls (skip, next, play/pause, stop) are supported. At this point it’s technically worse than the now packaged Rhythmbox web player except for that the web player isn’t an application (which is thin ice because definitely visible on phones thanks to this little invention we call a browser).

There are some other projects out there that were remotes for Rhythmbox, but they all seemed deprecated or dead, even the ones listed on Rhythmbox’s 3rd party plugins page:

Either way, along the way to the working PoC I ran into some things and got stuck in some places so wanted to share about those.

Issues during PoC plugin development

Thanks to Rhythmbox’s great plugin guide, and the existing web plugin (which I leaned on heavily) I was able to breeze my way through a crash course on python for plugin environments, and also the [Gnome Object (GObject) framework][gobject] (and PYGObject of course). While I originally really wanted to do the plugin in rust and use some of rust’s Foreign Function Interface and awesome C interop, I decided against it, since this project was mostly motivated by a want to try out Flutter for a non-trivial application (though the application is still very much trivial).

One thing I had to learn about in a hurry to support the component-based architecture I wanted (which I’ve talked about in lots of previous posts, but refer to stuartsierra/component) was how to work within the GTK Async environment and their idea of threading. The following resources proved very useful to me:

While I’m relatively certain the way I ended up doing things isn’t the greatest – right now if I try to listen for messages for any of the components in my app too often Rhythmbox gets locked up, which is definitely not how async event-driven things (or good plugins) are supposed to work. It actually makes me wonder if I even really need event-listening bit of the component framework I wrote – since the vast majority of interactions will be driven by an external impetus (someone hitting an Soup HTTP endpoint, or in the future sending a message over IPFS let’s say, it may be a good idea to just scrap the event listening all together.

I did get a chance to try out one of my longtime fetishes of trying to express the operation space of the components as an enumeration of commands. This is an idea that I’ve been thinking about a lot and honestly have a lot of space to run with but I’m starting to use the model almost everywhere – in general commands and events are about the only external leverage that any component (and resulting component system) need to build principled “distributed systems”.

Anyway, the code for the plugin is up, so I’d love some feedback from some pythonistas at some point – I haven’t written any python in ~5+ years (I think at least 7 maybe), so I can’t imagine how many things I’ve done silently but unmistakenly wrong.

Issues during PoC mobile development with Flutter

For those who may not be familiar, Flutter occupies the middle ground of mobile application development, with libraries like React Native and Nativescript (my personal favorite) – faster than webpage-in-a-webview approaches like Ionic and Apache Cordova (formerly known as PhoneGap) (and theoretically faster than regular PWAs), but slower than full on native. Companies like Instagram have written glowing posts about React Native and also other companies have written about sunsetting the technology as their needs changed. Either way, the middle-ground is at the very least better than the webpage-in-a-webview (which is basically just to get you into the app store), and the approach is generally capable of producing good-enough UIs. As with all things, YMMV.

The Flutter ecosystem (and the Google money behind it) is receiving the full marketing push and developer-centric advertising that internet-saavy companies and teams put out these days. While I say that somewhat cynically, it does mean there are some awesome resources out there like the Flutter boring development show, pretty awesome and considered documentation, and even a subreddit.

The very first time I tried to run flutter run after going through a tutorial-ish page, I was hit with an unexpected and non-obvious issuse. Luckily I found an SO post which explained the issue as a “simple” gradle config bug, but it was kind of an ominous thing to run into right at the start.

I also got pretty ambitious with the architecture (or at least what I think is a good architecture) for the mobile app, trying to set myself up with domain objects like Services to make later performance optimizations (basically running hard work in dart lightweight-thread-like Isolates) and to categorize & centralize domain model operations. This app is most likely both over-engineerd and under-developed – more abstraction than necessary and not enough features – but I don’t hate it so there’s that.

You can check out the app itself on Gitlab. At some point I’ll try and also get it on F Droid as well, since I think the overlap of people who use FDroid and people who run Linux and possibly Rhythmbox is relatively high. For now though, you can install it with Android’s adb locally and if you’re adventurous and determined, build it for iOS and install it with Cydia Impactor.

SIDENOTE - It’s really weird that r/FlutterDev says this:

This subreddit isn’t a support forum, but meant more for posting news articles, tutorials, plugins, open source projects, or general discussion related to Flutter.

I don’t know who moderates this, but if it’s people from Google, it’s feels to me a little bit like trying to suppress the subreddit content to “happy” Flutter news. While would certainly be noisy to see “I can’t get my X to work after doing the tutorial”, or “I can’t figure out X”, it would be a great signal for people looking to choose a solution because just about everything looks great in tutorials but if people have repeated simple problems it’s a reliable bad sign.

Future work

There’s a lot left to do with this project, but I figure I might as well lay out some of the goals and cool features I want to get a chance to implement (also on the README for the plugin project):

  • Transport-independent Security (preshared AES key should be enough), since the endpoint might be HTTP/HTTPS/anything else I build support for
  • SSE enabled endpoint that sends information (this might involve extending Soup.Server in another library possibily)
  • Build out proper configuration parsing
  • Create & start DataComponent to manage on-disk cache of transient data pertinent to the application or Rhythmbox
  • Use DataComponent to get more information out of Rhythmbox (starting with track information)
  • Create & start RendezvousComponent to manage meeting clients (rendezvous) and figuring out best connection method
  • More functionality/commands for RBComponent/DataComponent (ex. GET_MUSIC_FILE, GET_ALBUM, GET_LIBRARY_STATS, etc)
  • Rhythmbox-native GUI for settings (ensure in-app GUI can write default configuration out to a file)
  • GUI & functionality for QR code support w/ RendezvousComponent (scan a QR code on a phone and the phone will try multiple ways to connect)
  • More general protocol usage – some persnickety folks over in my post on r/Linux let me know about the MPRIS protocol and reminded me of MPD’s protocol. Honestly I’m less interested in writing a general works-everywhere client and really just one that works with Rhythmbox so I’m not sure how much I’ll care about this later but, with the way things are built currently it should be possible.

So this post is more about ambition and promises rather than content but given that I’ve already posted about it on reddit, I figured I’d at least post about it here as well.