I do not like beets, but I like everything else less, especially manual Music metadata management. Because I must, my setup is a bit non-standard, I do some hackery with paths and tags in beets to organize my music so that Syncthing can more easily sync important parts to my devices rather than fucking around with generating file-exclusion lists and such.
I install it in home-manager with the copyArtifacts plugin.
nix source: :tangle ~/arroyo-nix/hm/beets.nix{ pkgs, config, ... }: let myBeets = pkgs.python3.pkgs.beets.override { pluginOverrides = { beetcamp = { propagatedBuildInputs = [ pkgs.beetcamp ]; }; # copyartifacts = { # propagatedBuildInputs = [ pkgs.python3.pkgs.beets-copyartifacts ]; # }; }; }; in { home.activation.beets-config = pkgs.lib.mkActivationLocalLink config "~/Music/beets/config.yaml" ".config/beets/config.yaml"; # home.packages = [ myBeets ]; home.packages = [ myBeets pkgs.beetcamp ]; }
DONE check up on nixpkgs comment about copyartifacts plugin
INPROGRESS add beetcamp to package external plugins
nix source: :tangle ~/arroyo-nix/pkgs/beetcamp.nix{ lib, fetchFromGitHub, callPackage, buildPythonPackage, beets, setuptools, poetry-core, requests, cached-property, pycountry, python-dateutil, ordered-set, pytest-cov, pytest-randomly, pytest-lazy-fixture, rich, tox, types-setuptools, types-requests, propagateBeets ? false }: buildPythonPackage { pname = "beets-beetcamp"; version = lib.pkgVersions.beetcamp.version; src = callPackage lib.pkgVersions.beetcamp.src {}; format = "pyproject"; propagatedBuildInputs = [ setuptools poetry-core requests cached-property pycountry python-dateutil ordered-set ] ++ (lib.optional propagateBeets [ beets ]); postInstall = '' # rm $out/lib/python*/site-packages/LICENSE # mkdir -p $out/share/doc/beetcamp # mv $out/lib/python*/site-packages/README.md $out/share/doc/beetcamp/README.md ''; checkInputs = [ # pytestCheckHook pytest-cov pytest-randomly pytest-lazy-fixture rich tox types-setuptools types-requests ] ++ [ beets ]; meta = { homepage = "https://github.com/snejus/beetcamp"; description = "Bandcamp autotagger plugin for beets."; license = lib.licenses.gpl2; inherit (beets.meta) platforms; maintainers = with lib.maintainers; [ rrix ]; }; }
File Organization
I keep my music in ~/Music and a few subdirectories underneath it so that i can use Syncthing 's filtering to narrow the Music i sync to devices with less storage like the Cosmo Communicator or whatever.
Albums I download go in to New New, usually just unzipped and kept outside of beets.
High High contains music I want anywhere
Middle Middle contains music that is interesting but I do not listen to often
Low Low is where music I don't listen to or purchased without fully intending to listen to live
Archive Archive is where music goes to die
Archive Old Archive Old contains low quality rips and music I listened to when I was much younger.
To import an album in a particular class collection:
shell source:~/.nix-profile/bin/beet -c ~/Music/beets/config.yaml import --set=class=high --write --resume $IMPORT_PATH
to modify existing and move them:
shell source:~/.nix-profile/bin/beet -c ~/Music/beets/config.yaml modify class=low low:1
interactive command for importing a zip file or glob of them in to beets from Dired File Manager
I hate using unzip, such terrible ergonomics. I have this tiny helper shell-script to unzip to /tmp and then I wrap it in an interactive command elisp:(cce/import-to-beets) to make it most easy:
emacs-lisp source: :tangle beets.el(defvar beet-command "~/.nix-profile/bin/beet -c ~/Music/beets/config.yaml import --write --resume") (defun cce/import-to-beets (file class) "Import a Bandcamp zip FILE in to a CLASS in my Music folder." (interactive "ffile or directory to import? \nMimport-class? ") (let ((directory (if (s-ends-with? "zip" file) (progn (shell-command (format "bash ~/org/cce/beets_unzip.sh \"%s\"" (expand-file-name file)) "*beets_unzip*" "*beets_unzip_error*") (if (and (get-buffer "*beets_unzip_error*") (buffer-size (get-buffer "*beets_unzip_error*"))) (error "zip script failed") (with-current-buffer (get-buffer "*beets_unzip*") (buffer-string)))) file))) (async-shell-command (format "%s --set=class=%s \"%s\"" beet-command class directory) "*beets-import*")))
shell source: :tangle beets_unzip.sh :shebang #!/bin/env shTMPD=$(mktemp -d /tmp/bandcampXXXX) cd $TMPD unzip "$1" &>/tmp/bandcamp_unzip.log echo $TMPD
NEXT tag out "night music", ambient sleep stuff and code drone, music to sing along
Beets Configuration
yaml source: :tangle ~/Music/beets/config.yamlplugins: fetchart embedart convert scrub replaygain lastgenre web fetchart smartplaylist copyartifacts discogs bandcamp directory: /home/rrix/Music library: /home/rrix/Music/beets/musiclibrary.blb art_filename: albumart threaded: yes original_date: no per_disc_numbering: no
The class field describes the file organization hierarchy and is used in the path selection per the "Advanced Awesomeness" documentation to achieve this.
yaml source: :tangle ~/Music/beets/config.yamlpaths: default: New/%asciify{$albumartist}/%asciify{$album}%aunique{}/$track - %asciify{$title} class:high: High/%asciify{$albumartist}/%asciify{$album}%aunique{}/$track - %asciify{$title} class:middle: Middle/%asciify{$albumartist}/%asciify{$album}%aunique{}/$track - %asciify{$title} class:low: Low/%asciify{$albumartist}/%asciify{$album}%aunique{}/$track - %asciify{$title} class:archive: Archive/%asciify{$albumartist}/%asciify{$album}%aunique{}/$track - %asciify{$title}
I use copyartifacts to copy album art, pdfs, etc
yaml source: :tangle ~/Music/beets/config.yamlcopyartifacts: extensions: .*
import configuration is pretty simple, geared towards assuming the files are fungible, and indeed they are since i'll probably be running imports on my laptop from bandcamp downloads:
yaml source: :tangle ~/Music/beets/config.yamlimport: languages: en ja write: yes copy: no move: yes resume: ask incremental: yes quiet_fallback: skip timid: no log: /home/rrix/Music/beets/beet.log
I use =beetcamp= :
yaml source: :tangle ~/Music/beets/config.yamlbandcamp: search_max: 5 art: yes genre: capitalize: no mode: progressive
Fetch genres from Last.FM:
yaml source: :tangle ~/Music/beets/config.yamllastgenre: auto: yes source: album
Embed album art in the files
yaml source: :tangle ~/Music/beets/config.yamlembedart: auto: yes remove_art_file: yes
Fetch art where possible
yaml source: :tangle ~/Music/beets/config.yamlfetchart: auto: yes sources: "*" store_source: yes
Enable and disable various other plugins:
yaml source: :tangle ~/Music/beets/config.yamlchrome: auto: yes replaygain: auto: no scrub: auto: yes replace: '^\.': _ '[\x00-\x1f]': _ '[<>:"\?\*\|]': _ '[\xE8-\xEB]': e '[\xEC-\xEF]': i '[\xE2-\xE6]': a '[\xF2-\xF6]': o '[\xF8]': o '\.$': _ '\s+$': '' web: host: 0.0.0.0 port: 8337 reverse_proxy: true acousticbrainz: auto: yes
Smart Playlists
run shell:beet -c ~/Music/beets/config.yaml splupdate & (make sure to tangle this if you change it!):
yaml source: :tangle ~/Music/beets/config.yamlsmartplaylist: relative_to: /home/rrix/Music playlist_dir: /home/rrix/Music/playlists playlists: - name: upbeat.m3u query: - mood_happy::^0.99 ^genre::Rock ^artist::Vanilla ^artist:Bananarama ^album::Evangelion ^album::3.0 ^genre:Grunge ^class:archive - artist:"Electric Six" - danceable::^0.99999 ^genre::Rock ^artist::Vanilla ^artist:Bananarama ^album::Evangelion ^album::3.0 ^genre:Grunge ^class:archive - name: chiptunes.m3u query: - artist:Anamanaguchi - artist:"Bit Shifter" - artist:Chipzel - artist:chipzel - artist::Demoscene - album::Fez - album::"Hyper Light Drifter" - album::"Toffelskater" - artist:Fantomenk - artist:"Fighter X" - artist:"PDF Format" - artist:Sabrepulse - artist:Spamtron - artist:spamtron - genre:8-bit - genre:Chiptunes - name: j-pop.m3u query: - album::[Gg][Uu][Nn][Dd][Aa][Mm] - album::ガンダムユニコ - artist::まりや - artist:"Maria Asahina" - artist:"松原みき" - album::NieR - genre:J-pop - genre:J-Rock - name: various-and-sundry.m3u query: - album:"Birdy Nam Nam" - artist::Flashbulb - genre:vaporwave - genre:Ambient - artist:'Janel & Anthony' - artist:'Anthony Pirog' - name: music-for-sleep.m3u query: - album:CATCH‐Wave - album::Disassembly - artist:"Music For Sleep" - album:"Glider 10" - album:Liminal - album:"Solar Fields" - artist:"Matt Borghi" - artist:"Umber" - artist:"the volume settings folder" - artist:"Brian Eno"