beetbox / beetbox/beets

Track path formats not expanding in `set_fields` during import

Open
#5,549 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
15.7k
Forks
2.1k
Avg merge
4d 21h
Merged PRs (30d)
31

Description

When importing or re-importing albums I use the `set_fields` option to format incoming field metadata -- in my case using the `beets-titlecase` plugin. I would expect my configuration to format the `artist` and `title` fields using the `%tcp{}` function, but instead they are not expanded and written as `$Title` and `$Artist_diff`.

Disabling plugins completely prevents, the issue, but also negates the value of using this software for me. Disabling `bandcamp convert deezer discogs spotify replaygain web` specifically also prevents the issue, but I haven't been able to figure out why.

### Problem

Running this command in verbose (`-vv`) mode:

```sh
$ beet -vv /downloads/!!!\ -\ Let\ It\ Be\ Blue\ \(2022\)\ -\ WEB\ V0/
```

Led to this problem:

```
Should be imported as `/music/!!! - (2002) Let It Be Blue [VBR V0]/01. Normal People.mp3...` but is instead imported as `/music/!!! - (2002) Let It Be Blue [VBR V0]/01. $Title ($Artist_feat).mp3...
```

### Setup

* OS: Alpine Linux
* Python version: 3.10
* beets version: 2.2.0
* Turning off plugins made problem go away (yes/no): See note above

My configuration (output of `beet config`) is:

```yaml
album_fields:
mp3_info: |
import re

m = items[0].get('bitrate_mode', '')
s = re.sub('b|vbr-new|vbr-old|-| ', '', items[0].get('encoder_settings', ''))

if m == '':
m = 'CBR'

if not all(item.bitrate == items[0].bitrate for item in items):
m = 'VBR'

if 'preset' in s:
s = re.sub('altpreset|preset', 'AP', s)
s = s.replace('extreme', 'E').replace('insane', 'I').replace('standard', 'S')

if s == '':
if m == 'CBR':
s = items[0].get('bitrate', 0) // 1000
else:
t = 0

for item in items:
t += item.bitrate

s = t // len(items) // 1000

return ' '.join(filter(None, [m,str(s)]))

albumtypes:
bracket: '()'
ignore_va: compilation soundtrack
types:
- ep: 'EP'
- single: 'Single'

art_filename: front
bandcamp:
art: yes
search_max: 4

convert:
auto: yes
command: /config/convert.sh $source $dest
extension: mp3
ffmpeg: /usr/bin/ffmpeg
never_convert_lossy_files: yes
threads: 2

directory: /music

fetchart:
auto: yes
cover_format: JPEG
deinterlace: yes
high_resolution: yes
minwidth: 1000
maxwidth: 1000
quality: 100
sources:
- itunes
- coverart: releasegroup
- deezer
- coverart: releasegroup
- filesystem

format_album: $albumartist/($year) $album
format_item: ($year) $album/%if{$multi_disc,$disc_alt}$track. $title ($artist)
ftintitle:
auto: yes
format: (Feat. {0})

import:
bell: yes
copy: yes
detail: yes
from_scratch: yes
log: /config/beets.log
resume: ask
set_fields:
album: %tcp{$album%if{$albumdisambig, [$albumdisambig]}%if{$atypes, $atypes}}
albumartist: %tcp{$albumartist}
artist: %tcp{$artist_feat}
title: %tcp{$title}
timid: yes
write: yes

importadded:
preserve_mtimes: yes
preserve_write_mtimes: yes

importreplace:
replacements:
- album_fields: album artist title
item_fields: album artist title
replace:
'[\u2010-\u2015]': '-'
'[\u2018-\u201B]': ''''
'[\u201C-\u201F]': '"'
'[\u2026]': '...'
'\b([Aa]\.?[Kk]\.?[Aa]\.?)': 'AKA'
'\b(Mr\.?)\b': 'Mr.'
'\b(Mrs\.?)\b': 'Mrs.'
'\b(Ms\.?)\b': 'Ms.'
'\b([Vv]\.?[Ss]\.?)\b': 'vs.'

item_fields:
artist_diff: 1 if albumartist != artist else 0
artist_feat: |
a = artist

if 'Feat.' in a:
a = ' & '.join(a.rsplit(' and ', 1))

return a
disc_alt: u'%d' % (disc)
multi_disc: 1 if disctotal > 1 else 0

library: library.db
match:
preferred:
countries: ['US']
media: ['Digital Media|File', 'CD']

paths:
default: $albumartist - ($year) $album%if{$mp3_info, [$mp3_info]}/%if{$multi_disc,$disc_alt}$track. $title%if{$artist_diff, ($artist)}
singleton: Non-Album/$track. $title ($artist)

per_disc_numbering: yes
pluginpath:
- /config/plugins/beets-importreplace
- /config/plugins/beets-tcp
plugins: albumtypes bandcamp chroma convert deezer discogs duplicates edit fetchart ftintitle fuzzy importadded importreplace info inline missing replaygain tcp web zero
replace:
'[\x00-\x1F]': _
'[\xE2-\xE6]': a
'[\xE8-\xEB]': e
'[\xEC-\xEF]': i
'[\xF2-\xF6]': o
'[\xF8]': o
'[<>:"\?\*\|]': _
'\.$': ''
'\s+$': ''
'^\.': ''

sort_album: albumartist+ year+ album+
sort_item: year+ album+ disc+ track+ artist+
tcp:
asis: [
# artists
'$hort', 'AFI', 'INXS', 'MGMT',

# default overrides
'The',

# misc.
'ABC', 'AT&T', 'DJ', 'DNA', 'XXX',

# road abbreviations
'Blvd', 'Ln', 'Rd', 'St',
]

threaded: yes
va_name: Various Artists
ui:
color: yes

zero:
keep_fields: album albumartist albumtype artist comp disc disctotal rg_album_gain rg_album_peak rg_track_gain rg_track_peak singleton title track year
```

Contributor guide

Open the contributing guide

Research direction

Reproduce the import with the provided `beet -vv` command and configuration, focusing on `import.set_fields` and the `tcp` plugin while disabling the listed plugins to isolate the interaction. Trace why `%tcp{}` remains unexpanded during import; done means the configured artist and title fields expand correctly and the imported paths no longer contain `$Title` or `$Artist_diff`.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.