Exaile recently switched from Bazaar to Git. Dustin Spicuzza, who has been leading the Exaile project recently,
initiated this plan and the rest of the team were in full agreement. But why did we switch? Put simply, it was
because all of us prefer Git to Bazaar. However, that’s an oversimplification of my stance on the whole DVCS thing.
After the BitKeeper debacle, distributed version control system became the hottest buzzword around, and in
a short while the three major contenders became obvious: Bazaar, Git, and Mercurial. At the time I already had
experience with SVK (a semi-DVCS that extends Subversion), which I had used on Ruby.NET and Exaile, but when Adam
moved Exaile to Bazaar, I went all-in on Bazaar.
I loved nearly everything about Bazaar, and I instantly “got” it. This was in contrast to Mercurial, which I gave
up trying to understand, and to Git, which I wouldn’t have started using if I didn’t have to use it at work. I
really wanted Bazaar to be popular. I still agree with the general sentiment that it has excellent
usability, especially compared to Git.
But its modest popularity crashed under Git’s dominance; Git was getting all the attention and all the improvements
while Bazaar stagnated. Bazaar’s biggest promise from the start, that it would eventually be optimised for speed,
either came too late or never materialised—I still don’t know which. Its code documentation languished, and I had
to go digging around just to write a simple commit bot.
During casual chat among the active developers at #exaile, we found that our collective knowledge of Bazaar had
deteriorated so much that we couldn’t remember how to perform some relatively simple operations. We decided to move
on. Right now most of our services have moved to GitHub. We’re still keeping the Launchpad site to refer to old
bugs and for the web translation service (the latter is not available on GitHub).
Note: I wrote this article in full in 2014 but only decided to publish it in 2019, by which time
it was obviously not timely anymore. It still expresses my thoughts quite well, so I’m publishing it belatedly, but
backdated in order to not confuse people.
If you’ve been using Exaile’s Shoutcast plugin, you would have realised that it
hasn’t been working for a while now. This is due to a
change in the SHOUTcast directory API.
However, SHOUTcast directory support is not coming back. The VLC developers
explain in detail the problems that also prevent us from
complying with SHOUTcast’s terms of service. The specific wording in the terms makes me believe that we will never
see an acceptable solution, and that makes the issue of fixing the plugin a moot point. Following what VLC and
Amarok have
done, we have removed SHOUTcast directory support from Exaile.
Note, however, that Shoutcast/Icecast streams still work as long as you know the stream URI. It’s
just the directory that is not working; for the time being, you can use the
Icecast Web-based directory for this purpose.
In the future, we would love to switch to the Icecast directory, but their documentation seems
a bit sketchy. If you would like to help with this, feel free to contact us through IRC or at the
wishlist report. There is an
Amarok script that you may be able to use as reference.
Meanwhile, I have removed the Shoutcast plugin from Exaile’s list of installed plugins. The outdated code is still
in the source tree, but it will not be installed by our makefile.
[Update: I am was working on an Icecast directory plugin. It’s literally half-working (I can
get genres but not individual stations, still figuring out why). For this plugin I’m screen-scraping the website
because the actual YP directory seems to be incomplete.]
[Update 2: I’ve stopped working on the plugin for now as I’m occupied with something. Apparently there’s a working
Icecast plugin in the bugtracker somewhere; I haven’t tested it.]
This post explains a
Python EBML parser that I
wrote. (EBML is Matroska’s binary “markup language”.) It is implemented as
a single-file library and is available under a free software licence.
Background
I’ve been working to implement Matroska (mka, mkv, webm) tag-reading support in Exaile.
Mutagen—the tag library that we use—currently doesn’t have this
feature, so I looked elsewhere.
Choices
Previously I had a working solution using
hachoir-metadata, but it doesn’t really make
sense to depend on another large tagging library when we’re already using Mutagen. To make matters worse, I
accidentally deleted the branch during our recent
Bazaar upgrade problem.
I started shopping around for other possible solutions and found
videoparser, which seemed quite nice and compact. It’s
still a different library, though, and it doesn’t seem to be packaged in Debian.
I was considering just using it anyway for yet another temporary hack when I chanced on
MatroskaParser.pm
(dead link), a Perl library written by “Omion
(on HA)”. It’s only 816 lines of Perl; discounting the README and the
Matroska elements table, we’re looking at less than 450.
Solution
I decided to translate MatroskaParser.pm into Python. Despite the horror stories out there about Perl, this
particular code is written in a style that is extremely readable if you’re somewhat familiar with the language.
Well, I’ve finished the porting: 250 lines of EBML parser written in Python. Parts of MatroskaParser.pm that are
not relevant—mainly the validity checker and the Block parser—have been removed, and the output data structure has
been simplified. The next job is to actually extract tags out of the structure.
Matroska tags
Matroska tags are quite different from MP3 and Vorbis tags, in that they’re not just a flat list of key-value
pairs. Consider the following snippet.
[{'SimpleTag': [{'TagName': ['TITLE'],
'TagString': ['Light + Shade']},
{'TagName': ['ARTIST'],
'TagString': ['Mike Oldfield']}],
'Targets': [{'TargetTypevalue': [50]}]},
{'SimpleTag': [{'TagName': ['TITLE'],
'TagString': ['Surfing']}],
'Targets': [{'TargetTypevalue': [30]}]}]
There are two types of tags in this example. The first (target type: 50) explains the album (title:
Light + Shade, artist: Mike Oldfield), while the second (target type: 30) explains the track
(title: Surfing). Translating this structure into tags that Exaile can understand is not hard, just needs
a bit of planning.
By the way, notice that Matroska makes implementing album artists / compilation albums very intuitive: you can have
an artist tag at album level, and another at track level. There are even
other levels specified. As a
further example, because Light + Shade consists of two CDs labelled Light and
Shade respectively, you could use them as the titles at level 40 (between album and track); however, this
is not common practice.
Another tricky part is getting the track length out of the structure. Under /Segment/Info
, you’ll find
something like
[{'Duration': [14821615.0],
'TimecodeScale': [22674]}]
At first I randomly assumed that the duration was specified in seconds, and got around 171 days as output, which
was obviously wrong. Apparently you need to apply this formula to get the length in seconds:
Length = Duration * TimecodeScale / 10^9
Note that TimecodeScale may be omitted; it is one of the few important elements that have default values
(1,000,000 in its case).
Code
The code is now available in
Exaile’s repository. It’s
licensed under GPL 2+ with the standard Exaile exception, although I will consider relicensing it if there is
interest.
Notice that the last 100-or-so lines make up the Matroska tagging part. Depending on your needs, you may need to
expand the list of elements based on the Matroska specification. There are also 40 lines of code that subclasses
the parser to use GIO to read the files; you may want to remove this chunk of code if it’s not relevant to you.
Future
Matroska read-only tag support will be in Exaile 0.3.2. Maybe one day I’ll add write support and integrate the
whole thing into Mutagen, but don’t count on it. If anyone wants to do it, I’m more than happy to help.
What about WebM?
Funny how I made this post shortly before WebM was announced. Coincidence? Yes, unfortunately; I’m not as cool as
the Mozilla and Opera people, who were let in on Google’s secret.
At this point, the WebM container is mostly just a subset of Matroska (the only incompatibility I’ve noticed is the
change in doctype, from matroska to webm). As far as I know, they use the exact same EBML
structure for tags, so there’s no reason Exaile or this code shouldn’t be able to read tags from a WebM file.