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 14 buildFlags = [ "OSTYPE=linux" ]; 15 15 16 16 installPhase = '' 17 - mkdir -p $out/usr/bin 18 - cp mp3gain $out/usr/bin 17 + install -vD mp3gain "$out/bin/mp3gain" 19 18 ''; 20 19 21 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 757 758 758 beanstalkd = callPackage ../servers/beanstalkd { }; 759 759 760 + beets = callPackage ../tools/audio/beets { }; 761 + 760 762 bgs = callPackage ../tools/X11/bgs { }; 761 763 762 764 biber = callPackage ../tools/typesetting/biber {
+75 -42
pkgs/top-level/python-packages.nix
··· 531 531 }; 532 532 }); 533 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 + 534 564 autopep8 = buildPythonPackage (rec { 535 565 name = "autopep8-1.0.4"; 536 566 ··· 786 816 platforms = stdenv.lib.platforms.linux; 787 817 788 818 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 819 }; 828 820 }; 829 821 ··· 2188 2180 description = "derpconf abstracts loading configuration files for your app"; 2189 2181 homepage = https://github.com/globocom/derpconf; 2190 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"; 2191 2200 }; 2192 2201 }; 2193 2202 ··· 5760 5769 }; 5761 5770 5762 5771 mutagen = buildPythonPackage (rec { 5763 - name = "mutagen-1.23"; 5772 + name = "mutagen-1.27"; 5764 5773 5765 5774 src = pkgs.fetchurl { 5766 5775 url = "http://pypi.python.org/packages/source/m/mutagen/${name}.tar.gz"; 5767 - sha256 = "12f70aaf5ggdzll76bhhkn64b27xy9s1acx417dbsaqnnbis8s76"; 5776 + md5 = "6a9bb5cc33214add35348f1bb3448340"; 5768 5777 }; 5769 5778 5770 - # one unicode test fails 5771 - doCheck = false; 5779 + # Needed for tests only 5780 + buildInputs = [ pkgs.faad2 pkgs.flac pkgs.vorbisTools pkgs.liboggz ]; 5772 5781 5773 5782 meta = { 5774 5783 description = "Python multimedia tagging library"; ··· 6980 6989 src = pkgs.fetchurl { 6981 6990 url = "https://pypi.python.org/packages/source/p/py/${name}.tar.gz"; 6982 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; 6983 7016 }; 6984 7017 }; 6985 7018