Featured image for Dev Diary 6: Koto April Progress Report (A-side)

Dev Diary 6: Koto April Progress Report (A-side)

April 26, 2021

In this development diary, I provide an update on progress made on Koto in the first half of April 2021! My goals for the first half of April were centered around MPRIS and media keys support, as well as starting work on playlist management.

#MPRIS and MediaKeys Support

During the first half of April, my primary goal was the introduction of MPRIS and "media keys" support via the org.gnome.SettingsDaemon.MediaKeys D-Bus interface. This interface is exposed by GNOME Settings Daemon to enable applications to either grab or release media player key focus, as well as be notified when media keys are pressed in relation to the application, such as:
  • Play / Pause (typically a toggle functionality)
  • Stop
  • Backwards
  • Forwards
  • Repeat
  • Shuffle
While I personally have not seen dedicated repeat and shuffle keys on keyboards, I implemented these alongside the typical media key support for Koto just in case someone has a keyboard with these keys, or folks rebind or set keyboard shortcuts which act as XF86XK_AudioRepeat and XF86XK_AudioRandomPlay. Whenever Koto is in focus, we call GNOME Settings Daemon's MediaKeys GrabMediaPlayerKeys method, which ensures Koto is actively registered with the daemon. Similarly, when we lose focus, we call the ReleaseMediaPlayerKeys method. We are still able to receive these keyboard shortcuts after releasing or when the application is not in focus, so you can easily play / pause your music, go to the previous or next track, or even enable shuffle right from your keyboard's media keys (assuming you have them). The bigger item however was the introduction of the Media Player Remote Interfacing Specification (MPRIS) support. While the primary focus of this support was ensuring Budgie's Raven is able to generate MPRIS controls for Koto, it can actually apply to and support any desktop environment or software which acts as the MPRIS D-Bus "client", with Koto being the MPRIS D-Bus "server". As you can see from the image above, MPRIS support is now implemented in Koto. We are not providing 100% coverage of the specification, for example we are not actively supporting "TrackList" for recently played or upcoming tracks, nor do we support the "Playlists" part of the specification yet, however all relevant org.mpris.MediaPlayer2 and org.mpris.MediaPlayer2.Player interfaces necessary for Budgie's Raven support are implemented, such as:
  • Going to the previous or next track
  • Raising or quitting Koto (quitting can be done via Raven as an example)
  • Setting Repeat (referred to as "LoopStatus" in the spec) and Shuffle capabilities
  • Toggling playback in the forms of "Pause", "PlayPause", "Play", and "Stop" methods.
To facilitate this, I also needed to implement functionality for packing relevant artwork, artist, album, and track metadata into a GVariantBuilder to emit as an array of strings to values to the respective D-Bus clients. This is done when we first launch our client, in addition to being incorporated into our PlaybackEngine to update track info whenever we change tracks or even change the state of the engine via Koto itself, for example clicking on the play / pause button in the playerbar should communicate that change over MPRIS so Raven knows it should update its own button and functionality. This brings Koto much closer to being able to be actively dogfooded / tested locally. To really get the ball rolling on that, I personally need my playlist functionality. Oh, I have been working on that? Well how convenient.

#Playlists

In Dev Diary 5, I noted that one of my goals was to start work on graphical playlist management, which I noted was going to push into second half of April. I am happy to report that the playlist functionality is indeed underway. As Carl Sagan once said, "if you wish to make an apple pie from scratch, you must first invent the universe". In our far less grandiose scale, in order to manage or play a playlist (excluding our "temporary" ones we generate when playing an album), you must first be able to create a playlist. To facilitate this, work began on the Create Playlist dialog. While the design of the Create Playlist dialog will change (let us be honest, it is not the prettiest any of us have seen), the general idea for Koto is the majority of dialogs will not be the typical "popup" dialogs, but rather be GtkWidgets that are applied to a GtkStack, overlaid via GtkOverlay on top of our window contents. This provides a few benefits in our use case, such as:
  1. We are able to use functionality which create modal / popup dialogs without having a weird "popup creating a popup" experience, such as when using a file picker from GtkFileChooserNative.
  2. It enables us to implement more sophisticated multi-step dialog user experiences than what we would likely get from the likes of GtkAssistant
  3. We can block manipulation of most of the Koto UX, such as playerbar controls, in any possible "dialogs" which may be responsible for changing those widgets on-the-fly.
To facilitate this, I implemented a simple KotoDialogContainer that contains a close button for closing the active dialog, as well as a GtkStack so we can treat each dialog as a dedicated page or even series of pages if we want. Our KotoWindow was modified to have its primary layout as the "main child" of the GtkOverlay, with the overlays being the KotoDialogContainer itself. This provides us the benefit of not having to do per-dialog positioning and widget expansion as well, as the entire primary layout can be covered by the KotoDialogContainer itself. To get started, I implemented the KotoCreatePlaylistDialog you can see in the screenshot above. It is a fairly simple dialog currently, however there are plans on adding some more UX polish as well as some additional functionality I will get into in a moment. It is likely that this dialog will also be used for the editing of an existing playlist as well, so a rename is expected. The current Create Playlist Dialog is comprised of three parts: a GtkImage for setting the image of a playlist, a GtkEntry for the input of the playlist name, and the GtkButton for creating the playlist. You can set an image for a playlist in one of two ways:
  1. Setting the image by drag and dropping a file on top of the "drop target" of the image
  2. Clicking on the image will spawn a file chooser.
In the case of the file chooser, I intentionally went with using GtkFileChooserNative. While the documentation presents this as the desired object / abstraction of the file picker when using it across operating systems (since it will use the respective "native" file chooser for macOS and Windows), it also has the more important benefit of being able to be used within a sandboxed environment like Flatpak. In those scenarios, it will call the appropriate desktop portals and limit the file picking to what is defined for the Flatpak itself! This provides a zero-effort file picker for when I introduce proper Flatpak support. Clicking on Create button will create a new KotoPlaylist via koto_playlist_new, which dynamically creates a UUID for the playlist, and adds it to our KotoCartographer playlist map before closing the dialog. From there, a new KotoButton is generated for our Playlist navigation, which takes advantages of a metric ton of work that was done on KotoCartographer to emit "signals" (think of them as events that other parts of the codebase can listen for) for all of our different types, such as:
  • Adding or removing Albums
  • Adding or removing Artists
  • Adding or removing Playlists
  • Adding or removing Tracks
Our KotoNav will "listen" for the playlist-added signal and create a new KotoButton with the Playlist details. There is still some work to be done on the navigation front, such as handling our playlist-removed and setting up signals for when a playlist is modified (so we update the button), however the creation of the KotoButton is a sufficient amount of work to enable me to progress to other aspects of the playlist experience. While the above image is a mockup, it should help provide some clarity on what the intended early design of Koto's playlist page will look like. At the top, we have a header that will show the playlist image (if none is set, we default to the "audio-x-generic-symbolic" icon), information on the playlist, and some quick actions. Clicking on the image will provide the same functionality as the artwork does in the album views, in that it will start the playback of the playlist. For the header playlist info, we indicate whether or not it is an "automatically generated" playlist (such as what we will have in the future for daily routines) v.s. a "curated playlist" (what you do by hand), the name, and number of tracks. For the actions, we will present buttons for:
  1. An ability to "favorite" a playlist, so when we implement our Home dashboard it will be pinned there.
  2. An ability to "share" a playlist. This has no mechanism yet, so it will not likely be included until we have one implemented, but the idea is that once we have a discovery service, you will be able to toggle the sharing of the playlist contents (not the files themselves, mind you) to the service, which you will be able to share with others or be used for recommending you different artists.
  3. An ability to modify various attributes of the playlist such as the image and name, as well as delete the playlist.
The current track list implementation will use a GtkListView, assuming this issue with scrolling is resolved. This allows us to provide an efficient listing of tracks by rebinding data to the existing list items as you scroll. You can read about that here as well as this GNOME blog post. You will be able to click on individual tracks or even multiple, with the behavior being defined below:
  1. Whether you click one or more item, we will use a GtkActionBar to present various relevant actions for the items.
  2. When clicking on one item, you will have the option to play that track specifically, or start playlist playback from that track. Additionally you will have the option to navigate to the track's respective album (if no album, then artist).
  3. When clicking on one or more items, you will have the option to remove the track(s) from the KotoPlaylist.
For the track list headers, you will be able to order by the position, title, album, and artist. To keep the implementation simple and mirror other clients like Spotify, sorting by Album does not provide a guaranteed stable sort of the any tracks for the album. In other words, if you have 3 tracks of an Album in a Playlist, sorting by album would not guarantee that they would be ordered by their respective position in the album, the only guarantee is the album names are correctly alphabetized. Same goes for alphabetizing albums when sorting by artists. This will use GLib's UTF-8 collation functionality to ensure locales are respected as well. All of this leverages new functionality in our KotoPlaylist for defining a default sorting "model" and providing a "store" (not like one where you would buy something, rather an object which is capable of storing items by a key and value) for the playlist to leverage. We have the following defined "preferred models" that I have implemented:
  • KOTO_PREFERRED_MODEL_TYPE_DEFAULT: This is the default (hence the name) and we will sort by the most recent track added to a playlist first, followed by older tracks.
  • KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST: This model will sort by oldest tracks added to newest.
  • KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM: This model will alphabetically sort a playlist by album names.
  • KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST: This model will alphabetically sort a playlist by artist name.
  • KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME: This model will alphabetically sort a playlist by track names.
Adjustments will be made to our playlist creation dialog to enable the option to choose between KOTO_PREFERRED_MODEL_TYPE_DEFAULT and KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST models. The other sort models are really only intended to be temporary. Further work needs to be done on the KotoPlaylist to leverage the "current store" instead of our GQueue of tracks added so that way changing the sort order will properly update what tracks are previous and next.

#Upcoming

As I am sure you can tell, there is a lot of work to be done on playlist functionality. So far, our new dedicated KotoPlaylistPage generates the header based on playlist metadata, however the following additional work needs to be done (not in this specific order necessarily):
  1. Implementation of a playlist-metadata signal for KotoPlaylist so we can notify when a playlist has been modified.
  2. Updating our logic for going to the previous and next tracks to leverage our currently selected model (or default based on the type we set during creation) and base the tracks on that instead.
  3. Implement the track rendering in the GtkListView.
  4. Implement the selection handling and GtkActionBar rendering, events, etc. when clicking on one or more tracks.
  5. Implement helper functions as part of our KotoButton for "click" and touch gesture setup to avoid repetitive GtkEventController / Gesture code.
  6. Refactoring the album artwork image used in the KotoAlbumView into a generic widget that can be leveraged in our KotoPlaylistPage.
  7. Implement playlist-removed handling in the KotoNav to delete the button.
  8. Refactor some of our functionality that listens to the various KotoCartographer events so we do not have a bunch of signal listeners splintered across different files.
  9. Finish the uncrustify config so I can have a standardized code styling.
  10. Finish necessary VS Code bits so I can hopefully launch Koto in various modes and stop using GNOME Builder (too buggy for me).
I would be surprised if I get all of this done in the second half of April, but I am going to be an optimist and strive for it anyways. On top of all of this, I am going to be working on adopting GitHub's project board and mass file issues of work I need to get done for different milestones. This should help provide clarity to all of you on how much work I expect to be required for different alpha, beta, and stable release builds, in addition to what functionality you can expect for them! So stay tuned for a follow-up post on that.

#Streams

All development streams happen on my Twitch every Tuesday and Thursday from 12pm-5pm GMT+3 / EEST (Eastern European Time). Remember the daylight savings time change. If you miss these streams, I upload all of them to Odysee so be sure to check them out! I am part of Odysee's Viewer Rewards Program, so if you have an Odysee account, you can now get a daily watch reward of a bit of LBRY Credits (LBC) when you watch my videos, and I get some too!