Created
May 19, 2020 23:57
-
-
Save RollingStar/86e041338df295afbbf77a9027903068 to your computer and use it in GitHub Desktop.
beets_config.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# don't copy paste this into your config. It's tailored to me and I haven't really stress-tested it outside of my own use. Also, I was mostly using this in 2017-2019 and features may have changed. | |
# Some plugins have been retired and I haven't migrated yet. copyartifacts is the best example of this. | |
# Just use this as a starting point to learn about advanced, undocumented/poorly-documented features of beets. | |
directory: e:\Music\ | |
library: e:\Music\beetslibrary.bib | |
#copyartifactspy3 chroma scrub | |
plugins: the inline chroma fetchart orig_date convert mbsync duplicates replaygain info alternatives | |
# scrub: | |
# auto: no | |
asciify_paths: false | |
alternatives: | |
sd: | |
directory: 'd:\c5\' | |
# paths are pulled from beets settings if not specified here | |
formats: qaac mp3 aac | |
#query: "onplayer:true" | |
#query: "albumartist:beatles" | |
# query: "why do it road" | |
#query: britney, beatles | |
query: channels:..2 | |
# query: weird al | |
removable: false | |
replaygain: | |
backend: ffmpeg | |
# multidisc albums often have different mastering; ex. if disc 2 is a live recording | |
per_disc: yes | |
import: | |
log: e:\Music\beet.log | |
#copy: yes | |
move: yes | |
write: yes | |
timid: no | |
languages: en | |
quiet: no | |
duplicate_action: ask | |
# duplicate_action: skip | |
none_rec_action: ask | |
quiet_fallback: skip | |
per_disc_numbering: yes | |
musicbrainz: | |
#host: localhost:5000 | |
#default= 1 | |
#ratelimit: 80 | |
#default= 5 | |
#make higher for prolific artists (ex. Beatles) because otherwise, beets may pick the wrong release from the release group | |
searchlimit: 5 | |
terminal_encoding: | |
'utf_8' | |
convert: | |
dest: d:\c3 | |
copy_album_art: yes | |
embed: no | |
album_art_maxwidth: 1080 | |
never_convert_lossy_files: True | |
convert_lossy: False | |
format: qaac | |
threads: 3 #num_cpus minus 1 | |
formats: | |
#specify --rate 44100 to downsample high-res audio to Red Book CD standard | |
#(and, I guess, upsample lower-sample-rate audio to 44.1 KHz) | |
#I would like to keep stereo as stereo, but downmix 3+ channels to 3.0 channels (front left, front right, front center). | |
#this would probably require something more complicated than I am willing to set up at this point. | |
#So, just downsample everything to stereo. | |
#removed "--threading" option because the convert plugin appears to thread the parent calls to the convert command. Expected result: as many simultaneous commands as CPU threads. | |
#cmd ffmpeg -i "$source" -ac 2 -f flac - | | |
qaac: | |
# cshim is a private (for now) piece of trash 'compatibility shim' that connects to qaac.exe and ffmpeg. | |
# among other issues, it has a shell execution vulnerability, so I'm reluctant to share it. | |
command: python c:\\apps\\cshim.py "$source" "$dest" | |
extension: m4a | |
artist_credit: yes | |
#just for testing | |
# match: | |
# max_rec: | |
# missing_tracks: none | |
# artist: none | |
# year: none | |
match: | |
#.1 = 90% similarity required. automatically matches above the threshold. | |
#even at the chosen threshold, can be unintuitive and not automatically select the choice that meets the threshold. I think. | |
strong_rec_thresh: .05 | |
#medium_rec_thresh: .3 | |
#low_rec_thresh: .4 | |
#see https://beets.readthedocs.io/en/latest/reference/config.html#preferred | |
preferred: | |
media: ['CD', 'Digital Media|File', 'Digital Media'] | |
countries: ['US', 'GB|UK'] | |
distance_weights: | |
#should help "quiet" matching. If beets is too eager to match incorrectly and ignore missing tracks, | |
#then this creates more work for the user later when they manually correct the matches to a release | |
#without the tracks, or find the missing tracks. | |
missing_tracks: 10 | |
unmatched_tracks: 10 | |
#try to force away from vinyl | |
#media: 20.0 | |
#mediums: 20.0 | |
#"You can now customize the character substituted for path separators (e.g., /) in filenames via path_sep_replace. The default is an underscore. Use this setting with caution." | |
#do this for replacing "/" in paths, i.e., in album titles / track titles | |
#one would think this also makes the path seperator change from "/" but I have not found this to be the case. | |
#BIG SOLIDUS | |
path_sep_replace: '⧸' | |
#replace only works on paths (shouldn't change any internal tags) | |
# the "replace" setting seems to follow Python regex rules. | |
# Use Python 2.x or 3.x documentation depending on the Python version you're using with beets. See the output of `beet version`. | |
# https://docs.python.org/3/library/re.html | |
# sanitize_path within beets: | |
# https://github.com/beetbox/beets/blob/68089ac8e913b8175876b50cc7086bba8f355a5f/beets/util/__init__.py#L563 | |
#any customization in "replace" causes beets to not use the default "replace" values. | |
#So don't only half-fill out this section. If you put anything here, you have to make the whole | |
#section robust to strange characters that can make OS filesystems complain. | |
#see config_default.yaml in beets. | |
# https://github.com/beetbox/beets/blob/master/beets/config_default.yaml | |
#replace problematic characters with lookalikes | |
replace: | |
#BIG SOLIDUS | |
'/': '⧸' | |
#29FS BIG REVERSE SOLIDUS | |
'\\': ⧵ | |
#2223: DIVIDES | |
'\|': ∣ | |
#02C2 MODIFIER LETTER LEFT ARROWHEAD | |
'<': ˂ | |
#O2C3 MODIFIER LETTER RIGHT ARROWHEAD | |
'>': ˃ | |
#replace starting & closing periods with _ | |
'^\.': _ | |
'\.$': _ | |
'\s+$': '' | |
'^\s+': '' | |
#replace : with ։ (lookalike) | |
'\:': ։ | |
#replace " with two single apostrophes '' | |
"\"": "''" | |
'\“': "''" | |
'\”': "''" | |
'[\*]': ✶ | |
'[\?]': ? | |
#elipses are a bit annoying but this way avoids ending filenames with "." | |
'[\.]{3}$': … | |
#hopefully exclude DEL ␡ from the next Regex | |
'␡': '␡' | |
#control characters including Delete (DEL) ␡, see previous rule. | |
#should just exclude '[\x00-\x1f]'. | |
#commented out because it interpreted the end of every filename before the extension as a | |
#control character. maybe this is because of "end of text" (U+0003)? | |
#'[\x00-\x1f]': _ | |
#fake comment and fake tab options | |
#listen, these options are hacky. If you use symbols expected in context (like a quotation mark), you may break the comment you're trying to write. | |
#the best option in the long-term is real comment and tab support in the path config. This is just a workaround for readability reasons. | |
#there is an open issue for better newline support: | |
# https://github.com/beetbox/beets/issues/2147 | |
#start comments in paths with `#` and end them with `zxc\`. The \ is to avoid adding a space after a newline. Inelegant solution that will fail on any section of path (any line?) with # and zxc in the same section. Ex. an album called "The Strange #zxc". | |
#do not put comments inside of %left, %right, or any other tag that truncates. Specifically, it seems that left% and similar functions evaluate first, before the replace regex. If your comment gets eaten by the function, it will no longer match the regex. | |
#top level comments are the safest; things get less stable the deeper in a nested function that you go. for example, a comma "," in an %if statement comment can give | |
#unintuitive results for the if statement. | |
'\#.*zxc': '' | |
#fake tab (4+ spaces) to have tabs in paths. will still match to any path that really should have 4+ spaces in it. Ex. an album called "Trouble Maker" | |
' {4,}': '' | |
#end all lines with a backslash: \ | |
#see precautions above in "replace" for using fake comments and/or fake tabs (4+ spaces) | |
paths: | |
#regex to match all items. (avoid having to separately define compilations and singletons.) | |
title::.*: "%if{\ | |
$alb_artist,\ | |
%the{\ | |
$alb_artist\ | |
} - ,\ | |
No Artist - \ | |
}\ | |
%if{\ | |
$alb_promo_boot,\ | |
$alb_promo_boot%if{\ | |
$alb_exotic_type,\ | |
#add literal comma and space if promo_boot and exotic_type exist for the same album. zxc\ | |
#for example Promo comma Spoken Word zxc\ | |
$, , - \ | |
}\ | |
}\ | |
%if{\ | |
$alb_exotic_type,\ | |
$alb_exotic_type - \ | |
}\ | |
$alb_orig_year - \ | |
$alb_title\ | |
%aunique{\ | |
album $alb_orig_year_mm_dd albumartist albumstatus albumtype,\ | |
albumdisambig country label catalognum,\ | |
()\ | |
}\ | |
$i_cust_catalog%if{\ | |
$alb_subset_bitrate, \ | |
[$alb_subset_bitrate]\ | |
}\ | |
/%if{\ | |
$i_disc_layer,\ | |
$i_disc_layer-\ | |
}\ | |
%if{\ | |
$track,\ | |
$i_track. ,\ | |
[no track number]. \ | |
}\ | |
%if{\ | |
$title,\ | |
$title,\ | |
[unknown]\ | |
}" | |
#I still have not found a color scheme I really like for all this | |
# https://beets.readthedocs.io/en/v1.4.5/reference/config.html#colors | |
ui: | |
color: yes | |
colors: | |
text_success: green | |
text_warning: yellow | |
text_error: red | |
text_highlight: magenta | |
text_highlight_minor: brown | |
action_default: cyan | |
action: green | |
#avoid conflicts with existing files like "cover.jpg" "front.jpg" etc. | |
#art_filename: 'covauto' | |
art_filename: 'front' | |
fetchart: | |
#uses art_filename (see above) | |
#Pick only trusted album art by ignoring filenames that do not contain one of the keywords in cover_names. Default: no. | |
cautious: no | |
#drop the first letter to allow for capital or lowercase as a trusted keyword | |
cover_names: over ront art lbum older | |
#filesystem causes conflicts (hard stops in beets import) when used alongside copyartifacts | |
#some of the latter items require API keys which are not configured. as such, they will not work if you just use this config as-is | |
# filesystem | |
sources: coverart amazon albumart wikipedia google fanarttv | |
#posthumous is not yet a possible distinguisher (something I want in the future) | |
# https://github.com/beetbox/beets/issues/2338 | |
album_fields: | |
alb_promo_boot: | | |
#official, promo, bootleg, and pseudo-release. we only note the middle two. https://musicbrainz.org/doc/Release#Status | |
if 'Promo' in albumstatus: | |
return 'Promo' | |
elif 'Bootleg' in albumstatus: | |
return 'Bootleg' | |
else: | |
return '' | |
alb_exotic_type: | | |
#no special noting of albums, EPs, and soundtracks | |
if albumtype in {'album', 'ep', 'soundtrack'}: | |
return '' | |
#special case for proper spacing and caps | |
elif 'spokenword' in albumtype: | |
return 'Spoken Word' | |
else: | |
return str.title(albumtype) | |
alb_subset_bitrate: | | |
#return bitrate information for albums <150kbps. | |
#do nothing for higher-bitrate albums, because they are assumed to be perceptually lossless. | |
br_threshhold = 150 | |
max_tracks_to_check = 10 | |
def sample_tracks(tracks): | |
#reproducible randomness for debugging. should be excluded for optimal randomness from album to album. | |
#otherwise, any album with same number of tracks will get same sample of track numbers, I think | |
from random import seed, sample | |
seed(25) | |
num_items = len(tracks) | |
sample_size = min(max_tracks_to_check, num_items) | |
test_tracks = sample(tracks, sample_size) | |
return(test_tracks) | |
test_tracks = sample_tracks(items) | |
sample_size = len(test_tracks) | |
total = 0 | |
for item in test_tracks: | |
total += item.bitrate | |
album_br_bits_ps = total / sample_size | |
# music bitrates are base 10: | |
# https://hydrogenaud.io/index.php/topic,12633.0.html | |
album_br_kbps = album_br_bits_ps / 1000 | |
if album_br_kbps > br_threshhold: | |
out_text = '' | |
else: | |
#return Opus, MP3, "Mixed", etc. | |
#uses same test_tracks from earlier sample | |
alb_format = set() | |
for item in test_tracks[0:sample_size]: | |
alb_format.add(item.format) | |
if len(alb_format) != 1: | |
alb_format = 'Mixed' | |
else: | |
alb_format = alb_format.pop() | |
out_text = str(int(album_br_kbps)) + 'kbps ' + str(alb_format) | |
return out_text | |
alb_title: | | |
#convert long allcaps titles to title case, while leaving other titles alone | |
allowed_length = 3 | |
allowed_allcaps_artists = ['573fd3e2-3f61-4329-a6c1-89e20620b0b9'] | |
if album.isupper and len(album) > allowed_length: | |
if mb_albumartistid not in allowed_allcaps_artists: | |
from titlecase import titlecase | |
return titlecase(album) | |
if len(album) < 1: | |
return "[untitled]" | |
return album | |
alb_artist: | | |
VA_STR = "Various Artists" | |
if albumartist: | |
return albumartist | |
else: | |
# the album probably has no MBid | |
# return VA_STR if multiple artists are found on the album | |
my_artists = set() | |
for item in items: | |
my_artists.add(item.artist) | |
if len(my_artists) > 1: | |
return VA_STR | |
# every artist is the same | |
return my_artists.pop() | |
item_fields: | |
i_track: | | |
# hotfix for beets' hardcoded 2-digit track numbers | |
# https://github.com/beetbox/beets/issues/3352 | |
str_track = str(track) | |
# pad based on length of highest track number (per disc). | |
# may interact with per_disc_numbering, not sure | |
pad_length = len(str(tracktotal)) | |
if pad_length < 2: | |
pad_length = 2 | |
return str_track.zfill(pad_length) | |
i_disc_layer: | | |
#do nothing for single-disc releases | |
if disctotal > 1: | |
if disc != '': | |
str_disc = str(disc) | |
#pad based on length of highest disc number. | |
#ex. if total discs = 2 digits, pad 1 zero for discs 1-9 (01-09) | |
legth_to_pad_to = len(str(disctotal)) | |
return str_disc.zfill(legth_to_pad_to) | |
return '' | |
i_condition: | | |
#set as a flex attribute(?) elsewhere | |
return '' | |
i_cust_catalog: | | |
#omit uninteresting formats | |
#these formats are bit-identical to CD | |
snake_oil_formats = ['Blu-spec CD', 'SHM-CD', 'HQCD'] | |
media_types_to_omit = snake_oil_formats + ['CD', 'CD-R', 'Enhanced CD', 'CDDA', 'Digital Media', ''] | |
def item_cust_media(): | |
#see https://musicbrainz.org/doc/Release/Format | |
if media in media_types_to_omit: | |
return '' | |
#combine hybrid SACD with SACD, see https://en.wikipedia.org/wiki/Super_Audio_CD#Technology | |
elif 'SACD' in media: | |
return 'SACD' | |
#combine all vinyl types into "Vinyl" | |
elif 'Vinyl' in media: | |
#https://en.wikipedia.org/wiki/VinylDisc | |
if media == 'VinylDisc': | |
return 'VinylDisc' | |
else: | |
return 'Vinyl' | |
elif "USB" in media: | |
return 'USB' | |
elif media == 'HD-DVD': | |
return 'HD-DVD' | |
# note: DualDisc contains a DVD side and a CD side. | |
elif media == "DualDisc": | |
return "DVD" | |
elif 'DVD' in media: | |
return 'DVD' | |
else: | |
return media | |
def reissue(): | |
if year > original_year: | |
if original_year > 0: | |
return str(year) | |
return '' | |
# condition = custom attribute to note (mostly) vinyl skips | |
# and other seriously noticable artifacts. | |
result = reissue() + ' ' + item_cust_media() # + i_condition() | |
result = result.strip(' ') | |
if result != '': | |
return ' (' + result + ')' | |
return result |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment