Borscht is in development! Many of the features below have been implemented in some form, but have not yet been glued together into a cohesive experience. Borscht is my way of practicing Haskell, so it's a Frankenstein's monster of different packages and coding styles.
borscht
is a command line tool for organizing your personal music library.
- Search for missing music metadata from the Discogs and MusicBrainz databases.
- (in progress) Write custom Datalog inference rules to automatically tag and re-tag your music, and create playlists from saved Datalog queries.
borscht
takes inspiration from beets
, with a few key differences:
borscht
works natively with both MusicBrainz and Discogs, whereas the Discogs plugin forbeets
is has been somewhat neglectedbeets
assumes your music is organized into albums, whileborscht
is happy to work with individual songsborscht
has a powerful tagging system based on datalogborscht
helps you assign an acceptable set of metadata to digital music files, but is not too concerned with preserving the correspondence between your digial and physical librariesborscht
is optimized for my personal use case, and doesn't have all the same bells and whistles asbeets
I created borscht
not only to create a music database that suits my use case, but also to practice writing non-trivial applications with Haskell. Before this project, my experience was mostly small single-file experiments compiled directly with ghc
. Since the goal of this project was to learn as much as possible about "getting stuff done" in Haskell, the code is a bit of a mishmash of different styles, abstractions, and language extensions. During this project, I gained experience with the following topics:
- I learned how to use
cabal
to manage project dependencies - I use
req
to make HTTP requests, including API authentication - I use
aeson
to parse JSON responses from the Discogs and MusicBrainz APIs - (in progress) I use
persistent
to manage asqlite
database for music libraries created withborscht
- I use
mtl
to manage a monad transformer stack - I wrote a monadic parser combinator for Datalog inference rules
R(X,Y) :- P(X,Z), P(Z,Y).
- (in progress) I implemented semi-naive evaluation with stratified negation for Datalog, which powers the music tagging system.
- I wrote a foreign function interface wrapper for the Kakasi C library, which transliterates Japanese kanji to romaji.
- I merged a pull request into the
Byline
package adding support for vivid ANSI terminal colors, and a second one to repair an unlawful semigroup instance.
Here's a list of some tangential topics that I read about while working on borscht
. These are all really interesting to me and I hope to work with them directly on future projects.
- I learned about
stm
concurrency in Haskell by reading Peyton-Jones 2007, "Beautiful Concurrency", and inspecting the source code for therate-limit
package, whichborscht
uses to respect the rate limit intervals for API requests to the Discogs and MusicBrainz databases - While reading about Datalog, I learned about the
datafun
,flix
projects, which extend Datalog with cool features like incrementalization, function abstraction, and lattices. I hope to add some of these features to my own datalog implementation! There's also the incrementaldifferential-dataflow
project.
Why the name? Borscht is a type of soup made from beets!
What music do you listen to? Check out Tinariwen, Dakh Daughters, and Shaolin Afronauts.
sudo apt install libicu-dev
Borscht uses htaglib, which requires that we install TagLib separately. There's not much documentation, but the following worked for me on Ubuntu 20.04.
sudo apt install libtagc0-dev
The ICU library cannot transliterate Japanese kanji characters. Instead, Borscht relies on kakasi
, which is not commonly avilable in package managers. First, download the kakasi
source, then
$ ./config
$ make
$ make install
After installing, the necessary header and static library files should be found in /usr/local
. For a local install, use ./config --prefix=$(pwd)/build
before running make
.
./usr/local/include/libkakasi.h
./usr/local/lib/libkakasi.a
Cabal statically links the kakasi
library from these locations.