<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

    <title>The beets blog.</title>
    <link href="http://beets.io/blog/" />
    <link href="http://beets.io/blog/atom.xml" rel="self" />
    <updated>2025-09-10T08:01:02+00:00</updated>
    <id>http://beets.io/blog/</id>
    <author>
        <name>Adrian Sampson</name>
        <email>adrian@radbox.org</email>
    </author>

    
    <entry>
        <title>a guided walkthrough</title>
        <link href="http://beets.io/blog/walkthrough.html"/>
        <updated>2022-08-20T00:00:00+00:00</updated>
        <id>http://beets.io/blog/walkthrough</id>
        <content type="html">&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;This is a step by step walkthrough for getting started with the beets CLI using a sample music library. The sample music library includes public domain recordings of Debussy, Chopin, and Virginia Liston compiled from Open Music Archive, IMSLP, and Internet Archive.&lt;/p&gt;

&lt;p&gt;We will use this library in its messy/unaltered state to showcase the power of the beets CLI by correcting obscure metadata and organizing the music library with the auto-tagger (and a little manual input). Lastly, we will host the library locally using the beets web plugin to stream the audio once finished and admire our results. Let&amp;rsquo;s get started!&lt;/p&gt;

&lt;p&gt;If desired, there are resources included below to help find additional recordings that are in the public domain if you&amp;rsquo;d like to grow your sample music library. Note: be sure the release you&amp;rsquo;re looking to include has metadata in &lt;a href=&quot;https://musicbrainz.org/&quot;&gt;the MusicBrainz database&lt;/a&gt; for best results.&lt;/p&gt;

&lt;h2 id=&quot;installing-and-configuring-beets&quot;&gt;Installing and configuring beets&lt;/h2&gt;
&lt;p&gt;Following are brief instructions for downloading and installing the beets CLI. For detailed instructions, head over to &lt;a href=&quot;https://beets.readthedocs.io/en/stable/guides/main.html&quot;&gt;the Getting Started guide&lt;/a&gt; in the official documentation.&lt;/p&gt;

&lt;p&gt;Download beets with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install beets&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Write your config in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/beets/config.yaml&lt;/code&gt; specifying a directory for your copied music and a library for a database file to store an index of your music.&lt;/p&gt;

&lt;p&gt;My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.yaml&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/beets/music&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;library&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;~/beets/data/musiclibrary.db&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All I&amp;rsquo;ve done to support the above config is create the  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beets&lt;/code&gt; directory inside my home folder, with empty child directories for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;music&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; inside it. You can place your music library and data store wherever you like, and you don&amp;rsquo;t need to create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;musiclibrary.db&lt;/code&gt; file; it will be created for you.&lt;/p&gt;

&lt;h2 id=&quot;importing-the-sample-library&quot;&gt;Importing the sample library&lt;/h2&gt;
&lt;p&gt;First, download the .zip archive of the sample music library from &lt;a href=&quot;https://bit.ly/3EZb5ue&quot;&gt;this&lt;/a&gt; public google drive file, extract the contents and take note of the path to the sample library for later use.&lt;/p&gt;

&lt;p&gt;Take a second to scrub through the library&amp;rsquo;s organization and inconsistent file naming. On a small scale this is a prime example of how messy a music library can be in its raw form, with no standard for metadata or organization.&lt;/p&gt;

&lt;p&gt;Enter beets! We&amp;rsquo;re going to clean up this mess and make our library beautiful.&lt;/p&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet import {path to sample library}&lt;/code&gt; to import the messy sample library and start using the beets auto-tagger.&lt;/p&gt;

&lt;p&gt;In my case the path to the sample library is also in my home folder, so I will run the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet import ~/sample-library&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The autotagger starts work on Album 1, and the closest match is selection &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;. Go ahead and enter that in the prompt.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album1-find-tags.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; to apply the changes found to Album 1.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album1-correct-tags.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Album 2 doesn&amp;rsquo;t have a matching release, so we will enter one manually by entering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; and then the following MusicBrainz id &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;96581fca-a7b1-4c94-bb23-7b0df9f00507&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album2-no-release.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Accept the changes for our manual match by selecting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album2-correct-tags.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Album 3 doesn&amp;rsquo;t have a matching release either, so we will enter one manually with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; and then the following MusicBrainz id &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;95440be5-cff3-4222-8ddc-5b7ecb99a351&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album3-no-release.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We&amp;rsquo;ve located the correct album; however, we only have 11/48 tracks across a compilation of 3 CDs, hence the large output warning us of missing tracks.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album3-correct-tags.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The autotagger has done a decent job, but it has incorrectly matched tracks 7, 10, and 11 due to similar titles in other tracks throughout the compilation. We&amp;rsquo;ll use this opportunity to re-tag the offending tracks later in the next section.&lt;/p&gt;

&lt;p&gt;For now, accept the changes for our manual match by selecting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album3-apply.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Album 4 is a series of singletons with a number of matches found by the autotagger.&lt;/p&gt;

&lt;p&gt;Accept these changes and import the tracks with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t&lt;/code&gt; flag for individual tracks&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album4-find-tags.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The output should look like the following:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album4-import-tracks.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally, let&amp;rsquo;s check out our library in its current state with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet list&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/beets-list.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We&amp;rsquo;ve made significant progress! Now let&amp;rsquo;s correct some discrepencies that came up between the autotagger and our library.&lt;/p&gt;

&lt;h2 id=&quot;correcting-a-mistake-in-the-sample-library&quot;&gt;Correcting a mistake in the sample library&lt;/h2&gt;
&lt;p&gt;We can use the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet fields&lt;/code&gt; to see all the metadata that can be updated for each track in the library, in our case we&amp;rsquo;re interested in updating the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mb_trackid&lt;/code&gt; fields to correct a series of mistakes in a couple of our albums.&lt;/p&gt;

&lt;h3 id=&quot;mistake-album-2---swapped-track-titles-and-metadata&quot;&gt;Mistake: Album 2 - Swapped track titles and metadata&lt;/h3&gt;
&lt;p&gt;There are two tracks in our download of Moravec&amp;rsquo;s Debussy album where the titles and metadata are swapped. &amp;ldquo;Children&amp;rsquo;s Corner: Jimbo&amp;rsquo;s Lullaby&amp;rdquo; and &amp;ldquo;Clair de lune&amp;rdquo; incorrectly refer to the opposite recording. Let&amp;rsquo;s change that with beets!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Rename &amp;ldquo;Clair de lune&amp;rdquo; with the following command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet modify Clair de lune title=&quot;Children&apos;s Corner Suite&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why not name this &amp;ldquo;Children&amp;rsquo;s Corner: Jimbo&amp;rsquo;s Lullaby&amp;rdquo;? Well, there&amp;rsquo;s another mistake in our album: this track is actually a 17 minute long recording of all movements from the &amp;ldquo;Children&amp;rsquo;s Corner&amp;rdquo; Suite, which are typically broken up track by track in a traditional release. We are renaming this way to more accurately reflect the recording we have in our library.&lt;/p&gt;

&lt;p&gt;When running the command above, it will attempt to modify more than one track (since another track has the term &amp;ldquo;clair de lune&amp;rdquo; within its title), so use the select flag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; to systematically deny or approve the title update for multiple tracks one by one. This ensures that we only update the title for the track &amp;ldquo;Clair de lune&amp;rdquo;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album2-correct-title1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, update the MusicBrainz track id with the following command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet modify Children\&apos;s Corner Suite mb_trackid=9c7f4fc2-0d2b-42b6-99fd-6ac2cdf32123&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album2-correct-id1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This way our metadata for this track is correct. We will see a demonstration for why this is important later when we view our tracks with the web plugin.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Rename &amp;ldquo;Children&amp;rsquo;s Corner: Jimbo&amp;rsquo;s Lullaby&amp;rdquo; with the following command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet modify lullaby title=&quot;Clair de lune&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Be sure to update the MusicBrainz track id for this track as well with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet modify &quot;Clair de lune&quot; mb_trackid=9d6006ed-cb42-4227-945f-9e7f6766cc2c&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; tag to select and update the correct track.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;walkthrough-images/album2-correct-title-id-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;exercise---mistake-album-3---incorrect-titles-and-metadata&quot;&gt;Exercise - Mistake: Album 3 - Incorrect titles and metadata&lt;/h3&gt;
&lt;p&gt;There are three tracks we need to correct in Album 3. Use your knowledge of beets&amp;rsquo; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modify&lt;/code&gt; command from the previous mistake we corrected to update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mb_trackid&lt;/code&gt; fields for each of the tracks below.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;first title&lt;/th&gt;
      &lt;th&gt;current title (incorrect)&lt;/th&gt;
      &lt;th&gt;new title (correct)&lt;/th&gt;
      &lt;th&gt;mb_trackid (correct)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Op. 62, no. 1, B major&lt;/td&gt;
      &lt;td&gt;Nocturne in B major, op. 32 no. 1&lt;/td&gt;
      &lt;td&gt;Nocturne in B major, op. 62 no. 1&lt;/td&gt;
      &lt;td&gt;dda32c0b-bd61-45f3-bb98-682bc4dbf065&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Op. 62, no. 2, E major: posth.&lt;/td&gt;
      &lt;td&gt;12 Études, op. 10 no. 7 in C major &amp;ldquo;Toccata&amp;rdquo;&lt;/td&gt;
      &lt;td&gt;Nocturne in B major, op. 62 no. 2&lt;/td&gt;
      &lt;td&gt;4cec9444-351d-49c8-a42e-51734c6f27fe&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Op. 72, no. 1, E minor&lt;/td&gt;
      &lt;td&gt;12 Études, op. 10 no. 10 in A-flat major&lt;/td&gt;
      &lt;td&gt;Nocturne in E minor, op. 72 no. 1&lt;/td&gt;
      &lt;td&gt;1dc71083-bb07-46c4-9765-094cc3a3469c&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;check-in&quot;&gt;Check-in&lt;/h2&gt;
&lt;p&gt;At this point we have cleaned up an otherwise messy music library into a clean and updated public domain library of music. If we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet list&lt;/code&gt; we can see it in all its glory. Next, let&amp;rsquo;s host it on the web!&lt;/p&gt;

&lt;h2 id=&quot;setting-up-a-server-with-the-web-plugin&quot;&gt;Setting up a server with the web plugin&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;Following are brief instructions for setting up the beets web plugin. For detailed instructions, head over to the official documentation here: &lt;a href=&quot;https://beets.readthedocs.io/en/stable/plugins/web.html&quot;&gt;Web Plugin&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;To use the beets web plugin we&amp;rsquo;ll need to download flask with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install flask&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Update the beets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.yaml&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet config -e&lt;/code&gt;  and add the line &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugins: web&lt;/code&gt;  to the end (if you haven&amp;rsquo;t already)&lt;/li&gt;
  &lt;li&gt;Start up the server for the library with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet web&lt;/code&gt; and navigate to http://localhost:8337 to query, view, and listen to the music&lt;/li&gt;
  &lt;li&gt;If you search &amp;ldquo;Debussy&amp;rdquo; and play &amp;ldquo;Clair de lune&amp;rdquo; (which is a track who&amp;rsquo;s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mb_trackid&lt;/code&gt; we modified), then click &amp;ldquo;view&amp;rdquo; next to &amp;ldquo;MusicBrainz entry&amp;rdquo;, you&amp;rsquo;ll notice it correctly navigates us to the page for the updated track in the MusicBrainz database.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Thanks for coming along on this journey! You can now enjoy an organized and well-documented sample music library of recordings found in the public domain, and take your newfound knowledge of beets to import music, correct metadata, and maintain your collection of incomplete/messy downloads. Happy listening!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;sample-music-library-sources&quot;&gt;Sample Music Library Sources&lt;/h2&gt;

&lt;p&gt;Debussy - Jacopo Salvatori&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://imslp.org/wiki/Suite_bergamasque_(Debussy%2C_Claude)&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://musicbrainz.org/release/3d317801-cad6-4068-8544-35c545ad9ca9&quot;&gt;MusicBrainz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debussy - Ivan Moravec&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/debussysoundreco00debu/07_Children_s_corner_suite.mp3&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://musicbrainz.org/release/96581fca-a7b1-4c94-bb23-7b0df9f00507&quot;&gt;MusicBrainz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frederic Chopin - Guiomar Novaes&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/nocturnessoundre00chop&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://musicbrainz.org/release/95440be5-cff3-4222-8ddc-5b7ecb99a351&quot;&gt;MusicBrainz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Virginia Liston&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.openmusicarchive.org/browse_tag.php?tag=Virginia%20Liston&quot;&gt;Download&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://musicbrainz.org/artist/68ec4f9a-ca42-44a4-80a8-ef2c99cb5bf7&quot;&gt;MusicBrainz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
    
    <entry>
        <title>we’re pretty happy with SQLite &amp; not urgently interested in a fancier DBMS</title>
        <link href="http://beets.io/blog/sqlite-performance.html"/>
        <updated>2016-06-19T00:00:00+00:00</updated>
        <id>http://beets.io/blog/sqlite-performance</id>
        <content type="html">&lt;p&gt;Every once in a while, someone suggests that beets should use a &amp;ldquo;real database.&amp;rdquo; I think this means storing music metadata in &lt;a href=&quot;https://www.postgresql.org&quot;&gt;PostgreSQL&lt;/a&gt; or &lt;a href=&quot;https://www.mysql.com&quot;&gt;MySQL&lt;/a&gt; as an alternative to our current &lt;a href=&quot;https://sqlite.org&quot;&gt;SQLite&lt;/a&gt; database. The idea is that a more complicated DBMS should be faster, especially for huge music libraries.&lt;/p&gt;

&lt;p&gt;The pseudo-official position of the beets project is that supporting a new DBMS is probably not worth your time. If you&amp;rsquo;re interested in performance, please consider helping to optimize our database &lt;em&gt;queries&lt;/em&gt; instead.&lt;/p&gt;

&lt;p&gt;There are three reasons I&amp;rsquo;m unenthusiastic about alternative DBMSes: I&amp;rsquo;m skeptical that they will actually help performance; it&amp;rsquo;s a clear case of premature optimization; and SQLite is unbeatably convenient for desktop applications like beets.&lt;/p&gt;

&lt;h2 id=&quot;workload&quot;&gt;Workload&lt;/h2&gt;

&lt;p&gt;Many people assume that Postgres or MySQL must be faster than SQLite. But performance depends on the workload, and SQLite is a great match for the beets workload.&lt;/p&gt;

&lt;p&gt;Specifically, serious client&amp;ndash;server DBMSes are great for web applications, where writes are frequent and concurrency is paramount. In beets, we have the opposite workload: we&amp;rsquo;re read-heavy and write-light, and we rarely need concurrent updates. The main case when beets writes to its database is on &lt;a href=&quot;http://docs.beets.io/en/latest/reference/cli.html#import&quot;&gt;import&lt;/a&gt;, and import performance is dominated by I/O to the network and to music files. This mostly-read workload is exactly what SQLite was &lt;a href=&quot;https://www.sqlite.org/whentouse.html&quot;&gt;made for&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;low-hanging-fruit&quot;&gt;Low-Hanging Fruit&lt;/h2&gt;

&lt;p&gt;As &lt;a href=&quot;http://www-cs-faculty.stanford.edu/~uno/&quot;&gt;Prof. Knuth&lt;/a&gt; tells us, optimization before measurement is the &lt;a href=&quot;https://www.cs.sjsu.edu/~mak/CS185C/KnuthStructuredProgrammingGoTo.pdf&quot;&gt;root of all evil&lt;/a&gt;. Before we embark on any &lt;a href=&quot;http://c2.com/cgi/wiki?PrematureOptimization&quot;&gt;big change for performance&amp;rsquo;s sake&lt;/a&gt;, we should have some scrap of empirical evidence to suggest that it might pay off.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s a better way to spend our limited developer attention. We haven&amp;rsquo;t invested at all in making our database queries efficient&amp;mdash;we have almost no indices, no principled denormalization, and certainly no detailed profiling data. Carefully measuring our query efficiency is likely to yield asymptotically better database behavior. And since we&amp;rsquo;ve spent so little time on it so far, there&amp;rsquo;s probably a vast orchard of low-hanging fruit there.&lt;/p&gt;

&lt;h2 id=&quot;hassle&quot;&gt;Hassle&lt;/h2&gt;

&lt;p&gt;Even if everything else were equal, connecting to a new database backend would incur inconvenience for users and developers. SQLite is built into Python; any other DBMS would mean a new dependency. The &amp;ldquo;real databases&amp;rdquo; people usually envision are client&amp;ndash;server systems; these are many times more frustrating to use than an embedded library that uses a flat file. SQLite isn&amp;rsquo;t perfect, of course, but it&amp;rsquo;s pretty damn good for being hassle-free.&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re convinced that a real database would be good for beets, I&amp;rsquo;d love to be proven wrong. But you need to prove it: measure the bottlenecks in beets first, think carefully about whether they might be query problems rather than DBMS problems, and be confident that the return in performance is worth the cost in hassle.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>our solution for the hell that is filename encoding, such as it is</title>
        <link href="http://beets.io/blog/paths.html"/>
        <updated>2016-06-04T00:00:00+00:00</updated>
        <id>http://beets.io/blog/paths</id>
        <content type="html">&lt;p&gt;By far, the worst part of working on beets is dealing with filenames. And since our job is to keep track of your files in the database, we have to deal with them all the time. This post describes the filename problems we discovered in the project&amp;rsquo;s early days, how we address them now, and some alternatives for the future.&lt;/p&gt;

&lt;h2 id=&quot;what-is-a-path&quot;&gt;What is a Path?&lt;/h2&gt;

&lt;p&gt;What would you say a path is? In Python terms, what should the type of the argument to &lt;a href=&quot;https://docs.python.org/2/library/functions.html#open&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://docs.python.org/2/library/os.html#os.listdir&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os.listdir&lt;/code&gt;&lt;/a&gt; be?&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s say you think it should be text. The OS should tell us what encoding it&amp;rsquo;s using, and we get to treat its paths as human-readable strings. So the correct type is &lt;a href=&quot;https://docs.python.org/2/library/functions.html#unicode&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode&lt;/code&gt;&lt;/a&gt; on Python 2 or &lt;a href=&quot;https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;str&lt;/code&gt;&lt;/a&gt; on Python 3.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s the thing, though: on Unixes, &lt;em&gt;paths are fundamentally bytes&lt;/em&gt;. The arguments and return types of the standard Posix OS interfaces &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open(2)&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/009695399/functions/opendir.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;opendir(2)&lt;/code&gt;&lt;/a&gt; use &lt;a href=&quot;https://en.wikibooks.org/wiki/C_Programming/Strings&quot;&gt;C &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;char*&lt;/code&gt; strings&lt;/a&gt; (because we still live in 1969).&lt;/p&gt;

&lt;p&gt;This means that your operating system can, and does, lie about its filesystem encoding. As we discovered in the early days of beets, Linuxes everywhere often say their filesystem encoding is one thing and then give you bytes in a completely different encoding. The OS makes no attempt to avoid handing arbitrary bytes to applications. If you just call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fn.decode(sys.getfilesystemencoding())&lt;/code&gt; in attempt to make turn your paths into Unicode text, Python will crash sometimes.&lt;/p&gt;

&lt;p&gt;So, we must conclude that paths are bytes. But here&amp;rsquo;s the other thing: on Windows, &lt;em&gt;paths are fundamentally text&lt;/em&gt;. The equivalent interfaces on Windows accept and return &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/ff381407(v=vs.85).aspx&quot;&gt;wide character strings&lt;/a&gt;&amp;mdash;and on Python, that means &lt;a href=&quot;https://docs.python.org/2/library/functions.html#unicode&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode&lt;/code&gt;&lt;/a&gt; objects. So our grand plan to use bytes as the one true path representation is foiled.&lt;/p&gt;

&lt;p&gt;It gets worse: to use full-length paths on Windows, you need to &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx&quot;&gt;prefix them with the four magical characters &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\\?\&lt;/code&gt;&lt;/a&gt;. Every time. I know.&lt;/p&gt;

&lt;p&gt;This contradiction is the root of all evil.
It&amp;rsquo;s the cause of a huge amount of fiddly boilerplate code in beets, a good number of bug reports, and a lot of sadness during our current port to Python 3.&lt;/p&gt;

&lt;h2 id=&quot;our-conventions&quot;&gt;Our Conventions&lt;/h2&gt;

&lt;p&gt;In beets, we adhere to a set of &lt;a href=&quot;https://github.com/beetbox/beets/wiki/Hacking#handling-paths&quot;&gt;coding conventions&lt;/a&gt; that, when ruthlessly enforced, avoid potential problems.&lt;/p&gt;

&lt;p&gt;First, we need one consistent type to store in our database. We picked bytes. This way, we can consistently represent Unix filesystems, and it requires only a bit of hacking to support Windows too. In particular, beets encodes all filenames using a fixed encoding (UTF-8) on Windows, just so that the path type is always bytes on all platforms.&lt;/p&gt;

&lt;p&gt;To make this all work, we use three pervasive little &lt;a href=&quot;https://github.com/beetbox/beets/blob/master/beets/util/__init__.py&quot;&gt;utility functions&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We use &lt;a href=&quot;https://github.com/beetbox/beets/blob/42d642f1f603645ca8c3f6b0a17cd3048ef857c8/beets/util/__init__.py#L316-L334&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bytestring_path&lt;/code&gt;&lt;/a&gt; to force all paths to our consistent representation. If you don&amp;rsquo;t know where a path came from, you can just pass it through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bytestring_path&lt;/code&gt; to rectify it before proceeding.&lt;/li&gt;
  &lt;li&gt;The opposite function, &lt;a href=&quot;https://github.com/beetbox/beets/blob/42d642f1f603645ca8c3f6b0a17cd3048ef857c8/beets/util/__init__.py#L337-L353&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayable_path&lt;/code&gt;&lt;/a&gt;, must be used to format error messages and log output. It does its best to decode the path to human-readable Unicode text, and it&amp;rsquo;s not allowed to fail&amp;mdash;but it&amp;rsquo;s &lt;em&gt;lossy&lt;/em&gt;. The result is only good for human consumption, not for returning back to the OS. Hence the name, which is intentionally not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode_path&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Every argument to an OS function like &lt;a href=&quot;https://docs.python.org/2/library/functions.html#open&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;open&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://docs.python.org/2/library/os.html#os.listdir&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listdir&lt;/code&gt;&lt;/a&gt; must pass through the third utility: &lt;a href=&quot;https://github.com/beetbox/beets/blob/42d642f1f603645ca8c3f6b0a17cd3048ef857c8/beets/util/__init__.py#L356-L387&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;syspath&lt;/code&gt;&lt;/a&gt;. Think of this as converting from beets&amp;rsquo;s internal representation to the OS&amp;rsquo;s own representation. On Unix, this is a no-op: the representations are the same. On Windows, this converts a bytestring path back to Unicode and then adds the ridiculous &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\\?\&lt;/code&gt; prefix&lt;/a&gt;, which avoids problems with long names.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It&amp;rsquo;s not fun to force everybody to use these utilities everywhere, but it does work. Since we instated this policy, Unicode-related bugs still happen, but they&amp;rsquo;re not nearly as pervasive as they were in the project&amp;rsquo;s early days.&lt;/p&gt;

&lt;h2 id=&quot;must-it-be-this-way&quot;&gt;Must It Be This Way?&lt;/h2&gt;

&lt;p&gt;Although our solution works, I won&amp;rsquo;t pretend to love it. Here are a few alternatives we might consider for the future.&lt;/p&gt;

&lt;h3 id=&quot;python-3s-surrogate-escapes&quot;&gt;Python 3&amp;rsquo;s Surrogate Escapes&lt;/h3&gt;

&lt;p&gt;Python 3 chose the opposite answer to the root-of-all-evil contradiction: paths are always Unicode strings, not bytes. It invented &lt;a href=&quot;https://www.python.org/dev/peps/pep-0383/&quot;&gt;surrogate escapes&lt;/a&gt; to represent bytes that didn&amp;rsquo;t fit the platform&amp;rsquo;s purported filesystem encoding. This way, Python 3&amp;rsquo;s Unicode &lt;a href=&quot;https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;str&lt;/code&gt;&lt;/a&gt; can represent arbitrary bytes in filenames. (The first commit to beets happened a bit before Python 3.0 was released, so perhaps the project can be forgiven for not adopting this approach in the first place.)&lt;/p&gt;

&lt;p&gt;A few lingering details still worry me about surrogate escapes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Migrating people&amp;rsquo;s databases containing old bytes paths to surrogate-escaped strings won&amp;rsquo;t exactly be fun.&lt;/li&gt;
  &lt;li&gt;Might surrogate escapes tie us too much to the Python ecosystem? What happens when you try to send one of these paths to another non-Python tool that interacts with the same filesystem?&lt;/li&gt;
  &lt;li&gt;People in the Python community have misgivings about the current implementation of surrogate escapes. &lt;a href=&quot;https://thoughtstreams.io/ncoghlan_dev/missing-pieces-in-python-3-unicode/&quot;&gt;Nick Coghlan summarizes.&lt;/a&gt; We&amp;rsquo;ll need to investigate the nuances ourselves.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;require-utf-8-everywhere&quot;&gt;Require UTF-8 Everywhere&lt;/h3&gt;

&lt;p&gt;One day, I believe we will live in a world where everything is UTF-8 all the time. We could hasten that glorious day by requiring that all paths be UTF-8 and either rejecting or fixing any other filenames as they come into beets. For now, though, this seems a just tad user-hostile for a program that works so closely with your files.&lt;/p&gt;

&lt;h3 id=&quot;pathlib&quot;&gt;Pathlib&lt;/h3&gt;

&lt;p&gt;We could switch to Python 3&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/pathlib.html&quot;&gt;pathlib&lt;/a&gt; module. We&amp;rsquo;d still need to choose a uniform representation for putting these paths into our database, though, and it&amp;rsquo;s not clear how well the Python 2 backport works. But we do have &lt;a href=&quot;https://github.com/beetbox/beets/issues/1409&quot;&gt;a ticket for it&lt;/a&gt;.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>flexible attributes, in which schemaless and schemaful coexist peacefully</title>
        <link href="http://beets.io/blog/flexattr.html"/>
        <updated>2013-09-02T00:00:00+00:00</updated>
        <id>http://beets.io/blog/flexattr</id>
        <content type="html">&lt;p&gt;Since &lt;a href=&quot;https://github.com/beetbox/beets/commit/ee7bb4b9e8932cc186e46e7846a2d0006535c0c5&quot;&gt;the beginning&lt;/a&gt;, beets has been conceptually built around an object&amp;ndash;field data model. Your library is made up of &lt;em&gt;item&lt;/em&gt; objects, which represent audio files, and each item has a bunch of &lt;em&gt;fields&lt;/em&gt; corresponding to tags on those files: &amp;ldquo;artist,&amp;rdquo; &amp;ldquo;title,&amp;rdquo; and &amp;ldquo;year,&amp;rdquo; for example.&lt;/p&gt;

&lt;p&gt;Over the years, beets has grown to support a long &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/pathformat.html#available-values&quot;&gt;list of fields&lt;/a&gt;: by my count, we currently support 59. We&amp;rsquo;ve used an informal policy of adding almost every reasonable field that a user requests. But even this liberal approach can be too constricting: adding a new field at least requires getting the attention of a developer and then waiting for another beets release to roll around.&lt;/p&gt;

&lt;p&gt;But what if users could add any field they could think of, without needing to rely on changes to beets itself? Decoupling the list of fields from the beets schema would enable awesome new use cases, great new plugins that aren&amp;rsquo;t possible today, and even simplify the development of some &lt;a href=&quot;https://github.com/beetbox/beets/issues/111&quot;&gt;oft-requested features&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;flexible-attributes&quot;&gt;Flexible Attributes&lt;/h2&gt;

&lt;p&gt;The next version of beets, 1.3, overhauls the internals to support &lt;em&gt;flexible attributes&lt;/em&gt; (affectionately known as flexattrs). You&amp;rsquo;re no longer constrained to the fields that beets ships with: you can now store information in &lt;em&gt;any field name at all&lt;/em&gt;. For example, maybe you&amp;rsquo;d like to start classifying your music based on its &lt;em&gt;mood&lt;/em&gt;. Even though beets itself doesn&amp;rsquo;t use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mood&lt;/code&gt; field, you can set it on your music using the &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/cli.html#modify&quot;&gt;modify&lt;/a&gt; command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ beet modify mood=sexy artist:miguel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, later, if you want to see all your music whose mood is &amp;ldquo;sexy,&amp;rdquo; just use the &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/cli.html#list&quot;&gt;list&lt;/a&gt; command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ beet ls mood:sexy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is exciting enough on its own&amp;mdash;you can classify music based on any criteria you can think of&amp;mdash;but it also means many more possibilities for plugins in the future. (&lt;a href=&quot;https://github.com/beetbox/beets/issues/310&quot;&gt;Here&lt;/a&gt; &lt;a href=&quot;https://github.com/beetbox/beets/pull/101&quot;&gt;a&lt;/a&gt; &lt;a href=&quot;https://github.com/beetbox/beets/issues/111&quot;&gt;are&lt;/a&gt; &lt;a href=&quot;https://github.com/beetbox/beets/issues/266&quot;&gt;few&lt;/a&gt; &lt;a href=&quot;https://github.com/beetbox/beets/issues/116&quot;&gt;ideas&lt;/a&gt;.)&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it Works&lt;/h2&gt;

&lt;p&gt;I procrastinated for a long time in implementing flexible attributes because a clean, maintainable implementation was surprisingly tricky to get right. (This post is going to get a little nerdy from here on out; if you&amp;rsquo;re interested in the technical details, read on.)&lt;/p&gt;

&lt;p&gt;As background, beets libraries are stored in &lt;a href=&quot;http://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; databases. The schema is simple: there&amp;rsquo;s an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;items&lt;/code&gt; table and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;albums&lt;/code&gt; table, and each field is a column on these tables. To support flexattrs, I initially considered switching to a schema-free database: &lt;a href=&quot;https://github.com/Yelp/sqlite3dbm/&quot;&gt;abusing SQLite as a key&amp;ndash;value store&lt;/a&gt;, &lt;a href=&quot;https://github.com/sampsyo/jsonshelve&quot;&gt;serializing the whole database to JSON&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/leveldb/&quot;&gt;LevelDB&lt;/a&gt;, or the like. With the help of other developers, however, I eventually realized that we could tack on flexible attributes alongside the existing &lt;em&gt;fixed&lt;/em&gt; attributes and minimize the disruption.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s what it looks like from a developer&amp;rsquo;s perspective. If you write:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;item.artist = &apos;Rhye&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;beets updates the fixed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;artist&lt;/code&gt; attribute, which is backed by an SQLite column. If you make up your own field name:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;item.mood = &apos;ethereal&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;then beets detects that you&amp;rsquo;re defining a flexattr and updates a separate key&amp;ndash;value table instead. The idea here is that code that accesses fixed or flexible attributes should look identical&amp;mdash;that way, we can prototype code using flexible attributes and eventually promote the attribute to built-in field with zero changes to the client code. Three cheers for sound abstractions!&lt;/p&gt;

&lt;h3 id=&quot;queries-fast-and-slow&quot;&gt;Queries, Fast and Slow&lt;/h3&gt;

&lt;p&gt;Things get slightly more complicated when querying. For example, to query the database for songs by &lt;a href=&quot;http://www.officialmiguel.com/home&quot;&gt;Miguel&lt;/a&gt;, beets constructs a SQL query like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT * FROM items WHERE artist = &apos;Miguel&apos;;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This query can be fast because it&amp;rsquo;s performed entirely by the native SQLite library; no Python is involved. But to match on a flexible attribute, we&amp;rsquo;d need to generate complex query with a JOIN on the flexattr table. This gets especially hairy when wildcard-matching on any field or using a special query type like &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/query.html#regular-expressions&quot;&gt;regex queries&lt;/a&gt; or &lt;a href=&quot;http://beets.readthedocs.org/page/plugins/fuzzy.html&quot;&gt;fuzzy queries&lt;/a&gt;. To avoid this complexity, beets instead evaluates queries involving flexattrs &lt;em&gt;in Python&lt;/em&gt;. It fetches every item in the database and &amp;ldquo;manually&amp;rdquo; checks the query against each row. This is probably slower than the SQLite query used for purely fixed attribute queries, but it&amp;rsquo;s probably not much slower&amp;mdash;if at all&amp;mdash;than the complex JOIN on the flexattr table that would otherwise be required.&lt;/p&gt;

&lt;p&gt;To support flexattr queries, beets now classifies all queries into &lt;em&gt;fast&lt;/em&gt; and &lt;em&gt;slow&lt;/em&gt; queries. As always, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Query&lt;/code&gt; objects have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clause()&lt;/code&gt; method that returns the SQLite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WHERE&lt;/code&gt; expression that evaluates them and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match(item)&lt;/code&gt; method that returns a boolean indicating whether a particular item passes the query. With the flexattr changes, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clause()&lt;/code&gt; method can now return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;, indicating that the query is &lt;em&gt;slow&lt;/em&gt; and cannot be evaluated in SQLite. The library then falls back to one-at-a-time predication.&lt;/p&gt;

&lt;p&gt;This fast/slow distinction also had the knock-on effect of simplifying the way beets handles &amp;ldquo;special&amp;rdquo; queries like &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/query.html#regular-expressions&quot;&gt;regexes&lt;/a&gt; and &lt;a href=&quot;http://beets.readthedocs.org/page/plugins/fuzzy.html&quot;&gt;fuzzy matching&lt;/a&gt;. These query types can&amp;rsquo;t easily be written in SQLite, so they&amp;rsquo;re implemented as Python predicates. Previously, to shoehorn everything into a SQLite query, we had to &lt;a href=&quot;http://docs.python.org/2/library/sqlite3.html#sqlite3.Connection.create_function&quot;&gt;&lt;em&gt;register&lt;/em&gt; a Python function as a callback&lt;/a&gt; and then name the function in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clause()&lt;/code&gt; result. Crossing the Python/C barrier is not great for performance, so sticking to SQLite queries here was likely not buying us much. Now, any query involving a complex predicate implemented in Python is just evaluated using the slow path&amp;mdash;eliminating the need for callback registration.&lt;/p&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;I&amp;rsquo;m happy with the overall design for flexible attributes, but there&amp;rsquo;s a laundry list of things we can do better with over time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ideally, it should be easy to &amp;ldquo;promote&amp;rdquo; a flexible attribute to a built-in fixed attribute. This almost works already, but we&amp;rsquo;ll need a way to transparently move the data from the flexattr tables to the main &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;item&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;album&lt;/code&gt; tables. We can cross this bridge when some field eventually needs migration.&lt;/li&gt;
  &lt;li&gt;A query that matches on both a fixed attribute and a flexible attribute, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;artist:miguel mood:sexy&lt;/code&gt;, is currently classified as &amp;ldquo;slow&amp;rdquo;&amp;mdash;any slow component makes the whole query slow. This could be optimized by matching the fast component in SQLite and then filtering on the slow component in Python. In general, we need to measure the performance of this feature to see whether optimizations like this are necessary.&lt;/li&gt;
  &lt;li&gt;I&amp;rsquo;m not ruling out an eventual move to a completely schema-free design, perhaps using a simple key&amp;ndash;value store like &lt;a href=&quot;http://code.google.com/p/leveldb/&quot;&gt;LevelDB&lt;/a&gt; or a putative &lt;a href=&quot;http://sqlite.org/src4/doc/trunk/www/design.wiki&quot;&gt;SQLite4&lt;/a&gt;. Eliminating the distinction between the two types of attributes will pay off in simplicity.&lt;/li&gt;
  &lt;li&gt;Beets needs a system for specifying value types. Currently, we have an ad-hoc data type classification that, for example, knows to print bitrates in KHz and track numbers with zero padding. We need a more principled way to specify conversions to and from strings&amp;mdash;at the same time, we should let plugins specify this type information for attributes they define.&lt;/li&gt;
&lt;/ul&gt;

</content>
    </entry>
    
    <entry>
        <title>moving from Google Code to GitHub: a horrible, ultimately rewarding odyssey</title>
        <link href="http://beets.io/blog/github-issues.html"/>
        <updated>2013-02-28T00:00:00+00:00</updated>
        <id>http://beets.io/blog/github-issues</id>
        <content type="html">&lt;p&gt;The beets project began on May 14, 2008 with my first commit to a Subversion repository. On that day, &lt;a href=&quot;http://github.com/&quot;&gt;GitHub&lt;/a&gt; was &lt;a href=&quot;https://github.com/blog/40-we-launched&quot;&gt;four days old&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Had I known of GitHub then, I never would have started the project on Google Code. But I didn&amp;rsquo;t, so I did, and it&amp;rsquo;s been a hell of a time gradually moving the project over. Moving the code was easy and happened earliest. (The repository is also mirrored &lt;a href=&quot;https://bitbucket.org/adrian/beets&quot;&gt;on BitBucket&lt;/a&gt; as a Mercurial repo.) The wiki was its own ordeal and required manual conversion from Google Code&amp;rsquo;s proprietary wiki syntax to Markdown (thanks, vim macros). But I procrastinated on the hardest part: the issues. Today, I finally moved that last puzzle piece over.&lt;/p&gt;

&lt;p&gt;While I&amp;rsquo;m excited to finally move from an aging, neglected issue tracker to a shiny, well-supported new thing, the process probably could not have been more painful. Here are some tips for other open-source projects looking to make the same transition.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Start early! This was only made more painful by having more than 500 issues in the Google Code tracker. I wish I had not procrastinated so long.&lt;/li&gt;
  &lt;li&gt;Use the handy, hacky &lt;a href=&quot;https://github.com/arthur-debert/google-code-issues-migrator&quot;&gt;google-code-issues-migrator script&lt;/a&gt; by @arthur-debert on GitHub.&lt;/li&gt;
  &lt;li&gt;But be aware that this project suffers from a peculiar kind of tragedy of the commons. Everybody needs this script &lt;em&gt;once&lt;/em&gt; but no one is motivated to maintain it long term. So take a look at the repository&amp;rsquo;s &lt;a href=&quot;https://github.com/arthur-debert/google-code-issues-migrator/network&quot;&gt;forks&lt;/a&gt; and choose one by a reasonably recent pilgrim. &lt;a href=&quot;https://github.com/sampsyo/google-code-issues-migrator&quot;&gt;Here&amp;rsquo;s mine.&lt;/a&gt; As messy as the code and the network of forks is, this little cottage industry is an incredible testament to the open-source bazaar and how GitHub is able to facilitate it.&lt;/li&gt;
  &lt;li&gt;To test your migration, I suggest creating a temporary, private repository that won&amp;rsquo;t bug anyone as you open and close a million issues. This way, I was able to debug lots of problems with labels, which you don&amp;rsquo;t get to see when performing a dry run.&lt;/li&gt;
  &lt;li&gt;You&amp;rsquo;re going to need to hack the script for your particular needs. Here are some things I did:
    &lt;ul&gt;
      &lt;li&gt;I cobbled together some features from various forks of the repository, such as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--skip_closed&lt;/code&gt; flag. (Beets has hundreds of closed issues that I don&amp;rsquo;t need clogging up the issue tracker.)&lt;/li&gt;
      &lt;li&gt;I added a flag to turn off comments and issue bodies. The issues now just link back to Google Code for their historical context. This works for us because the issues in the old tracker are old and have lots of comments, some of which are totally irrelevant. The original report text is also not usually very relevant out of context (although the title usually is).&lt;/li&gt;
      &lt;li&gt;I changed the script to only migrate labels that have a configured mapping since I&amp;rsquo;ve resolved to fiddle with labels less and use a simpler organization scheme.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Here&amp;rsquo;s the worst part: since there&amp;rsquo;s no way to avoid creating each issue in turn, GitHub is going to send an email for every new issue that gets copied over. There&amp;rsquo;s no awesome way to get around this; you&amp;rsquo;ll just have to apologize to all your repository&amp;rsquo;s watchers. (I tried temporarily removing all the repository collaborators, but this does not, unfortunately, unsubscribe them.)&lt;/li&gt;
  &lt;li&gt;I did, however, use a temporary user to do the migration instead of my main account. This helped avoid cluttering my history with 89 different &amp;ldquo;created issue&amp;rdquo; events. Since I deleted that user, the migrated issues are now marked as having been created by a cute little &lt;a href=&quot;https://github.com/ghost&quot;&gt;ghost&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Finally, Google code lets you replace a project tab with a wiki page. I used this to &lt;a href=&quot;http://code.google.com/p/beets/wiki/Issues?tm=3&quot;&gt;redirect users to the GitHub issue tracker&lt;/a&gt;. Fortunately, this does not prevent access to old issues (which are linked from GitHub).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As unpleasant as it was (as I write this, I&amp;rsquo;m &lt;em&gt;still&lt;/em&gt; deleting straggling notification spam), I recommend moving from Google Code to GitHub Issues. This is an observation that&amp;rsquo;s been made umpteen times before, but I remember when Google Code was an incredible relief from the antiquated and convoluted SourceForge&amp;mdash;and this move has been every bit as satisfying.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>your musical year in review</title>
        <link href="http://beets.io/blog/musical-year.html"/>
        <updated>2013-01-04T00:00:00+00:00</updated>
        <id>http://beets.io/blog/musical-year</id>
        <content type="html">&lt;p&gt;Against my better judgment, I tend to think of music in years. Some part of me feels like it understands music better in the context of its time&amp;mdash;what other bands were doing contemporaneously, the year&amp;rsquo;s major headlines, and, most importantly, the phase of my life when I first heard an album I love.&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s the same instinct that, over the last few weeks, has flooded the Web with top-&lt;em&gt;N&lt;/em&gt; lists. Even if I never write it down, I find myself making a list each winter of the &amp;ldquo;best&amp;rdquo; albums of the year. I&amp;rsquo;m not a music critic, but in January, I sometimes wish I were: as utterly subjective as my yearly mental list is, it helps me close the books on 2012 and file its music away in my memory before moving on to 2013&amp;rsquo;s releases.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s how beets can help you with your own retrospective. First and foremost, you probably want to see a list of all your albums that were released last year:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ beets ls -a year:2012
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(I use this query&amp;mdash;or its &lt;a href=&quot;http://beets.readthedocs.org/en/1.0rc2/plugins/rdm.html&quot;&gt;randomized&lt;/a&gt; counterpart&amp;mdash;almost daily throughout the year to decide what to listen to.) You might also be curious to see how many albums you collected in the year:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ beet ls -a year:2012 | wc -l
85
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can take the chronology-worshiping one step further using full release dates. Since most albums in &lt;a href=&quot;http://musicbrainz.org/&quot;&gt;MusicBrainz&lt;/a&gt; have release months and years, we can sort these albums by date:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ beet ls -af &apos;$month-$day $albumartist - $album&apos; year:2012 | sort -n
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That query helps me &amp;ldquo;replay&amp;rdquo; the year from start to finish when thinking about each album.&lt;/p&gt;

&lt;p&gt;This kind of flexible collection browsing is one of the reasons I originally started building beets. I hope it helps you look back over the music of 2012.&lt;/p&gt;

&lt;h2 id=&quot;my-favorites&quot;&gt;My Favorites&lt;/h2&gt;

&lt;p&gt;For whatever it&amp;rsquo;s worth, here&amp;rsquo;s a sort, predictable list of some albums I remember fondly from last year, copied &amp;rsquo;n pasted out of my terminal window in no particular order:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;alt-J - An Awesome Wave&lt;/li&gt;
  &lt;li&gt;Passion Pit - Gossamer&lt;/li&gt;
  &lt;li&gt;Santigold - Master of My Make-Believe&lt;/li&gt;
  &lt;li&gt;Jack White - Blunderbuss&lt;/li&gt;
  &lt;li&gt;Dirty Projectors - Swing Lo Magellan&lt;/li&gt;
  &lt;li&gt;How to Dress Well - Total Loss&lt;/li&gt;
  &lt;li&gt;Miguel - Kaleidoscope Dream&lt;/li&gt;
  &lt;li&gt;Bob Mould - Silver Age&lt;/li&gt;
  &lt;li&gt;Kendrick Lamar - good kid, m.A.A.d city&lt;/li&gt;
  &lt;li&gt;Macklemore &amp;amp; Ryan Lewis - The Heist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everyone should clearly love all of these records as much as I do.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>rescheduling 1.0</title>
        <link href="http://beets.io/blog/rescheduling.html"/>
        <updated>2012-11-27T00:00:00+00:00</updated>
        <id>http://beets.io/blog/rescheduling</id>
        <content type="html">&lt;p&gt;I &lt;a href=&quot;http://beets.io/blog/one-point-oh.html&quot;&gt;blogged previously&lt;/a&gt; that beets would see two more betas&amp;mdash;16 and 17&amp;mdash;before hitting 1.0. The first upcoming beta version would include a complete overhaul of the configuration system.&lt;/p&gt;

&lt;p&gt;Since that post in August, something awesome has happened: the beets community has contributed an collection of &lt;a href=&quot;http://beets.readthedocs.org/en/latest/changelog.html&quot;&gt;new features and fixes&lt;/a&gt; to beets, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;em&gt;Convert&lt;/em&gt; plugin for transcoding and embedding album art in copies.&lt;/li&gt;
  &lt;li&gt;A convenient &lt;em&gt;Fuzzy Search&lt;/em&gt; plugin.&lt;/li&gt;
  &lt;li&gt;The aptly-named &lt;em&gt;The&lt;/em&gt; plugin, which helps format strings for sorting.&lt;/li&gt;
  &lt;li&gt;A &lt;em&gt;Zero&lt;/em&gt; plugin for nulling out certain fields.&lt;/li&gt;
  &lt;li&gt;The new &lt;em&gt;IHate&lt;/em&gt; plugin to help you skip over albums.&lt;/li&gt;
  &lt;li&gt;A completely rewritten and more stable version of the &lt;em&gt;ReplayGain&lt;/em&gt; plugin.&lt;/li&gt;
  &lt;li&gt;Album art image resizing.&lt;/li&gt;
  &lt;li&gt;&amp;hellip; and much, much more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A million thanks to all the contributors over the last few months (see the &lt;a href=&quot;http://beets.readthedocs.org/en/latest/changelog.html&quot;&gt;changelog&lt;/a&gt; for credits).&lt;/p&gt;

&lt;p&gt;Work on the as-planned beta 16 has been going on concurrently under the &lt;a href=&quot;https://github.com/beetbox/beets/tree/confit&quot;&gt;confit branch&lt;/a&gt;. But, at this point, it would be unwise to unleash all these great new contributed features in the same release as the major refactoring that &lt;a href=&quot;https://github.com/sampsyo/confit&quot;&gt;Confit&lt;/a&gt; entails.&lt;/p&gt;

&lt;p&gt;So the release schedule is changing. The current development version will become 1.0 RC 1 in the next few days. After a testing period, we&amp;rsquo;ll release 1.0. At this point, the Confit branch will be developed as 1.1. This will let us fix bugs in the stable version of beets while we polish up 1.1 as the next major version.&lt;/p&gt;

&lt;p&gt;To summarize: 1.0 very soon; Confit right after that. Here goes!&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>the end of Twitter syndication is nigh</title>
        <link href="http://beets.io/blog/twitter.html"/>
        <updated>2012-10-06T00:00:00+00:00</updated>
        <id>http://beets.io/blog/twitter</id>
        <content type="html">&lt;p&gt;Since before beets had a &lt;a href=&quot;http://beets.io/blog/&quot;&gt;real blog&lt;/a&gt; of its own, I&amp;rsquo;ve been using the &lt;a href=&quot;https://twitter.com/b33ts&quot;&gt;@b33ts&lt;/a&gt; Twitter account as a simple, zero-effort way to make announcements about the project and keep users up to date. Most of the news that comes out of a small open-source project like beets doesn&amp;rsquo;t warrant a full blog post, so 140-character summaries are usually enough. And the shorter format encourages me to post more often.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve also always syndicated the Twitter feed to &lt;a href=&quot;http://beets.io&quot;&gt;beets&amp;rsquo; homepage&lt;/a&gt; (you can see it on the right hand side there). I like having the latest news from Twitter up there to show visitors that beets, unlike so many other open-source projects, is under active development. And returning users get to see what&amp;rsquo;s new at a glance without my needing to cross-post to the blog.&lt;/p&gt;

&lt;p&gt;With &lt;a href=&quot;https://dev.twitter.com/blog/changes-coming-to-twitter-api&quot;&gt;Twitter&amp;rsquo;s new API restrictions&lt;/a&gt;, this kind of syndication is no longer possible. Twitter is exerting more control over how tweets are displayed and this news feed violates the new display requirements. I&amp;rsquo;m not shocked by this development. In fact, it always surprised me that this use case was allowed in the first place&amp;mdash;I&amp;rsquo;m making demands on Twitter&amp;rsquo;s servers and giving them nothing in return. So, while it will be a hassle to switch to a different solution, I understand that it needs to be done.&lt;/p&gt;

&lt;p&gt;But what should would-be syndicators do instead? This is an honest question&amp;mdash;I don&amp;rsquo;t know of a good solution that obeys Twitter&amp;rsquo;s new rules. I like being able to post with the many &lt;a href=&quot;http://tapbots.com/tweetbot/&quot;&gt;awesome Twitter clients&lt;/a&gt; out there and I&amp;rsquo;m sure users want to see updates in their streams, so I will keep using the @b33ts account. But the news feed on the homepage is also too useful to give up. Here are the alternatives I know of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use Twitter&amp;rsquo;s new &lt;a href=&quot;https://twitter.com/settings/widgets&quot;&gt;official widgets&lt;/a&gt;. These widgets are large, clunky, and image-laden&amp;mdash;I&amp;rsquo;m not sure where Twitter intends these to be used (&lt;a href=&quot;http://www.margaretatwood.ca&quot;&gt;MargaretAtwood.ca&lt;/a&gt;, I guess?), but it&amp;rsquo;s certainly not in a sidebar element. (The old twitter widgets, which featured a less visually intrusive design, &lt;a href=&quot;https://dev.twitter.com/docs/embedded-timelines&quot;&gt;are deprecated&lt;/a&gt;.)&lt;/li&gt;
  &lt;li&gt;Cross-post. Twitter&amp;rsquo;s new rules restrict how you get data &lt;em&gt;out&lt;/em&gt; of Twitter&amp;mdash;not how you &lt;em&gt;add&lt;/em&gt; data to Twitter. This means that I could &amp;ldquo;microblog&amp;rdquo; (is that still a word?) somewhere else&amp;mdash;on &lt;a href=&quot;http://pages.github.com&quot;&gt;GitHub Pages&lt;/a&gt;, say&amp;mdash;and run a cron job to post &lt;em&gt;those&lt;/em&gt; updates to Twitter. Syndicate in, not out.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://tent.io/&quot;&gt;Tent&lt;/a&gt; or &lt;a href=&quot;http://app.net/&quot;&gt;App.net&lt;/a&gt;, I guess?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are as simple as my current setup with Twitter. Do you have a better idea for maintaining this use case? Please &lt;a href=&quot;mailto:adrian@radbox.org&quot;&gt;send me email&lt;/a&gt; or&amp;mdash;if you&amp;rsquo;re not too bitter about it yet&amp;mdash;tweet at &lt;a href=&quot;https://twitter.com/b33ts&quot;&gt;@b33ts&lt;/a&gt;.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>beets and Python 3</title>
        <link href="http://beets.io/blog/py3k.html"/>
        <updated>2012-09-09T00:00:00+00:00</updated>
        <id>http://beets.io/blog/py3k</id>
        <content type="html">&lt;p&gt;&lt;a href=&quot;http://docs.python.org/dev/&quot;&gt;Python 3&lt;/a&gt; is the future. Every day, it seems, I come across a Python 3 feature that I wish I could use in beets (to name a few: &lt;a href=&quot;http://docs.python.org/dev/library/functools.html#functools.lru_cache&quot;&gt;lru_cache&lt;/a&gt;, &lt;a href=&quot;http://www.python.org/dev/peps/pep-0380/&quot;&gt;yield from&lt;/a&gt;, &lt;a href=&quot;http://www.python.org/dev/peps/pep-0420/&quot;&gt;working namespace packages&lt;/a&gt;, and my own patch, &lt;a href=&quot;http://docs.python.org/dev/library/argparse.html#sub-commands&quot;&gt;aliases in argparse&lt;/a&gt;). And with the release of &lt;a href=&quot;http://docs.python.org/dev/whatsnew/3.3.html&quot;&gt;Python 3.3&lt;/a&gt; imminent, I think the age of widespread adoption is nearly here.&lt;/p&gt;

&lt;p&gt;So when will beets move to Python 3? As with so many other projects, beets&amp;rsquo; transition is blocked on its dependencies. Before we can make the jump, all of these libraries, which are required by beets &amp;ldquo;core&amp;rdquo; or its included plugins, need to go first:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://code.google.com/p/mutagen/&quot;&gt;Mutagen&lt;/a&gt;, the audio tag manipulation library. This is beets&amp;rsquo; main barrier&amp;mdash;Mutagen is an integral part of beets; it will be complex to port due to its Unicode-related logic; and I have no intention of switching to a different library (&lt;a href=&quot;http://taglib.github.com&quot;&gt;TagLib&lt;/a&gt; being the only viable alternative I know of).&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://software.clapper.org/munkres/&quot;&gt;Munkres&lt;/a&gt;, a bipartite matching solver. Should be a straightforward port.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/alastair/python-musicbrainz-ngs&quot;&gt;python-musicbrainz-ngs&lt;/a&gt;, bindings to the &lt;a href=&quot;http://musicbrainz.org&quot;&gt;MusicBrainz&lt;/a&gt; API. I and the other contributors to this library have been moving toward Python 3 compatibility for some time.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/beetbox/pyacoustid&quot;&gt;pyacoustid&lt;/a&gt; and &lt;a href=&quot;https://github.com/beetbox/audioread&quot;&gt;audioread&lt;/a&gt; support beets&amp;rsquo; acoustic fingerprinting plugin. I wrote both of these libraries and should be able to port them with minimal effort.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gstreamer.freedesktop.org/modules/gst-python.html&quot;&gt;python-gstreamer&lt;/a&gt; is used by the &lt;a href=&quot;http://beets.readthedocs.org/en/1.0b15/plugins/bpd.html&quot;&gt;BPD plugin&lt;/a&gt; and the current incarnation of the &lt;a href=&quot;http://beets.readthedocs.org/en/1.0b15/plugins/replaygain.html&quot;&gt;ReplayGain plugin&lt;/a&gt;. I believe that forward-compatibility just entails moving to &lt;a href=&quot;https://wiki.ubuntu.com/Novacut/GStreamer1.0&quot;&gt;Gstreamer 1.0&lt;/a&gt; and PyGI.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;Flask&lt;/a&gt;, the basis of the &lt;a href=&quot;http://beets.readthedocs.org/en/1.0b15/plugins/web.html&quot;&gt;Web interface plugin&lt;/a&gt;, currently &lt;a href=&quot;http://flask.pocoo.org/docs/advanced_foreword/?highlight=python#the-status-of-python-3&quot;&gt;does not support Python 3&lt;/a&gt;. The author, Armin Ronacher, has been &lt;a href=&quot;http://lucumr.pocoo.org/2011/12/7/thoughts-on-python3/&quot;&gt;critical of some changes in Python 3&lt;/a&gt;, especially with regard to Web applications and frameworks. So Armin may need to help fix Python itself first before Flask and Werkzeug become 3-compatible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some dependencies (&lt;a href=&quot;http://pypi.python.org/pypi/Unidecode/&quot;&gt;Unidecode&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/pylast/&quot;&gt;pyLast&lt;/a&gt;, &lt;a href=&quot;http://pypi.python.org/pypi/colorama&quot;&gt;Colorama&lt;/a&gt;, and &lt;a href=&quot;https://github.com/sampsyo/bluelet&quot;&gt;Bluelet&lt;/a&gt;) are already compatible.&lt;/p&gt;

&lt;p&gt;The principal blockers, then, are Mutagen and Flask. The remaining dependencies either have clear upgrade paths or are simple enough that I should be able to contribute compatibility patches back to them. But Mutagen and Flask are too complex (and popular) to make a unilateral porting effort feasible. This means that, if you want to help bring beets to Python 3, the best place to start is with porting Mutagen.&lt;/p&gt;

&lt;p&gt;In the meantime, I&amp;rsquo;m writing beets to be as &lt;a href=&quot;https://github.com/beetbox/beets/wiki/Python3&quot;&gt;forward-compatible&lt;/a&gt; as possible. An eventual port will still be a headache&amp;mdash;due to the need to &lt;a href=&quot;https://github.com/beetbox/beets/wiki/Hacking&quot;&gt;explicitly manage&lt;/a&gt; a distinction between Unicode and raw-bytes path names&amp;mdash;but I&amp;rsquo;m excited to do the work as soon as it&amp;rsquo;s feasible.&lt;/p&gt;

&lt;p&gt;When the leap does eventually happen, I plan to leave Python 2 behind entirely. Beets is not widely used as a library (&lt;a href=&quot;https://github.com/rembo10/headphones/&quot;&gt;Headphones&lt;/a&gt; is the only dependent I&amp;rsquo;m aware of) and most Linux distributions these days ship with some version of Python 3. Going 3-exclusive will make maintaining beets&amp;rsquo; bytes/Unicode distinction much simpler.&lt;/p&gt;

&lt;p&gt;While beets will almost certainly not make the Python 3 leap until after &lt;a href=&quot;http://beets.io/blog/one-point-oh.html&quot;&gt;1.0&lt;/a&gt;, that day is not as distant as it might seem at first.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>the SQLite lock timeout nightmare</title>
        <link href="http://beets.io/blog/sqlite-nightmare.html"/>
        <updated>2012-08-24T00:00:00+00:00</updated>
        <id>http://beets.io/blog/sqlite-nightmare</id>
        <content type="html">&lt;p&gt;Software has bugs. There are little bugs: the &lt;a href=&quot;http://readthedocs.org/docs/beets/-/changelog.html&quot;&gt;beets release notes&lt;/a&gt; are saturated with them. And then there are the monstrous, enormous bugs: the kind that follow you from version to version, from year to year.&lt;/p&gt;

&lt;p&gt;This is a story about one of those bugs. It existed in eleven releases of beets over almost two years. The problem stuck around for so long because it seemed to manifest exclusively on other people&amp;rsquo;s machines. Until the day I finally fixed it, I never reproduced the bug once on my own box.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s what the bug looked like to users who experienced it: beets is running along normally, happily chewing through your multi-terabyte music collection and making corrections. Then, seemingly at random, it crashes and spits out:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sqlite3.OperationalError: database is locked
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is particularly frustrating because there&amp;rsquo;s no correlation at all between what you do as a user and when this exception comes up. Like appendicitis, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OperationalError&lt;/code&gt; can strike at any time, which makes it all the more maddening.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;A little bit of background: beets uses the amazing &lt;a href=&quot;http://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt; database library to store its music catalog. When importing music, multiple threads collaborate to speed up the process and several of the threads have to read from and write to the database. Fortunately, SQLite&amp;rsquo;s &lt;a href=&quot;http://www.sqlite.org/lang_transaction.html&quot;&gt;transactions&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/ACID&quot;&gt;ACID guarantees&lt;/a&gt; make this straightforward: each thread gets to make atomic accesses without bothering the other threads.&lt;/p&gt;

&lt;p&gt;But things can go wrong. If a transaction stays open too long, it can block other threads from accessing the database&amp;mdash;or, in the worst case, several threads can deadlock while waiting for each other. For exactly this reason, SQLite has a lock timeout built in. If it ever sees that a thread has been waiting for a lock for more than five seconds (by default), it throws up its hands and the user sees the dreaded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;database is locked&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;So the solution should be simple: somewhere, beets is holding a transaction open for more than five seconds, so we can either find the offending transaction or crank up that timeout. But herein lies the mystery: five seconds is a &lt;em&gt;long&lt;/em&gt; time. That beets spends &lt;em&gt;5,000 milliseconds&lt;/em&gt; manipulating the database in a single transaction is indicative of something dark and terrible. No amount of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT&lt;/code&gt;s and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INSERT&lt;/code&gt;s at beets&amp;rsquo; scale should add up to five seconds, so turning up the timeout parameter is really just painting over the rot.&lt;/p&gt;

&lt;p&gt;So I looked at every line in the source where a transaction could start. I made extra-double-sure that filesystem operations happened only outside of transactions. I fastidiously closed every &lt;a href=&quot;http://docs.python.org/library/sqlite3.html#cursor-objects&quot;&gt;cursor&lt;/a&gt; after each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT&lt;/code&gt;. But all this was to no avail&amp;mdash;the bug reports continued to pour in.&lt;/p&gt;

&lt;p&gt;At this point, I was almost certain that nothing was wrong with beets&amp;rsquo; transactions in themselves. I measured the length of each access and, on my machine, they each took a handful of milliseconds apiece&amp;mdash;nowhere near a full five seconds.&lt;/p&gt;

&lt;h2 id=&quot;the-real-problem&quot;&gt;The Real Problem&lt;/h2&gt;

&lt;p&gt;I finally gave up trying to reproduce the problem on my own machine. Eventually, one incredibly helpful user offered to give me guest SSH access so I could see the bug manifest &lt;em&gt;in vitro&lt;/em&gt; on his machine.&lt;/p&gt;

&lt;p&gt;I again set about measuring the length of each transaction. And again, most transactions were in the one- or two-millisecond range. But, this time, an occasional transaction would sometimes take &lt;em&gt;much&lt;/em&gt; longer: 1.08 seconds, say. And, eventually, some errant transaction would take 5.04 seconds and beets would crash: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;database is locked&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But there was a pattern. Every long-lasting transaction took slightly more than an integral number of seconds. I saw transactions that took 1.02 and 1.04 seconds, but never 1.61 seconds or 0.98 seconds. Something was adding whole seconds to transactions&amp;rsquo; latencies.&lt;/p&gt;

&lt;p&gt;Digging through the &lt;a href=&quot;http://www.sqlite.org/download.html&quot;&gt;SQLite source code&lt;/a&gt;, I looked for places where it could sleep in whole-second increments. I found &lt;a href=&quot;http://read.cs.ucla.edu/~vandebo/sqlite/source/src/main.c#L305&quot;&gt;sqliteDefaultBusyCallback&lt;/a&gt;, the function that gets called when SQLite tries to acquire a lock but finds that it&amp;rsquo;s held by a different thread. In ordinary circumstances, that function uses a simple backoff algorithm to wait a few milliseconds before trying again. But that reasonable behavior is wrapped in a preprocessor conditional like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#if HAVE_USLEEP&lt;/code&gt; and, if SQLite doesn&amp;rsquo;t think the system can sleep in millisecond intervals, it sleeps &lt;em&gt;for a whole second each time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So this was why some users saw this horrible behavior but I never did: all my systems have SQLite compiled with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HAVE_USLEEP=1&lt;/code&gt;. Disassembling SQLite on my machine and the affected user&amp;rsquo;s confirmed the difference. Even though &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/7908799/xsh/usleep.html&quot;&gt;usleep&lt;/a&gt; is so old that it was obsoleted by &lt;a href=&quot;http://pubs.opengroup.org/onlinepubs/7908799/xsh/nanosleep.html&quot;&gt;nanosleep&lt;/a&gt; in 2001, the user&amp;rsquo;s SQLite had somehow been compiled assuming it did not exist.&lt;/p&gt;

&lt;p&gt;The mystery was solved. And while one solution would be to &lt;a href=&quot;http://mail-index.netbsd.org/current-users/2012/06/01/msg020320.html&quot;&gt;berate the world&amp;rsquo;s software packagers&lt;/a&gt; into compiling SQLite with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HAVE_USLEEP=1&lt;/code&gt;, we needed a nearer-term solution.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;/h2&gt;

&lt;p&gt;A simple solution would be to crank the SQLite lock timeout up to eleven. But I wanted something a little bit more durable and a little less ad-hoc. So beets&amp;rsquo; eventual solution to the SQLite Lock Timeout Bug from Hell kills several birds with one &lt;a href=&quot;http://www.python.org/dev/peps/pep-0020/&quot;&gt;Pythonic&lt;/a&gt; stone:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ensure that SQLite locks can &lt;em&gt;never&lt;/em&gt; time out because they never contend.&lt;/li&gt;
  &lt;li&gt;Through a simple coding convention, make it easy to avoid accidentally leaving a transaction open longer than it needs to be.&lt;/li&gt;
  &lt;li&gt;Use &lt;em&gt;portable&lt;/em&gt; synchronization that will work if beets eventually &lt;a href=&quot;https://github.com/beetbox/beets/wiki/Refactoring&quot;&gt;moves to a dumber storage backend&lt;/a&gt; that doesn&amp;rsquo;t have its own concurrency support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To accomplish all of this, beets uses &lt;em&gt;explicit transactions&lt;/em&gt; that make it obvious where database accesses begin and end. And those transactions are made &lt;em&gt;mutually exclusive&lt;/em&gt; using Python-level locks to ensure that only one thread accesses the database at a time.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s what it looks like. When a thread needs to access the database, it uses a &lt;a href=&quot;http://www.python.org/dev/peps/pep-0343/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;with&lt;/code&gt; block&lt;/a&gt; and a &amp;ldquo;Transaction&amp;rdquo; &lt;a href=&quot;http://docs.python.org/library/stdtypes.html#typecontextmanager&quot;&gt;context manager&lt;/a&gt; to query and manipulate the data. Here&amp;rsquo;s &lt;a href=&quot;https://github.com/beetbox/beets/blob/master/beets/library.py#L1182&quot;&gt;an example&lt;/a&gt; in which a Library object looks up an Item by its ID:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;with self.transaction() as tx:
    rows = tx.query(&apos;SELECT * FROM items WHERE id=?&apos;, (load_id,))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only way to access the database is via methods on the &lt;a href=&quot;https://github.com/beetbox/beets/blob/master/beets/library.py#L919&quot;&gt;Transaction object&lt;/a&gt;. And creating a Transaction means acquiring a lock. Together, these two restrictions make it impossible for two different threads to access the database at the same time. This reduces the concurrency available in the DB (appropriate for beets but not for, say, a popular Web service) but eradicates the possibility of SQLite timeouts and will make it easy for beets to move to a different backend in the future&amp;mdash;even one that doesn&amp;rsquo;t support concurrency itself.&lt;/p&gt;

&lt;p&gt;To make this explicit-transaction approach feasible, transactions need to be &lt;em&gt;composable:&lt;/em&gt; it has to be possible to take two correctly-coded transactional functions and call them both together in a single transaction. For example, the beets Library has &lt;a href=&quot;https://github.com/beetbox/beets/blob/master/beets/library.py#L1220&quot;&gt;a method that deletes a single track&lt;/a&gt;. The &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/cli.html#remove&quot;&gt;&amp;ldquo;beet remove&amp;rdquo; command&lt;/a&gt; needs to remove &lt;em&gt;many&lt;/em&gt; tracks in one fell, atomic swoop.&lt;/p&gt;

&lt;p&gt;The smaller method&amp;mdash;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Library.remove&lt;/code&gt;&amp;mdash;uses a transaction internally so it can synchronize correctly when it&amp;rsquo;s called alone. But the higher-level command has to call it many times in a single transaction, &lt;a href=&quot;https://github.com/beetbox/beets/blob/master/beets/ui/commands.py#L984&quot;&gt;like so&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;with lib.transaction():
    for item in items:
        lib.remove(item)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To make all of this work, I want to make the &lt;em&gt;outermost&lt;/em&gt; transaction the only one that synchronizes. If a thread enters a transaction and then, before leaving the outer one, enters another nested transaction, the inner one should have no effect. In this case, the transaction that surrounds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; loop needs to synchronize with other threads, but the inner transactions (inside each call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib.remove&lt;/code&gt;) should be &lt;a href=&quot;http://en.wikipedia.org/wiki/NOP&quot;&gt;no-ops&lt;/a&gt; because the thread is already holding a lock.&lt;/p&gt;

&lt;p&gt;To accomplish this, each thread transparently maintains a &lt;em&gt;transaction stack&lt;/em&gt; that keeps track of all the Transaction objects that are currently active. When a transaction starts, it gets pushed onto the stack; when it finishes, it pops off. When the stack goes from having zero transactions to one, the thread acquires a lock; when the last transaction is popped from the stack, the lock is released. This simple policy allows beets to safely compose transactional code into larger functions.&lt;/p&gt;

&lt;h2 id=&quot;takeaway-for-other-projects&quot;&gt;Takeaway for Other Projects&lt;/h2&gt;

&lt;p&gt;What can we learn from the vanquishing of this monstrous bug&amp;mdash;other than the &lt;a href=&quot;http://www.cs.columbia.edu/~junfeng/09fa-e6998/papers/concurrency-bugs.pdf&quot;&gt;well-known fact&lt;/a&gt; that &lt;a href=&quot;http://en.wiktionary.org/wiki/heisenbug&quot;&gt;concurrency bugs are horrifying&lt;/a&gt;? I think there are two lessons here: one for everybody who uses SQLite and one developers of any small-scale, desktop application that uses a database.&lt;/p&gt;

&lt;h3 id=&quot;assume-sqlite-sleeps-whole-seconds&quot;&gt;Assume SQLite Sleeps Whole Seconds&lt;/h3&gt;

&lt;p&gt;If you use SQLite, you currently need to assume that some users will have a copy compiled without usleep support. If you&amp;rsquo;re using multiple threads, this means that, even under light contention, some transactions &lt;em&gt;will&lt;/em&gt; take longer than five seconds. Either turn the timeout parameter up or otherwise account for this inevitability.&lt;/p&gt;

&lt;p&gt;I haven&amp;rsquo;t seen this particular quirk documented elsewhere, but it should be common knowledge among SQLite users.&lt;/p&gt;

&lt;h3 id=&quot;try-explicit-transactions&quot;&gt;Try Explicit Transactions&lt;/h3&gt;

&lt;p&gt;If you&amp;rsquo;re writing a small-scale application that doesn&amp;rsquo;t need highly concurrent access to a database, consider using explicit transactions based on a language-level construct (Python&amp;rsquo;s &lt;a href=&quot;http://docs.python.org/library/stdtypes.html#typecontextmanager&quot;&gt;context managers&lt;/a&gt; are a perfect example).&lt;/p&gt;

&lt;p&gt;Without explicit transactions, it&amp;rsquo;s hard&amp;mdash;impossible, in some cases&amp;mdash;to see where transactions begin and end. So it&amp;rsquo;s easy to introduce bugs where transactions remain open much longer than they need to be. There are several advantages to marking the start and end of every transaction:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It&amp;rsquo;s easy to verify that a transaction ends in a timely manner.&lt;/li&gt;
  &lt;li&gt;You can add synchronization to unsynchronized datastores like &lt;a href=&quot;http://code.google.com/p/leveldb/&quot;&gt;LevelDB&lt;/a&gt; or flat files.&lt;/li&gt;
  &lt;li&gt;You can interpose on transactions for debugging purposes. For example, you might want to measure the time taken by each transaction. (This technique was instrumental to diagnosing this bug in beets.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if you&amp;rsquo;re coding for SQLite in Python, feel free to &lt;a href=&quot;https://github.com/beetbox/beets/blob/master/beets/library.py#L919&quot;&gt;steal beets&amp;rsquo; Transaction implementation&lt;/a&gt;&amp;mdash;it&amp;rsquo;s open source!&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>the road to 1.0</title>
        <link href="http://beets.io/blog/one-point-oh.html"/>
        <updated>2012-08-17T00:00:00+00:00</updated>
        <id>http://beets.io/blog/one-point-oh</id>
        <content type="html">&lt;p&gt;Beets has been in beta forever&amp;mdash;for small values of &lt;em&gt;forever&lt;/em&gt;, at least. I made &lt;a href=&quot;https://github.com/beetbox/beets/commit/c1ed60af98bd5f18ab0a32bf782260ac15954d8e&quot;&gt;the first commit&lt;/a&gt; to the original Subversion repository (for shame!) on May 14, 2008 and uploaded &lt;a href=&quot;http://beets.readthedocs.org/en/latest/changelog.html#b1-june-17-2010&quot;&gt;version 1.0b1&lt;/a&gt; on June 17, 2010. I&amp;rsquo;m now working on beets&amp;rsquo; sixteenth beta. It&amp;rsquo;s high time that the project hit the big 1.0. I&amp;rsquo;m excited to give beets the point-zero stamp of approval, but I have two big, backwards-incompatible changes I want to make before sprouting a stable maintenance branch. In this post, I&amp;rsquo;ll describe my plans for beets 1.0b16 and b17&amp;mdash;and beyond.&lt;/p&gt;

&lt;h2 id=&quot;beta-16&quot;&gt;Beta 16&lt;/h2&gt;

&lt;p&gt;As nerd-oriented software, beets has a &lt;em&gt;lot&lt;/em&gt; of &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/config.html&quot;&gt;configuration options&lt;/a&gt;. The config file, affectionately known as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.beetsconfig&lt;/code&gt;, has used an &lt;a href=&quot;http://en.wikipedia.org/wiki/INI_file&quot;&gt;INI&lt;/a&gt;-like syntax as defined by Python&amp;rsquo;s &lt;a href=&quot;http://docs.python.org/library/configparser.html&quot;&gt;ConfigParser&lt;/a&gt; since the beginning. More than two years later, beets has outgrown the dated, inflexible ConfigParser system. So the next beta of beets will focus on ripping out ConfigParser altogether and replacing it with an entirely new configuration system that solves many problems at once. Here&amp;rsquo;s what you can expect in 1.0b16:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://yaml.org/&quot;&gt;YAML&lt;/a&gt; syntax. The config file has grown a troubling number of half-working syntax hacks over the years. Lists of things have to be separated by whitespace, which means you can&amp;rsquo;t use spaces in &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/config.html#replace&quot;&gt;regular expressions&lt;/a&gt;. Egregiously, I had to force users to substitute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; characters for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt;s in query-based &lt;a href=&quot;http://beets.readthedocs.org/en/latest/reference/config.html#path-format-configuration&quot;&gt;path format&lt;/a&gt; keys because ConfigParser splits on colons. Because it has a well-defined and nuanced &lt;a href=&quot;http://www.yaml.org/spec/1.2/spec.html&quot;&gt;syntax specification&lt;/a&gt;, YAML-based config files will eschew these hacks: lists will look like lists and all characters will be treated as equals.&lt;/li&gt;
  &lt;li&gt;You&amp;rsquo;ll have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.config/beets&lt;/code&gt; directory (or the equivalent on Windows). No more cluttering your home directory with the configuration file, the state file, the library database, and maybe even the import log.&lt;/li&gt;
  &lt;li&gt;ConfigParser does not play nice with Unicode. It&amp;rsquo;s 2012, folks.&lt;/li&gt;
  &lt;li&gt;You&amp;rsquo;ll be able to combine multiple configuration files&amp;mdash;for example, using a global base configuration with per-location overrides.&lt;/li&gt;
  &lt;li&gt;Most crucially, programming with the ConfigParser API is getting to be a nightmare. Look no further than the monstrous &lt;a href=&quot;https://github.com/beetbox/beets/blob/30ac59f3d20dd3e7ef72456e8fca3e47713d38dc/beets/ui/commands.py#L627&quot;&gt;import_files&lt;/a&gt; function signature to witness the pain of threading every config option through the UI to the business end of the code. Every time I add a new config option or command-line switch to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet import&lt;/code&gt;, I have to touch at least five files to keep the frontend and backend in sync and update the unit tests. And that&amp;rsquo;s just to parse the option: the real work for the feature begins &lt;em&gt;after&lt;/em&gt; all this. The ConfigParser disaster discourages me from adding new features to beets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These problems are so basic that I don&amp;rsquo;t think I&amp;rsquo;m alone in growing uneasy with ConfigParser. So I&amp;rsquo;m writing a new configuration library called &lt;a href=&quot;https://github.com/sampsyo/confit&quot;&gt;Confit&lt;/a&gt; (that&amp;rsquo;s pronounced &lt;a href=&quot;http://en.wikipedia.org/wiki/Confit&quot;&gt;&lt;em&gt;con-FEE&lt;/em&gt;&lt;/a&gt;). I hope to make Confit into the best available library for configuring Python applications. I&amp;rsquo;ll have more to say about Confit on this blog as work progresses.&lt;/p&gt;

&lt;h2 id=&quot;beta-17&quot;&gt;Beta 17&lt;/h2&gt;

&lt;p&gt;Unlike b16&amp;rsquo;s configuration overhaul, beta 17&amp;rsquo;s nightmare is a less user-visible one: the &lt;a href=&quot;http://beets.readthedocs.org/en/latest/plugins/writing.html&quot;&gt;plugin API&lt;/a&gt;. If you browse through the code for &lt;a href=&quot;http://beets.readthedocs.org/en/latest/plugins/index.html#plugins-included-with-beets&quot;&gt;beets&amp;rsquo; standard plugins&lt;/a&gt;, you&amp;rsquo;ll quickly see that they all employ a tragic conflation between module-scoped and object-scoped variables. There are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global&lt;/code&gt;s all over the place and a nonsensical distinction between &lt;a href=&quot;http://www.python.org/dev/peps/pep-0318/&quot;&gt;decorated&lt;/a&gt; functions and specially-named methods.&lt;/p&gt;

&lt;p&gt;The plugin API overhaul will consolidate everything into the object scope. Plugins will follow the &lt;a href=&quot;http://en.wikipedia.org/wiki/Singleton_pattern&quot;&gt;singleton pattern&lt;/a&gt;, so it will no longer be necessary to keep anything module-global or assigned to the class itself. Decorators will be deemphasized; events will use a method naming convention instead.&lt;/p&gt;

&lt;p&gt;And, finally, we&amp;rsquo;ll drop &lt;a href=&quot;http://docs.python.org/library/pkgutil.html#pkgutil.extend_path&quot;&gt;namespace packages&lt;/a&gt;. I didn&amp;rsquo;t know this when I first started using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beetsplug&lt;/code&gt; namespace package for beets plugins, but &lt;a href=&quot;https://bitbucket.org/tarek/distribute/issue/179/namespace-packages-installed-with-single&quot;&gt;a bug&lt;/a&gt; in &lt;a href=&quot;http://pypi.python.org/pypi/distribute/&quot;&gt;distribute&lt;/a&gt; utterly breaks them when they&amp;rsquo;re installed with a program like &lt;a href=&quot;http://www.pip-installer.org/&quot;&gt;pip&lt;/a&gt;. This meant that some users could never use third-party plugins without reinstalling beets. (&lt;a href=&quot;http://www.python.org/dev/peps/pep-0420/&quot;&gt;PEP 420&lt;/a&gt; fixes everything but, alas, won&amp;rsquo;t be backported to Python 2.x.)&lt;/p&gt;

&lt;p&gt;While these changes aren&amp;rsquo;t particularly exciting for end users, it&amp;rsquo;s important that I break all the plugins before 1.0 rather than after. The goal is to make beets&amp;rsquo; plugin system future-proof&amp;mdash;to contain the &lt;a href=&quot;http://en.wikipedia.org/wiki/Spaghetti_code&quot;&gt;spaghetti&lt;/a&gt; before it spreads.&lt;/p&gt;

&lt;h2 id=&quot;beyond&quot;&gt;Beyond&lt;/h2&gt;

&lt;p&gt;With these two major changes out of the way, it will be time for some release candidates and then a massive party as we release 1.0. At this point, I plan on dividing beets development into a stable/&lt;a href=&quot;http://en.wikipedia.org/wiki/Trunk_(software)&quot;&gt;trunk&lt;/a&gt; development model: version 1.0 will see bug-fix-only releases while the new features go into a separate 1.1 branch. This will let me&amp;mdash;and maybe other developers?&amp;mdash;experiment with new stuff without rocking the boat for users who don&amp;rsquo;t want to be bothered.&lt;/p&gt;

&lt;p&gt;I have some exciting plans for new directions post-1.0. But, for now, I have two big betas to work on&amp;mdash;we can talk about 1.1 and beyond a little later. Keep that dial right here.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>Tomahawk resolver</title>
        <link href="http://beets.io/blog/tomahawk-resolver.html"/>
        <updated>2012-08-03T00:00:00+00:00</updated>
        <id>http://beets.io/blog/tomahawk-resolver</id>
        <content type="html">&lt;p&gt;Beets is a music library manager&amp;mdash;not, for the most part, a music player. It does include a &lt;a href=&quot;http://beets.readthedocs.org/page/plugins/bpd.html&quot;&gt;simple player plugin&lt;/a&gt; and an &lt;a href=&quot;http://beets.readthedocs.org/page/plugins/web.html&quot;&gt;experimental Web-based player&lt;/a&gt;, but it generally leaves actual sound-reproduction to specialized tools.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.tomahawk-player.org/&quot;&gt;Tomahawk&lt;/a&gt; is one particularly exciting new open-source music player. The magic of Tomahawk lies in its ability to consolidate many sources of music into a single player interface. (It&amp;rsquo;s also a very nicely-designed, cross-platform player even if you don&amp;rsquo;t count the magic.) To integrate new sources of music with Tomahawk, you just have to provide a &lt;a href=&quot;http://www.tomahawk-player.org/resolvers.html&quot;&gt;resolver&lt;/a&gt;: a piece of code that searches for music and gives it to Tomahawk to display and play back.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s now a beets Tomahawk resolver that can hook your meticulously organized beets music library into the Tomahawk interface. And it even works remotely, so you can stream music from a server running beets to a different machine running Tomahawk.&lt;/p&gt;

&lt;p&gt;To use the resolver, first run the &lt;a href=&quot;http://beets.readthedocs.org/page/plugins/web.html&quot;&gt;beets Web plugin&lt;/a&gt; on the machine with your music. Just add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; to your &amp;ldquo;plugins&amp;rdquo; line in your &lt;a href=&quot;http://beets.readthedocs.org/page/reference/config.html&quot;&gt;config file&lt;/a&gt; and then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beet web&lt;/code&gt; to start the server.&lt;/p&gt;

&lt;p&gt;Then, open the Tomahawk settings and check the beets service. Click the wrench icon next to the beets resolver to configure it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/tomahawk-resolver-config.png&quot; alt=&quot;Configuring the beets Tomahawk resolver.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You&amp;rsquo;ll need to enter the hostname and port of the beets Web server. (You don&amp;rsquo;t need to change anything if you&amp;rsquo;re running the server on the local host on the default port, 8337.)&lt;/p&gt;

&lt;p&gt;Tomahawk will now be able to find tracks from your beets library. Type a query into the &amp;ldquo;global search&amp;rdquo; box and rock out.&lt;/p&gt;

</content>
    </entry>
    
    <entry>
        <title>beets has a new blog</title>
        <link href="http://beets.io/blog/new-blog.html"/>
        <updated>2012-08-01T00:00:00+00:00</updated>
        <id>http://beets.io/blog/new-blog</id>
        <content type="html">&lt;p&gt;Welcome to the brand new &lt;a href=&quot;http://beets.io/blog/&quot;&gt;beets blog&lt;/a&gt;! The beets project has had &lt;a href=&quot;http://twitter.com/b33ts&quot;&gt;a Twitter account&lt;/a&gt; for some time now, but I increasingly have more to say than will fit in 140 characters. This blog will be a venue for more detailed writing about the project, Python, open source development, and music software.&lt;/p&gt;

&lt;p&gt;These are some of the posts you can expect as the blog gets rolling:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In-depth descriptions of new features and design changes in beets.&lt;/li&gt;
  &lt;li&gt;Guides to doing cool, non-obvious stuff with beets.&lt;/li&gt;
  &lt;li&gt;My plans and proposals for how beets will evolve.&lt;/li&gt;
  &lt;li&gt;Useful Python snippets and libraries that grow out of the beets project (like &lt;a href=&quot;https://github.com/sampsyo/bluelet&quot;&gt;Bluelet&lt;/a&gt;, &lt;a href=&quot;https://github.com/sampsyo/confit&quot;&gt;Confit&lt;/a&gt;, &lt;a href=&quot;https://github.com/beetbox/pyacoustid&quot;&gt;pyacoustid&lt;/a&gt;, and &lt;a href=&quot;https://github.com/beetbox/audioread&quot;&gt;audioread&lt;/a&gt;).&lt;/li&gt;
  &lt;li&gt;Opinions on Python development and maintaining open source projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can subscribe to this blog using its &lt;a href=&quot;/blog/atom.xml&quot;&gt;Atom feed&lt;/a&gt;. The &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;
source for the site is
&lt;a href=&quot;https://github.com/beetbox/beets/tree/gh-pages&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

</content>
    </entry>
    

</feed>
