Merge branch 'better-beets'.

Makes beets actually usable (and configurable) on Nix(OS), if you want
to use more plugins rather than just plain lookup of tracks based on
(fuzzy) string matching.

This also changes the derivation name from "python2.7-beets" to just
"beets".

* Commit summary:
beets: Check dependencies on activated plugins.
beets: Check plugin definitions against package.
beets: Use audiotools backend for replaygain.
beets: Allow to configure plugin dependencies.
beets: Switch to using fetchFromGitHub.
python: Add new package audiotools.
python: Add new package discogs_client.
python: Add pyacoustid and dependencies.
python/mutagen: Update to upstream version 1.27.
mp3gain: Fix output path bin directory.
beets: Add myself to maintainers.
beets: Update to new upstream version 1.3.9.
beets: Move into its own package directory.

aszlig 880c985f c087c456

+297 -44
+1 -2
pkgs/applications/audio/mp3gain/default.nix
··· 14 buildFlags = [ "OSTYPE=linux" ]; 15 16 installPhase = '' 17 - mkdir -p $out/usr/bin 18 - cp mp3gain $out/usr/bin 19 ''; 20 21 meta = {
··· 14 buildFlags = [ "OSTYPE=linux" ]; 15 16 installPhase = '' 17 + install -vD mp3gain "$out/bin/mp3gain" 18 ''; 19 20 meta = {
+177
pkgs/tools/audio/beets/default.nix
···
··· 1 + { stdenv, fetchFromGitHub, writeScript 2 + , buildPythonPackage, pythonPackages, python 3 + 4 + , enableAcoustid ? true 5 + , enableBeatport ? true 6 + , enableDiscogs ? true 7 + , enableEchonest ? true 8 + , enableFetchart ? true 9 + , enableLastfm ? true 10 + , enableMpd ? true 11 + , enableReplaygain ? true 12 + , enableWeb ? true 13 + 14 + , bashInteractive, bashCompletion 15 + }: 16 + 17 + assert enableAcoustid -> pythonPackages.pyacoustid != null; 18 + assert enableBeatport -> pythonPackages.responses != null; 19 + assert enableDiscogs -> pythonPackages.discogs_client != null; 20 + assert enableEchonest -> pythonPackages.pyechonest != null; 21 + assert enableFetchart -> pythonPackages.responses != null; 22 + assert enableLastfm -> pythonPackages.pylast != null; 23 + assert enableMpd -> pythonPackages.mpd != null; 24 + assert enableReplaygain -> pythonPackages.audiotools != null; 25 + assert enableWeb -> pythonPackages.flask != null; 26 + 27 + with stdenv.lib; 28 + 29 + let 30 + optionalPlugins = { 31 + beatport = enableBeatport; 32 + chroma = enableAcoustid; 33 + discogs = enableDiscogs; 34 + echonest = enableEchonest; 35 + echonest_tempo = enableEchonest; 36 + fetchart = enableFetchart; 37 + lastgenre = enableLastfm; 38 + lastimport = enableLastfm; 39 + mpdstats = enableMpd; 40 + mpdupdate = enableMpd; 41 + replaygain = enableReplaygain; 42 + web = enableWeb; 43 + }; 44 + 45 + pluginsWithoutDeps = [ 46 + "bench" "bpd" "bpm" "bucket" "convert" "duplicates" "embedart" "freedesktop" 47 + "fromfilename" "ftintitle" "fuzzy" "ihate" "importadded" "importfeeds" 48 + "info" "inline" "keyfinder" "lyrics" "mbcollection" "mbsync" "missing" 49 + "play" "random" "rewrite" "scrub" "smartplaylist" "spotify" "the" "types" 50 + "zero" 51 + ]; 52 + 53 + enabledOptionalPlugins = attrNames (filterAttrs (_: id) optionalPlugins); 54 + 55 + allPlugins = pluginsWithoutDeps ++ attrNames optionalPlugins; 56 + allEnabledPlugins = pluginsWithoutDeps ++ enabledOptionalPlugins; 57 + 58 + # Discogs plugin wants to have an API token, so skip install checks. 59 + allTestablePlugins = remove "discogs" allEnabledPlugins; 60 + 61 + testShell = "${bashInteractive}/bin/bash --norc"; 62 + completion = "${bashCompletion}/share/bash-completion/bash_completion"; 63 + 64 + in buildPythonPackage rec { 65 + name = "beets-${version}"; 66 + version = "1.3.9"; 67 + namePrefix = ""; 68 + 69 + src = fetchFromGitHub { 70 + owner = "sampsyo"; 71 + repo = "beets"; 72 + rev = "v${version}"; 73 + sha256 = "1srhkiyjqx6i3gn20ihf087l5pa77yh5b81ivc52lj491fda7xqk"; 74 + }; 75 + 76 + propagatedBuildInputs = [ 77 + pythonPackages.enum34 78 + pythonPackages.munkres 79 + pythonPackages.musicbrainzngs 80 + pythonPackages.mutagen 81 + pythonPackages.pyyaml 82 + pythonPackages.unidecode 83 + python.modules.sqlite3 84 + python.modules.readline 85 + ] ++ optional enableAcoustid pythonPackages.pyacoustid 86 + ++ optional (enableBeatport || enableFetchart) pythonPackages.requests2 87 + ++ optional enableDiscogs pythonPackages.discogs_client 88 + ++ optional enableEchonest pythonPackages.pyechonest 89 + ++ optional enableLastfm pythonPackages.pylast 90 + ++ optional enableMpd pythonPackages.mpd 91 + ++ optional enableReplaygain pythonPackages.audiotools 92 + ++ optional enableWeb pythonPackages.flask; 93 + 94 + buildInputs = with pythonPackages; [ 95 + beautifulsoup4 96 + flask 97 + mock 98 + nose 99 + pyechonest 100 + pylast 101 + rarfile 102 + requests2 103 + responses 104 + ]; 105 + 106 + patches = [ 107 + ./mediafile-codec-fix.patch 108 + ./replaygain-default-audiotools.patch 109 + ]; 110 + 111 + postPatch = '' 112 + sed -i -e '/assertIn.*item.*path/d' test/test_info.py 113 + echo echo completion tests passed > test/test_completion.sh 114 + 115 + sed -i -e '/^BASH_COMPLETION_PATHS *=/,/^])$/ { 116 + /^])$/i u"${completion}" 117 + }' beets/ui/commands.py 118 + ''; 119 + 120 + doCheck = true; 121 + 122 + preCheck = '' 123 + (${concatMapStrings (s: "echo \"${s}\";") allPlugins}) \ 124 + | sort -u > plugins_defined 125 + find beetsplug -mindepth 1 \ 126 + \! -path 'beetsplug/__init__.py' -a \ 127 + \( -name '*.py' -o -path 'beetsplug/*/__init__.py' \) -print \ 128 + | sed -n -re 's|^beetsplug/([^/.]+).*|\1|p' \ 129 + | sort -u > plugins_available 130 + 131 + if ! mismatches="$(diff -y plugins_defined plugins_available)"; then 132 + echo "The the list of defined plugins (left side) doesn't match" \ 133 + "the list of available plugins (right side):" >&2 134 + echo "$mismatches" >&2 135 + exit 1 136 + fi 137 + ''; 138 + 139 + checkPhase = '' 140 + runHook preCheck 141 + 142 + BEETS_TEST_SHELL="${testShell}" \ 143 + BASH_COMPLETION_SCRIPT="${completion}" \ 144 + HOME="$(mktemp -d)" \ 145 + nosetests -v 146 + 147 + runHook postCheck 148 + ''; 149 + 150 + doInstallCheck = true; 151 + 152 + installCheckPhase = '' 153 + runHook preInstallCheck 154 + 155 + tmphome="$(mktemp -d)" 156 + 157 + EDITOR="${writeScript "beetconfig.sh" '' 158 + #!${stdenv.shell} 159 + cat > "$1" <<CFG 160 + plugins: ${concatStringsSep " " allTestablePlugins} 161 + musicbrainz: 162 + user: dummy 163 + pass: dummy 164 + CFG 165 + ''}" HOME="$tmphome" "$out/bin/beet" config -e 166 + EDITOR=true HOME="$tmphome" "$out/bin/beet" config -e 167 + 168 + runHook postInstallCheck 169 + ''; 170 + 171 + meta = { 172 + homepage = http://beets.radbox.org; 173 + description = "Music tagger and library organizer"; 174 + license = stdenv.lib.licenses.mit; 175 + maintainers = with stdenv.lib.maintainers; [ iElectric aszlig ]; 176 + }; 177 + }
+25
pkgs/tools/audio/beets/mediafile-codec-fix.patch
···
··· 1 + From 903e88a228d6bd93bd1884c59dd23dd9f04a1199 Mon Sep 17 00:00:00 2001 2 + From: Adrian Sampson <adrian@radbox.org> 3 + Date: Wed, 26 Nov 2014 19:04:40 -0800 4 + Subject: [PATCH] Fix codec reference in MediaFile (fix #1117) 5 + 6 + --- 7 + beets/mediafile.py | 5 +++-- 8 + 1 file changed, 3 insertions(+), 2 deletions(-) 9 + 10 + diff --git a/beets/mediafile.py b/beets/mediafile.py 11 + index ce42621..a459e09 100644 12 + --- a/beets/mediafile.py 13 + +++ b/beets/mediafile.py 14 + @@ -1340,8 +1340,9 @@ def __init__(self, path, id3v23=False): 15 + raise FileTypeError(path) 16 + elif (type(self.mgfile).__name__ == 'M4A' or 17 + type(self.mgfile).__name__ == 'MP4'): 18 + - if hasattr(self.mgfile.info, 'codec'): 19 + - if self.mgfile.codec and self.mgfile.codec.startswith('alac'): 20 + + info = self.mgfile.info 21 + + if hasattr(info, 'codec'): 22 + + if info.codec and info.codec.startswith('alac'): 23 + self.type = 'alac' 24 + else: 25 + self.type = 'aac'
+17
pkgs/tools/audio/beets/replaygain-default-audiotools.patch
···
··· 1 + diff --git a/beetsplug/replaygain.py b/beetsplug/replaygain.py 2 + index 40b3a3a..9b54a5a 100644 3 + --- a/beetsplug/replaygain.py 4 + +++ b/beetsplug/replaygain.py 5 + @@ -627,11 +627,10 @@ class ReplayGainPlugin(BeetsPlugin): 6 + super(ReplayGainPlugin, self).__init__() 7 + self.import_stages = [self.imported] 8 + 9 + - # default backend is 'command' for backward-compatibility. 10 + self.config.add({ 11 + 'overwrite': False, 12 + 'auto': True, 13 + - 'backend': u'command', 14 + + 'backend': u'audiotools', 15 + 'targetlevel': 89, 16 + }) 17 +
+2
pkgs/top-level/all-packages.nix
··· 757 758 beanstalkd = callPackage ../servers/beanstalkd { }; 759 760 bgs = callPackage ../tools/X11/bgs { }; 761 762 biber = callPackage ../tools/typesetting/biber {
··· 757 758 beanstalkd = callPackage ../servers/beanstalkd { }; 759 760 + beets = callPackage ../tools/audio/beets { }; 761 + 762 bgs = callPackage ../tools/X11/bgs { }; 763 764 biber = callPackage ../tools/typesetting/biber {
+75 -42
pkgs/top-level/python-packages.nix
··· 531 }; 532 }); 533 534 autopep8 = buildPythonPackage (rec { 535 name = "autopep8-1.0.4"; 536 ··· 786 platforms = stdenv.lib.platforms.linux; 787 788 maintainers = [ stdenv.lib.maintainers.bluescreen303 ]; 789 - }; 790 - }; 791 - 792 - beets = buildPythonPackage rec { 793 - name = "beets-1.3.6"; 794 - 795 - src = pkgs.fetchurl { 796 - url = "http://pypi.python.org/packages/source/b/beets/${name}.tar.gz"; 797 - md5 = "59615a54b3ac3983159e77ff9dda373e"; 798 - }; 799 - 800 - # tests depend on $HOME setting 801 - preConfigure = "export HOME=$TMPDIR"; 802 - 803 - propagatedBuildInputs = 804 - [ self.pyyaml 805 - self.unidecode 806 - self.mutagen 807 - self.munkres 808 - self.musicbrainzngs 809 - self.enum34 810 - self.pylast 811 - self.rarfile 812 - self.flask 813 - modules.sqlite3 814 - modules.readline 815 - ]; 816 - 817 - buildInputs = with self; [ mock pyechonest six responses nose ]; 818 - 819 - # 10 tests are failing 820 - doCheck = false; 821 - 822 - meta = { 823 - homepage = http://beets.radbox.org; 824 - description = "Music tagger and library organizer"; 825 - license = licenses.mit; 826 - maintainers = [ stdenv.lib.maintainers.iElectric ]; 827 }; 828 }; 829 ··· 2188 description = "derpconf abstracts loading configuration files for your app"; 2189 homepage = https://github.com/globocom/derpconf; 2190 license = licenses.mit; 2191 }; 2192 }; 2193 ··· 5760 }; 5761 5762 mutagen = buildPythonPackage (rec { 5763 - name = "mutagen-1.23"; 5764 5765 src = pkgs.fetchurl { 5766 url = "http://pypi.python.org/packages/source/m/mutagen/${name}.tar.gz"; 5767 - sha256 = "12f70aaf5ggdzll76bhhkn64b27xy9s1acx417dbsaqnnbis8s76"; 5768 }; 5769 5770 - # one unicode test fails 5771 - doCheck = false; 5772 5773 meta = { 5774 description = "Python multimedia tagging library"; ··· 6980 src = pkgs.fetchurl { 6981 url = "https://pypi.python.org/packages/source/p/py/${name}.tar.gz"; 6982 md5 = "8f32ee0cd1e01472a255fe1d28d81217"; 6983 }; 6984 }; 6985
··· 531 }; 532 }); 533 534 + audioread = buildPythonPackage rec { 535 + name = "audioread-1.2.1"; 536 + 537 + src = pkgs.fetchurl { 538 + url = "https://pypi.python.org/packages/source/a/audioread/${name}.tar.gz"; 539 + md5 = "01a80357f38dbd9bf8d7403802df89ac"; 540 + }; 541 + 542 + meta = { 543 + description = "Cross-platform audio decoding"; 544 + homepage = "https://github.com/sampsyo/audioread"; 545 + license = stdenv.lib.licenses.mit; 546 + }; 547 + }; 548 + 549 + audiotools = buildPythonPackage rec { 550 + name = "audiotools-2.22"; 551 + 552 + src = pkgs.fetchurl { 553 + url = "mirror://sourceforge/audiotools/${name}.tar.gz"; 554 + sha256 = "1c52pggsbxdbj8h92njf4h0jgfndh4yv58ad723pidys47nw1y71"; 555 + }; 556 + 557 + meta = { 558 + description = "Utilities and Python modules for handling audio."; 559 + homepage = "http://audiotools.sourceforge.net/"; 560 + license = stdenv.lib.licenses.gpl2Plus; 561 + }; 562 + }; 563 + 564 autopep8 = buildPythonPackage (rec { 565 name = "autopep8-1.0.4"; 566 ··· 816 platforms = stdenv.lib.platforms.linux; 817 818 maintainers = [ stdenv.lib.maintainers.bluescreen303 ]; 819 }; 820 }; 821 ··· 2180 description = "derpconf abstracts loading configuration files for your app"; 2181 homepage = https://github.com/globocom/derpconf; 2182 license = licenses.mit; 2183 + }; 2184 + }; 2185 + 2186 + discogs_client = buildPythonPackage rec { 2187 + name = "discogs-client-2.0.2"; 2188 + 2189 + src = pkgs.fetchurl { 2190 + url = "https://pypi.python.org/packages/source/d/discogs-client/${name}.tar.gz"; 2191 + md5 = "2cc57e1d134aa93404e779b9311676fa"; 2192 + }; 2193 + 2194 + propagatedBuildInputs = with self; [ oauth2 requests ]; 2195 + 2196 + meta = { 2197 + description = "Official Python API client for Discogs"; 2198 + license = licenses.bsd2; 2199 + homepage = "https://github.com/discogs/discogs_client"; 2200 }; 2201 }; 2202 ··· 5769 }; 5770 5771 mutagen = buildPythonPackage (rec { 5772 + name = "mutagen-1.27"; 5773 5774 src = pkgs.fetchurl { 5775 url = "http://pypi.python.org/packages/source/m/mutagen/${name}.tar.gz"; 5776 + md5 = "6a9bb5cc33214add35348f1bb3448340"; 5777 }; 5778 5779 + # Needed for tests only 5780 + buildInputs = [ pkgs.faad2 pkgs.flac pkgs.vorbisTools pkgs.liboggz ]; 5781 5782 meta = { 5783 description = "Python multimedia tagging library"; ··· 6989 src = pkgs.fetchurl { 6990 url = "https://pypi.python.org/packages/source/p/py/${name}.tar.gz"; 6991 md5 = "8f32ee0cd1e01472a255fe1d28d81217"; 6992 + }; 6993 + }; 6994 + 6995 + 6996 + pyacoustid = buildPythonPackage rec { 6997 + name = "pyacoustid-1.1.0"; 6998 + 6999 + src = pkgs.fetchurl { 7000 + url = "https://pypi.python.org/packages/source/p/pyacoustid/${name}.tar.gz"; 7001 + md5 = "b27c714d530300b917eb869726334226"; 7002 + }; 7003 + 7004 + propagatedBuildInputs = with self; [ requests audioread ]; 7005 + 7006 + postPatch = '' 7007 + sed -i \ 7008 + -e '/^FPCALC_COMMAND *=/s|=.*|= "${pkgs.chromaprint}/bin/fpcalc"|' \ 7009 + acoustid.py 7010 + ''; 7011 + 7012 + meta = { 7013 + description = "Bindings for Chromaprint acoustic fingerprinting"; 7014 + homepage = "https://github.com/sampsyo/pyacoustid"; 7015 + license = stdenv.lib.licenses.mit; 7016 }; 7017 }; 7018