diff --git a/pkgs/tools/audio/beets/default.nix b/pkgs/tools/audio/beets/default.nix
index 893bef7ea102..d0a3a922f2bf 100644
--- a/pkgs/tools/audio/beets/default.nix
+++ b/pkgs/tools/audio/beets/default.nix
@@ -1,6 +1,63 @@
-{ stdenv, fetchFromGitHub, buildPythonPackage, pythonPackages, python }:
+{ stdenv, fetchFromGitHub, writeScript
+, buildPythonPackage, pythonPackages, python
 
-buildPythonPackage rec {
+, enableAcoustid   ? true
+, enableBeatport   ? true
+, enableDiscogs    ? true
+, enableEchonest   ? true
+, enableFetchart   ? true
+, enableLastfm     ? true
+, enableMpd        ? true
+, enableReplaygain ? true
+, enableWeb        ? true
+
+, bashInteractive, bashCompletion
+}:
+
+assert enableAcoustid    -> pythonPackages.pyacoustid     != null;
+assert enableBeatport    -> pythonPackages.responses      != null;
+assert enableDiscogs     -> pythonPackages.discogs_client != null;
+assert enableEchonest    -> pythonPackages.pyechonest     != null;
+assert enableFetchart    -> pythonPackages.responses      != null;
+assert enableLastfm      -> pythonPackages.pylast         != null;
+assert enableMpd         -> pythonPackages.mpd            != null;
+assert enableReplaygain  -> pythonPackages.audiotools     != null;
+assert enableWeb         -> pythonPackages.flask          != null;
+
+with stdenv.lib;
+
+let
+  optionalPlugins = {
+    beatport = enableBeatport;
+    chroma = enableAcoustid;
+    discogs = enableDiscogs;
+    echonest = enableEchonest;
+    echonest_tempo = enableEchonest;
+    fetchart = enableFetchart;
+    lastgenre = enableLastfm;
+    lastimport = enableLastfm;
+    mpdstats = enableMpd;
+    mpdupdate = enableMpd;
+    replaygain = enableReplaygain;
+    web = enableWeb;
+  };
+
+  pluginsWithoutDeps = [
+    "bench" "bpd" "bpm" "bucket" "convert" "duplicates" "embedart" "freedesktop"
+    "fromfilename" "ftintitle" "fuzzy" "ihate" "importadded" "importfeeds"
+    "info" "inline" "keyfinder" "lyrics" "mbcollection" "mbsync" "missing"
+    "play" "random" "rewrite" "scrub" "smartplaylist" "spotify" "the" "types"
+    "zero"
+  ];
+
+  enabledOptionalPlugins = attrNames (filterAttrs (_: id) optionalPlugins);
+
+  allPlugins = pluginsWithoutDeps ++ attrNames optionalPlugins;
+
+  testShell = "${bashInteractive}/bin/bash --norc";
+  completion = "${bashCompletion}/share/bash-completion/bash_completion";
+
+in buildPythonPackage rec {
   name = "beets-${version}";
   version = "1.3.9";
   namePrefix = "";
@@ -12,27 +69,60 @@ buildPythonPackage rec {
     sha256 = "1srhkiyjqx6i3gn20ihf087l5pa77yh5b81ivc52lj491fda7xqk";
   };
 
-  # tests depend on $HOME setting
-  preConfigure = "export HOME=$TMPDIR";
-
   propagatedBuildInputs = [
-    pythonPackages.pyyaml
-    pythonPackages.unidecode
-    pythonPackages.mutagen
+    pythonPackages.enum34
     pythonPackages.munkres
     pythonPackages.musicbrainzngs
-    pythonPackages.enum34
-    pythonPackages.pylast
-    pythonPackages.rarfile
-    pythonPackages.flask
+    pythonPackages.mutagen
+    pythonPackages.pyyaml
+    pythonPackages.unidecode
     python.modules.sqlite3
     python.modules.readline
+  ] ++ optional enableAcoustid                     pythonPackages.pyacoustid
+    ++ optional (enableBeatport || enableFetchart) pythonPackages.requests2
+    ++ optional enableDiscogs                      pythonPackages.discogs_client
+    ++ optional enableEchonest                     pythonPackages.pyechonest
+    ++ optional enableLastfm                       pythonPackages.pylast
+    ++ optional enableMpd                          pythonPackages.mpd
+    ++ optional enableReplaygain                   pythonPackages.audiotools
+    ++ optional enableWeb                          pythonPackages.flask;
+
+  buildInputs = with pythonPackages; [
+    beautifulsoup4
+    flask
+    mock
+    nose
+    pyechonest
+    pylast
+    rarfile
+    requests2
+    responses
   ];
 
-  buildInputs = with pythonPackages; [ mock pyechonest six responses nose ];
+  patches = [ ./mediafile-codec-fix.patch ];
+
+  postPatch = ''
+    sed -i -e '/assertIn.*item.*path/d' test/test_info.py
+    echo echo completion tests passed > test/test_completion.sh
+
+    sed -i -e '/^BASH_COMPLETION_PATHS *=/,/^])$/ {
+      /^])$/i u"${completion}"
+    }' beets/ui/commands.py
+  '';
+
+  doCheck = true;
+
+  checkPhase = ''
+    runHook preCheck
+
+    BEETS_TEST_SHELL="${testShell}" \
+    BASH_COMPLETION_SCRIPT="${completion}" \
+    HOME="$(mktemp -d)" \
+      nosetests -v
+
+    runHook postCheck
+  '';
 
-  # 10 tests are failing
-  doCheck = false;
 
   meta = {
     homepage = http://beets.radbox.org;
diff --git a/pkgs/tools/audio/beets/mediafile-codec-fix.patch b/pkgs/tools/audio/beets/mediafile-codec-fix.patch
new file mode 100644
index 000000000000..7eaa5e19590e
--- /dev/null
+++ b/pkgs/tools/audio/beets/mediafile-codec-fix.patch
@@ -0,0 +1,25 @@
+From 903e88a228d6bd93bd1884c59dd23dd9f04a1199 Mon Sep 17 00:00:00 2001
+From: Adrian Sampson <adrian@radbox.org>
+Date: Wed, 26 Nov 2014 19:04:40 -0800
+Subject: [PATCH] Fix codec reference in MediaFile (fix #1117)
+
+---
+ beets/mediafile.py | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/beets/mediafile.py b/beets/mediafile.py
+index ce42621..a459e09 100644
+--- a/beets/mediafile.py
++++ b/beets/mediafile.py
+@@ -1340,8 +1340,9 @@ def __init__(self, path, id3v23=False):
+             raise FileTypeError(path)
+         elif (type(self.mgfile).__name__ == 'M4A' or
+               type(self.mgfile).__name__ == 'MP4'):
+-            if hasattr(self.mgfile.info, 'codec'):
+-                if self.mgfile.codec and self.mgfile.codec.startswith('alac'):
++            info = self.mgfile.info
++            if hasattr(info, 'codec'):
++                if info.codec and info.codec.startswith('alac'):
+                     self.type = 'alac'
+                 else:
+                     self.type = 'aac'