Today I got to install python for the first time on my new system. I’m a complete noob to python. I’ve used it a few times to start learning it, but always got sidetracked by something else. Now that this new PC has been built, I have felt the urge to get back into this type of thing.
I’ve been going through my drives and finally sorting everything to its final spot. 20+ years of computers & drives can add up to a TON of junk files taking up space. My Media drive is the current one I am working on and trying to figure out some self-hosting solutions.
I decided to use Calibre for e-book library management, with Moon+ Reader as my current android reading app. FolderSync seems like it would be a good app to keep my phone updated with whatever I add to the collection. The usage seems pretty straightforward so far. I think it’ll be a good addition to libby.
For the fairly small music collection I have so far, I’m going to attempt to use Beets, which is the reason for the python install. I installed a pretty old version first since I wasn’t paying attention and then kept getting SSL errors when trying to use pip. I did finally get an error message saying pip was like version 9 and the new version was like 25. Uninstalled then reinstalled the newest python and that seemed to work perfectly. I’m about to go through the Getting Started guide and see what can be done.
Well, it’s a few hours later and I kind of got it working. Threw together a config file that I’ll put at the end of this. I got it to mostly import my music and organize it. It is certainly faster than doing it manually, but the program keeps crashing with: Error: The process cannot access the file because it is being used by another process while moving … to …\cover.jpg. Led me to learn a little more, so now I can just do: ‘beet import G:\Music- –resume’. I’m getting a lot of “no matching release found” so I’m doing a lot of skipping. I’ll have to look at the ones that didn’t move and see if it’s an easy fix. All I really wanted to do was get everything the same style, so once I restart this enough, the project will be complete. Then it’ll be easy enough to run it again when I get new music.
directory: G:\Music
library: G:\Music\library.db
# files matching these patterns are deleted from source after import
#clutter: ["Thumbs.DB", ".DS_Store", "*.m3u", "*.pls",
# "*.jpg", "*.nfo", "*.txt", "*.log", "*.gif",
# ]
clutter: [ "Thumbs.DB", ".DS_Store", ".jpg", ".png" ]
replace:
'[\\]': ''
'[_]': '-'
'[/]': '-'
'^\.': ''
'[\x00-\x1f]': ''
'[<>:"\?\*\|]': ''
'\.$': ''
'\s+$': ''
'^\s+': ''
'^-': ''
'’': "'"
'′': "'"
'″': ''
'‐': '-'
# only interested in duplicate albums, and use bitrate as tiebreaker
duplicates:
album: yes
count: yes
delete: no
full: yes
# move: path
path: yes
strict: no
tiebreak:
# higher bitrate wins
items: [bitrate]
# I wish this didn't asciify explicit unicode characters, i.e │
#asciify_paths: yes
art_filename: cover # cover.*
format_album: $path
format_item: $path
ignore: ['System Volume Information', '+tracks']
ignore_hidden: yes
per_disc_numbering: no
sort_album: path+
sort_item: path+
sort_case_insensitive: yes
threaded: yes
timeout: 5.0
verbose: no
# Sets the albumartist for various-artist compilations. Defaults to 'Various
# Artists' (the MusicBrainz standard)
va_name: 'Various Artists'
# options->importer
import:
languages: en
default_action: apply
# remove causes a crash: https://github.com/beetbox/beets/issues/716
# Solution:
# while true; do beet import -Iq .; printf "\033[48;5;196;1m=======> AGAIN....\e[m\n"; sleep 3; done
# when using skip, might be a good idea to check what's happening:
# tail -f BEETS_IMPORTER_LOG | grep duplicate
# duplicate_action: ask
# duplicate_action: remove
duplicate_action: skip
non_rec_action: asis
# non_rec_action: ask
autotag: yes
write: yes
copy: no
link: no
move: yes
log: '~/BEETS_IMPORTER_LOG'
quiet_fallback: asis # when using the -q flag
# quiet_fallback: skip # when using the -q flag
# Either yes or no, controlling whether imported directories are recorded
# and whether these recorded directories are skipped. This corresponds to
# the -i flag to beet import.
incremental: yes
# options->plugin
plugins: [
'discogs',
'embedart',
'fetchart',
'lyrics',
]
discogs:
source_weight: 0.0
embedart:
auto: yes
ifempty: no
remove_art_file: no
fetchart:
sources:
- coverart: release
- coverart: releasegroup
- albumart
- amazon
- google
- itunes
- fanarttv
- lastfm
- wikipedia
auto: yes
cautious: yes
minwidth: 500
maxwidth: 1200
cover_format: jpeg
store_source: yes
lastfm:
user: betbot
lastgenre:
auto: yes
canonical: yes
force: no
source: artist
lyrics:
auto: yes
force: yes
sources: lrclib genius google tekstowo
synced: yes
musicbrainz:
extra_tags: [year, originalyear, catalognum,
country, media, label
]
# inline fun!
item_fields:
fmt: |
if format.lower() == 'flac':
return ' [FLAC]'
else:
return None
# pad track number with zero if < 10
padded_tracknr: "'{:02n}'.format(track)"
# remove spaces from catalognum
moh_catalog: catalognum.replace(" ", "")
# capture first artist as primary artist to avoid directories like this:
# · B/Britney Spears/
# · B/Britney Spears feat Madonna/
# · B/Britney Spears vs Metallica/
# > https://github.com/beetbox/beets/issues/3176
# > https://www.japh.se/2021/06/01/capture-primary-artist-as-a-separate-field-in-beets.html
#
# handles:
# · Artist,
# · Artist &
# · Artist feat
# · Artist feat.
# · Artist featuring
# · Artist ft.
# · Artist vs
# · Artist vs.
# · Artist &
#
# The idea is to use $first_artist in the beginning of the path format
# like so:
#
# %tcp{$first_artist}/%tcp{$albumartist}
#
# which will put 'Jennifer Lopez feat. Pitbull' inside the main Jennifer
# Lopez directory, but still keep the feat. part in the directory name
# inside it.
#
# J/Jennifer Lopez Feat. Pitbull/Jennifer Lopez Feat. Pitbull │2012│ Dance Again [Single, WEB, MP3]/01 Dance Again.mp3
# -> J/Jennifer Lopez/Jennifer Lopez Feat. Pitbull │2012│ Dance Again [Single, WEB, MP3]/01 Dance Again.mp3
first_artist: |
# import an album to another artists directory, like:
# Tom Jones │1999│ Burning Down the House [Single, CD, FLAC]
# to The Cardigans/+singles/Tom Jones & the Cardigans │1999│ Burning Down the House [Single, CD, FLAC]
# https://github.com/beetbox/beets/discussions/4012#discussioncomment-1021414
# beet import --set myartist='The Cardigans'
# we must first check to see if myartist is defined, that is, given on
# import time, or we raise an NameError exception.
try:
myartist
except NameError:
import re
return re.split(',|\\s+(feat(.?|uring)|&|(Vs|Ft).)', albumartist, 1, flags=re.IGNORECASE)[0]
else:
return myartist
first_artist_singleton: |
try:
myartist
except NameError:
import re
return re.split(',|\\s+(feat(.?|uring)|&|(Vs|Ft).)', artist, 1, flags=re.IGNORECASE)[0]
else:
return myartist
# file basename for singletons import - import as is, minus the extension.
base_name: |
import os.path
base = os.path.basename(path)
return os.path.splitext(base)[0]
album_fields:
cdtype: u'[%s]' % (albumtype.lower()) if albumtype else u''
notalbum: 0 if albumtype and albumtype.lower() == 'album' else 1
source: |
media = set([i.media.lower() for i in items])
format = set([i.format for i in items])
tbr = sum([i.bitrate for i in items])
abr = tbr / len(items) / 1000
bitdepth = sum([i.bitdepth for i in items]) // len(items)
# Init output
o = [f for f in format] if format else []
# Handle bitrate categories
for f in format:
if f == 'FLAC':
o.append(str(bitdepth) + 'bit')
if abr < 480 and abr >= 320:
o.append('320')
elif abr < 320 and abr >= 220:
o.append('V0')
elif abr < 215 and abr >= 170 and abr != 192:
o.append('V2')
elif abr == 192:
o.append('192')
elif abr < 170:
o.append(str(abr))
# Handle media aliases
for m in media:
if any(t in m for t in ('digital', 'file')):
o.append('WEB')
elif 'vinyl' in m:
o.append('Vinyl')
elif 'cd' in m:
o.append('CD')
elif m != '':
o.append(m)
return ", ".join(o)
mixtape_album: |
import re
album_fixed = album
return re.sub(r"G.unit Radio,?\s+(Pt|Part)[.]?\s*(.*)", r"G-Unit Radio \2", album_fixed, flags=re.IGNORECASE)
alb_status: |
# MB returns 4 values describing how "offical" a release is, they are:
# Official, Promotional, 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, '
elif 'Official' in albumstatus:
return 'Official, '
elif 'Promotion' in albumstatus:
return 'Promo, '
else:
return None
# alb_type: |
# if albumtype:
# return albumtype + ', '
# else:
# return None
alb_type: |
alb_types = ""
albumtypes_list = {
'ep': 'EP',
'lp': 'LP',
'single': 'Single',
'live': 'Live',
'remix': 'Remix',
'dj-mix': 'DJ-mix',
'mixtape/street': 'Mixtape',
'interview': 'Interview',
}
for key, value in albumtypes_list.items():
if albumtype == key:
alb_types += str(value)
if alb_types is not None:
if alb_types != '':
return alb_types + ', '
else:
return None
media_type: |
# https://musicbrainz.org/doc/Release/Format
# Lets Merge the variations of the same medium into the main
# medium name (Opinonated)
media_list = {
'12" Vinyl': 'Vinyl',
'10" Vinyl': 'Vinyl',
'7" Vinyl': 'Vinyl',
'Vinyl': 'Vinyl',
'CDr': 'CDR',
'CD-R': 'CDR',
'Cassette': 'Cassette',
'Digital Media': 'Web',
'CD': 'CD',
'File': 'Web',
'DVD': 'DVDA',
}
# Lets omit these instead of converging them under a similar label like above (Opinonated)
media_types_to_omit = ['Blu-spec CD']
if items[0].media in media_list:
return str(media_list[items[0].media]) + ', '
elif items[0].media in media_types_to_omit:
return None
elif items[0].media == '':
return None
else:
return str(items[0].media) + ', '
hasyear: 1 if year > 0 else 0
#>
#< autotagger
# To control how tolerant the autotagger is of differences, use the
# strong_rec_thresh option, which reflects the distance threshold below
# which beets will make a “strong recommendation” that the metadata
# be used.
# default is 0.04
match:
strong_rec_thresh: 0.08
# strong_rec_thresh: 0.14
# strong_rec_thresh: 0.20
# strong_rec_thresh: 0.10
# medium_rec_thresh: 0.25
required: year label country
preferred:
media: ['CD', 'Digital Media|File', 'Vinyl']
ignored: missing_tracks unmatched_tracks
ignored_media: ['Data CD', 'DVD', 'DVD-Video', 'Blu-ray', 'HD-DVD',
'VCD', 'SVCD', 'UMD', 'VHS',
]
#"
ui:
color: yes
# colors:
# text_success: '40'
# text_warning: '137'
# text_error: '160'
# text_highlight: '106'
# text_highlight_minor: '108'
# action_default: '197'
# action: '210'
aunique:
keys: albumartist albumtype year album
disambuguators: format mastering media label albumdisambig releasegroupdisambig
bracket: '[]'