beets-multivalue 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- beets_multivalue-0.2.0/LICENSE +21 -0
- beets_multivalue-0.2.0/PKG-INFO +239 -0
- beets_multivalue-0.2.0/README.md +211 -0
- beets_multivalue-0.2.0/beets_multivalue.egg-info/PKG-INFO +239 -0
- beets_multivalue-0.2.0/beets_multivalue.egg-info/SOURCES.txt +13 -0
- beets_multivalue-0.2.0/beets_multivalue.egg-info/dependency_links.txt +1 -0
- beets_multivalue-0.2.0/beets_multivalue.egg-info/requires.txt +1 -0
- beets_multivalue-0.2.0/beets_multivalue.egg-info/top_level.txt +2 -0
- beets_multivalue-0.2.0/beetsplug/__init__.py +3 -0
- beets_multivalue-0.2.0/beetsplug/multivalue.py +422 -0
- beets_multivalue-0.2.0/pyproject.toml +2 -0
- beets_multivalue-0.2.0/setup.cfg +4 -0
- beets_multivalue-0.2.0/setup.py +27 -0
- beets_multivalue-0.2.0/tests/__init__.py +0 -0
- beets_multivalue-0.2.0/tests/test_multivalue.py +390 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Eric Masseran
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: beets-multivalue
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A beet plugin to manage multi-value fields
|
|
5
|
+
Home-page: https://github.com/Morikko/beets-multivalue
|
|
6
|
+
Author: Eric Masseran
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: beets>2
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: description
|
|
22
|
+
Dynamic: description-content-type
|
|
23
|
+
Dynamic: home-page
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: summary
|
|
28
|
+
|
|
29
|
+
# Multi-value Plugin
|
|
30
|
+
|
|
31
|
+
Multi-values on a tag is useful to provide different and independent information
|
|
32
|
+
belonging to the same context.
|
|
33
|
+
|
|
34
|
+
This plugin provides a boosted modify command to also add/remove values in
|
|
35
|
+
multi-value tag. It can also fix `grouping` field and add `work` field.
|
|
36
|
+
|
|
37
|
+
## Beet official list tags
|
|
38
|
+
|
|
39
|
+
A few fields already support native multi-values separated with `\␀`. Those are
|
|
40
|
+
always available in the command without a configuration change. The current
|
|
41
|
+
fields are:
|
|
42
|
+
|
|
43
|
+
- artists
|
|
44
|
+
- albumartists
|
|
45
|
+
- artists_sort
|
|
46
|
+
- artists_credit
|
|
47
|
+
- albumartists_sort
|
|
48
|
+
- albumartists_credit
|
|
49
|
+
- mb_artistids
|
|
50
|
+
- mb_albumartistids
|
|
51
|
+
|
|
52
|
+
## String multi-value tags
|
|
53
|
+
|
|
54
|
+
Some fields do not support multi-values yet like
|
|
55
|
+
[genre](https://github.com/beetbox/beets/pull/5426) or some may never support it
|
|
56
|
+
as not a "standard" like `grouping`.
|
|
57
|
+
|
|
58
|
+
An example could be to add multiple genres separated by a comma: `Rock,Hard Rock`
|
|
59
|
+
|
|
60
|
+
Some external tools also supports custom separator splitting like
|
|
61
|
+
[navidrome](https://www.navidrome.org/docs/usage/customtags/#changing-separators)
|
|
62
|
+
since v0.55.0.
|
|
63
|
+
|
|
64
|
+
By default, no field is considered a string multi-value. Each one must be
|
|
65
|
+
explicitly defined in the configuration with the expected separator:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
multivalue:
|
|
69
|
+
string_fields:
|
|
70
|
+
grouping: ";"
|
|
71
|
+
genre: ","
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Multi Modify Command
|
|
75
|
+
|
|
76
|
+
### Usage
|
|
77
|
+
|
|
78
|
+
```shell
|
|
79
|
+
# Initial: genre: Rock
|
|
80
|
+
|
|
81
|
+
# Add a value
|
|
82
|
+
beet multimodify grouping+="Hard Rock" <query>
|
|
83
|
+
# genre: Rock,Hard Rock
|
|
84
|
+
|
|
85
|
+
# Add and remove values
|
|
86
|
+
beet mmod genre+="Classic Rock" genre-="Hard Rock" <query>
|
|
87
|
+
# genre: Rock,Classic Rock
|
|
88
|
+
|
|
89
|
+
# Original modify command still applies
|
|
90
|
+
beet mmod genre+="Classic Rock" genre-="Hard Rock" year! title="Best song"
|
|
91
|
+
|
|
92
|
+
# Adding the same value is detected and avoided. By default, exact match is applied.
|
|
93
|
+
# It is easy to remember, there is the "=", the same used in a regular query.
|
|
94
|
+
# Initial: genre: Rock
|
|
95
|
+
beet multimodify grouping+="Rock" <query>
|
|
96
|
+
# genre: Rock
|
|
97
|
+
|
|
98
|
+
# For case insensitivity, add "~" as in a regular query.
|
|
99
|
+
beet multimodify grouping+=~rock <query>
|
|
100
|
+
# genre: Rock
|
|
101
|
+
|
|
102
|
+
# It is also possible to use the ones from a plugin by adding the equivalent prefix.
|
|
103
|
+
# For example with bareasc set to the prefix "#".
|
|
104
|
+
# Initial: artists: [Eric]
|
|
105
|
+
beet multimodify artists+=#Éric <query>
|
|
106
|
+
# no change
|
|
107
|
+
|
|
108
|
+
# The same work for removing
|
|
109
|
+
|
|
110
|
+
# Initial: genre: Rock
|
|
111
|
+
beet multimodify grouping-="Blues" <query>
|
|
112
|
+
# No change
|
|
113
|
+
|
|
114
|
+
# For case insensitivity, add "~" as in a regular query
|
|
115
|
+
# Initial: genre: Rock
|
|
116
|
+
beet multimodify grouping-=~rock <query>
|
|
117
|
+
# genre: ""
|
|
118
|
+
|
|
119
|
+
# For bareasc set to the prefix "#"
|
|
120
|
+
# Initial: artists: [Éric]
|
|
121
|
+
beet multimodify artists-=#Eric <query>
|
|
122
|
+
# artists: []
|
|
123
|
+
|
|
124
|
+
# Removing is also supporting Regex
|
|
125
|
+
# Initial: artists: [Eric]
|
|
126
|
+
beet multimodify artists-=:E?ic <query>
|
|
127
|
+
# artists: []
|
|
128
|
+
|
|
129
|
+
# Adding can not support regex as else the regex itself would be added.
|
|
130
|
+
# If you want to harmonize the data, you may remove and add at the same time.
|
|
131
|
+
# Initial: genre: Rock&Roll
|
|
132
|
+
beet multimodify 'genre-=:Rock.+' genre+=Rock <query>
|
|
133
|
+
# genre: Rock
|
|
134
|
+
|
|
135
|
+
# Order of execution
|
|
136
|
+
# genre: original
|
|
137
|
+
beet mm genre+=base genre-=base genre=base,pivot
|
|
138
|
+
# genre: pivot,base
|
|
139
|
+
|
|
140
|
+
# Deletion always win
|
|
141
|
+
beet mm genre! genre+=base genre-=base genre=base,pivot
|
|
142
|
+
# genre:
|
|
143
|
+
|
|
144
|
+
# Reset field first
|
|
145
|
+
beet mm genre= genre+=new genre-=new2
|
|
146
|
+
# genre: new,new2
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The command is influenced by the `modify` one and provides the same flags. By
|
|
150
|
+
default, a confirmation after showing the diff is requested and highly
|
|
151
|
+
recommended to avoid any data loss.
|
|
152
|
+
|
|
153
|
+
### Order of execution
|
|
154
|
+
|
|
155
|
+
1. Direct assignment are always run first: `genre=Rock`. If multiple assignments
|
|
156
|
+
on the same field are written, the last one win.
|
|
157
|
+
|
|
158
|
+
2. Values are removed: `genre-=Rock`. It allows to do some pre-cleaning before
|
|
159
|
+
adding values in the same run. Always with capital R: `genre-=rock
|
|
160
|
+
genre+=Rock`. Values are removed in order from the CLI `genre-=Rock
|
|
161
|
+
genre-=Classic`: first `Rock` then `Classic`.
|
|
162
|
+
|
|
163
|
+
3. Values are added in order from the CLI `genre+=Rock genre+=Classic`: first
|
|
164
|
+
`Rock` then `Classic`.
|
|
165
|
+
|
|
166
|
+
The order above is not impacted if the CLI options are unordered: `genre+=Rock
|
|
167
|
+
genre=Blues` would still do the assignment first.
|
|
168
|
+
|
|
169
|
+
The deletion `genre!` is always run last whatever its position. It keeps the
|
|
170
|
+
compatibility with the actual `modify` command behavior. To reset a field before
|
|
171
|
+
adding values, one must use `genre= genre+=Rock` or `artists= artists+=Eric`.
|
|
172
|
+
|
|
173
|
+
### Performance
|
|
174
|
+
|
|
175
|
+
To optimize performance and avoid iterating over a lot of data, the query should
|
|
176
|
+
prune items as much as possible.
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
# Only iterate over items without the Rock word in genre
|
|
180
|
+
beet multimodify genre+=Rock '^genre:Rock'
|
|
181
|
+
|
|
182
|
+
# Only iterate over items with the Rock word in genre
|
|
183
|
+
beet multimodify genre-=Rock 'genre:Rock'
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Limitation
|
|
187
|
+
|
|
188
|
+
A/ Sub-Optimal Diff
|
|
189
|
+
|
|
190
|
+
The diff may be sub-optimal as it does not know about the separator. In the
|
|
191
|
+
following example "Classic Rock" is not highlighted continuously although the
|
|
192
|
+
final change is still accurate: `genre: Hard Rock,**Classic** Rock,**Rock** ->
|
|
193
|
+
Hard Rock,Rock`.
|
|
194
|
+
|
|
195
|
+
B/ Relation to the original modify command
|
|
196
|
+
|
|
197
|
+
The modify command has been "copied" and upstream changes won't apply without a
|
|
198
|
+
port in this plugin.
|
|
199
|
+
|
|
200
|
+
C/ No order support
|
|
201
|
+
|
|
202
|
+
I have no need for ordering the values in the tag, as a result, it is always
|
|
203
|
+
added last.
|
|
204
|
+
|
|
205
|
+
## Grouping/Work fields
|
|
206
|
+
|
|
207
|
+
`mediafile` the file level library is using the wrong tag name for MP3 (see
|
|
208
|
+
[issue](https://github.com/beetbox/mediafile/issues/15)). As the `grouping`
|
|
209
|
+
field was my first motivation to use string multi-value, this plugin is also
|
|
210
|
+
able to fix the field.
|
|
211
|
+
|
|
212
|
+
The changes are:
|
|
213
|
+
- `grouping`: for MP3 is changed to `GRP1` and ASF does not support it as
|
|
214
|
+
without a defined value
|
|
215
|
+
- `work` field is added for MP3 (`TIT1`), MP4 (`@wrk`), Vorbis (`WORK`) and ASF
|
|
216
|
+
(`WM/ContentGroupDescription`). The values for MP3 and ASF were previously
|
|
217
|
+
assigned to `grouping`.
|
|
218
|
+
|
|
219
|
+
The tags are chosen from [Kid3
|
|
220
|
+
table](https://kid3.sourceforge.io/kid3_en.html#frame-list).
|
|
221
|
+
|
|
222
|
+
It is disabled by default so to enable it:
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
multivalue:
|
|
226
|
+
fix_media_fields: true
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
It is required to make beets read the tags from the file again as else the kept
|
|
230
|
+
values in DB are the old ones from the potential wrong fields:
|
|
231
|
+
|
|
232
|
+
```shell
|
|
233
|
+
beet update -F work -F grouping
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
WARNING: As the `work` field was not saved to the file previously, by reading
|
|
237
|
+
from the files, it may remove all the `work` fetched from Musicbrainz and only
|
|
238
|
+
stored in the DB.
|
|
239
|
+
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Multi-value Plugin
|
|
2
|
+
|
|
3
|
+
Multi-values on a tag is useful to provide different and independent information
|
|
4
|
+
belonging to the same context.
|
|
5
|
+
|
|
6
|
+
This plugin provides a boosted modify command to also add/remove values in
|
|
7
|
+
multi-value tag. It can also fix `grouping` field and add `work` field.
|
|
8
|
+
|
|
9
|
+
## Beet official list tags
|
|
10
|
+
|
|
11
|
+
A few fields already support native multi-values separated with `\␀`. Those are
|
|
12
|
+
always available in the command without a configuration change. The current
|
|
13
|
+
fields are:
|
|
14
|
+
|
|
15
|
+
- artists
|
|
16
|
+
- albumartists
|
|
17
|
+
- artists_sort
|
|
18
|
+
- artists_credit
|
|
19
|
+
- albumartists_sort
|
|
20
|
+
- albumartists_credit
|
|
21
|
+
- mb_artistids
|
|
22
|
+
- mb_albumartistids
|
|
23
|
+
|
|
24
|
+
## String multi-value tags
|
|
25
|
+
|
|
26
|
+
Some fields do not support multi-values yet like
|
|
27
|
+
[genre](https://github.com/beetbox/beets/pull/5426) or some may never support it
|
|
28
|
+
as not a "standard" like `grouping`.
|
|
29
|
+
|
|
30
|
+
An example could be to add multiple genres separated by a comma: `Rock,Hard Rock`
|
|
31
|
+
|
|
32
|
+
Some external tools also supports custom separator splitting like
|
|
33
|
+
[navidrome](https://www.navidrome.org/docs/usage/customtags/#changing-separators)
|
|
34
|
+
since v0.55.0.
|
|
35
|
+
|
|
36
|
+
By default, no field is considered a string multi-value. Each one must be
|
|
37
|
+
explicitly defined in the configuration with the expected separator:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
multivalue:
|
|
41
|
+
string_fields:
|
|
42
|
+
grouping: ";"
|
|
43
|
+
genre: ","
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Multi Modify Command
|
|
47
|
+
|
|
48
|
+
### Usage
|
|
49
|
+
|
|
50
|
+
```shell
|
|
51
|
+
# Initial: genre: Rock
|
|
52
|
+
|
|
53
|
+
# Add a value
|
|
54
|
+
beet multimodify grouping+="Hard Rock" <query>
|
|
55
|
+
# genre: Rock,Hard Rock
|
|
56
|
+
|
|
57
|
+
# Add and remove values
|
|
58
|
+
beet mmod genre+="Classic Rock" genre-="Hard Rock" <query>
|
|
59
|
+
# genre: Rock,Classic Rock
|
|
60
|
+
|
|
61
|
+
# Original modify command still applies
|
|
62
|
+
beet mmod genre+="Classic Rock" genre-="Hard Rock" year! title="Best song"
|
|
63
|
+
|
|
64
|
+
# Adding the same value is detected and avoided. By default, exact match is applied.
|
|
65
|
+
# It is easy to remember, there is the "=", the same used in a regular query.
|
|
66
|
+
# Initial: genre: Rock
|
|
67
|
+
beet multimodify grouping+="Rock" <query>
|
|
68
|
+
# genre: Rock
|
|
69
|
+
|
|
70
|
+
# For case insensitivity, add "~" as in a regular query.
|
|
71
|
+
beet multimodify grouping+=~rock <query>
|
|
72
|
+
# genre: Rock
|
|
73
|
+
|
|
74
|
+
# It is also possible to use the ones from a plugin by adding the equivalent prefix.
|
|
75
|
+
# For example with bareasc set to the prefix "#".
|
|
76
|
+
# Initial: artists: [Eric]
|
|
77
|
+
beet multimodify artists+=#Éric <query>
|
|
78
|
+
# no change
|
|
79
|
+
|
|
80
|
+
# The same work for removing
|
|
81
|
+
|
|
82
|
+
# Initial: genre: Rock
|
|
83
|
+
beet multimodify grouping-="Blues" <query>
|
|
84
|
+
# No change
|
|
85
|
+
|
|
86
|
+
# For case insensitivity, add "~" as in a regular query
|
|
87
|
+
# Initial: genre: Rock
|
|
88
|
+
beet multimodify grouping-=~rock <query>
|
|
89
|
+
# genre: ""
|
|
90
|
+
|
|
91
|
+
# For bareasc set to the prefix "#"
|
|
92
|
+
# Initial: artists: [Éric]
|
|
93
|
+
beet multimodify artists-=#Eric <query>
|
|
94
|
+
# artists: []
|
|
95
|
+
|
|
96
|
+
# Removing is also supporting Regex
|
|
97
|
+
# Initial: artists: [Eric]
|
|
98
|
+
beet multimodify artists-=:E?ic <query>
|
|
99
|
+
# artists: []
|
|
100
|
+
|
|
101
|
+
# Adding can not support regex as else the regex itself would be added.
|
|
102
|
+
# If you want to harmonize the data, you may remove and add at the same time.
|
|
103
|
+
# Initial: genre: Rock&Roll
|
|
104
|
+
beet multimodify 'genre-=:Rock.+' genre+=Rock <query>
|
|
105
|
+
# genre: Rock
|
|
106
|
+
|
|
107
|
+
# Order of execution
|
|
108
|
+
# genre: original
|
|
109
|
+
beet mm genre+=base genre-=base genre=base,pivot
|
|
110
|
+
# genre: pivot,base
|
|
111
|
+
|
|
112
|
+
# Deletion always win
|
|
113
|
+
beet mm genre! genre+=base genre-=base genre=base,pivot
|
|
114
|
+
# genre:
|
|
115
|
+
|
|
116
|
+
# Reset field first
|
|
117
|
+
beet mm genre= genre+=new genre-=new2
|
|
118
|
+
# genre: new,new2
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The command is influenced by the `modify` one and provides the same flags. By
|
|
122
|
+
default, a confirmation after showing the diff is requested and highly
|
|
123
|
+
recommended to avoid any data loss.
|
|
124
|
+
|
|
125
|
+
### Order of execution
|
|
126
|
+
|
|
127
|
+
1. Direct assignment are always run first: `genre=Rock`. If multiple assignments
|
|
128
|
+
on the same field are written, the last one win.
|
|
129
|
+
|
|
130
|
+
2. Values are removed: `genre-=Rock`. It allows to do some pre-cleaning before
|
|
131
|
+
adding values in the same run. Always with capital R: `genre-=rock
|
|
132
|
+
genre+=Rock`. Values are removed in order from the CLI `genre-=Rock
|
|
133
|
+
genre-=Classic`: first `Rock` then `Classic`.
|
|
134
|
+
|
|
135
|
+
3. Values are added in order from the CLI `genre+=Rock genre+=Classic`: first
|
|
136
|
+
`Rock` then `Classic`.
|
|
137
|
+
|
|
138
|
+
The order above is not impacted if the CLI options are unordered: `genre+=Rock
|
|
139
|
+
genre=Blues` would still do the assignment first.
|
|
140
|
+
|
|
141
|
+
The deletion `genre!` is always run last whatever its position. It keeps the
|
|
142
|
+
compatibility with the actual `modify` command behavior. To reset a field before
|
|
143
|
+
adding values, one must use `genre= genre+=Rock` or `artists= artists+=Eric`.
|
|
144
|
+
|
|
145
|
+
### Performance
|
|
146
|
+
|
|
147
|
+
To optimize performance and avoid iterating over a lot of data, the query should
|
|
148
|
+
prune items as much as possible.
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
# Only iterate over items without the Rock word in genre
|
|
152
|
+
beet multimodify genre+=Rock '^genre:Rock'
|
|
153
|
+
|
|
154
|
+
# Only iterate over items with the Rock word in genre
|
|
155
|
+
beet multimodify genre-=Rock 'genre:Rock'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Limitation
|
|
159
|
+
|
|
160
|
+
A/ Sub-Optimal Diff
|
|
161
|
+
|
|
162
|
+
The diff may be sub-optimal as it does not know about the separator. In the
|
|
163
|
+
following example "Classic Rock" is not highlighted continuously although the
|
|
164
|
+
final change is still accurate: `genre: Hard Rock,**Classic** Rock,**Rock** ->
|
|
165
|
+
Hard Rock,Rock`.
|
|
166
|
+
|
|
167
|
+
B/ Relation to the original modify command
|
|
168
|
+
|
|
169
|
+
The modify command has been "copied" and upstream changes won't apply without a
|
|
170
|
+
port in this plugin.
|
|
171
|
+
|
|
172
|
+
C/ No order support
|
|
173
|
+
|
|
174
|
+
I have no need for ordering the values in the tag, as a result, it is always
|
|
175
|
+
added last.
|
|
176
|
+
|
|
177
|
+
## Grouping/Work fields
|
|
178
|
+
|
|
179
|
+
`mediafile` the file level library is using the wrong tag name for MP3 (see
|
|
180
|
+
[issue](https://github.com/beetbox/mediafile/issues/15)). As the `grouping`
|
|
181
|
+
field was my first motivation to use string multi-value, this plugin is also
|
|
182
|
+
able to fix the field.
|
|
183
|
+
|
|
184
|
+
The changes are:
|
|
185
|
+
- `grouping`: for MP3 is changed to `GRP1` and ASF does not support it as
|
|
186
|
+
without a defined value
|
|
187
|
+
- `work` field is added for MP3 (`TIT1`), MP4 (`@wrk`), Vorbis (`WORK`) and ASF
|
|
188
|
+
(`WM/ContentGroupDescription`). The values for MP3 and ASF were previously
|
|
189
|
+
assigned to `grouping`.
|
|
190
|
+
|
|
191
|
+
The tags are chosen from [Kid3
|
|
192
|
+
table](https://kid3.sourceforge.io/kid3_en.html#frame-list).
|
|
193
|
+
|
|
194
|
+
It is disabled by default so to enable it:
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
multivalue:
|
|
198
|
+
fix_media_fields: true
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
It is required to make beets read the tags from the file again as else the kept
|
|
202
|
+
values in DB are the old ones from the potential wrong fields:
|
|
203
|
+
|
|
204
|
+
```shell
|
|
205
|
+
beet update -F work -F grouping
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
WARNING: As the `work` field was not saved to the file previously, by reading
|
|
209
|
+
from the files, it may remove all the `work` fetched from Musicbrainz and only
|
|
210
|
+
stored in the DB.
|
|
211
|
+
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: beets-multivalue
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A beet plugin to manage multi-value fields
|
|
5
|
+
Home-page: https://github.com/Morikko/beets-multivalue
|
|
6
|
+
Author: Eric Masseran
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Programming Language :: Python
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: beets>2
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: description
|
|
22
|
+
Dynamic: description-content-type
|
|
23
|
+
Dynamic: home-page
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: summary
|
|
28
|
+
|
|
29
|
+
# Multi-value Plugin
|
|
30
|
+
|
|
31
|
+
Multi-values on a tag is useful to provide different and independent information
|
|
32
|
+
belonging to the same context.
|
|
33
|
+
|
|
34
|
+
This plugin provides a boosted modify command to also add/remove values in
|
|
35
|
+
multi-value tag. It can also fix `grouping` field and add `work` field.
|
|
36
|
+
|
|
37
|
+
## Beet official list tags
|
|
38
|
+
|
|
39
|
+
A few fields already support native multi-values separated with `\␀`. Those are
|
|
40
|
+
always available in the command without a configuration change. The current
|
|
41
|
+
fields are:
|
|
42
|
+
|
|
43
|
+
- artists
|
|
44
|
+
- albumartists
|
|
45
|
+
- artists_sort
|
|
46
|
+
- artists_credit
|
|
47
|
+
- albumartists_sort
|
|
48
|
+
- albumartists_credit
|
|
49
|
+
- mb_artistids
|
|
50
|
+
- mb_albumartistids
|
|
51
|
+
|
|
52
|
+
## String multi-value tags
|
|
53
|
+
|
|
54
|
+
Some fields do not support multi-values yet like
|
|
55
|
+
[genre](https://github.com/beetbox/beets/pull/5426) or some may never support it
|
|
56
|
+
as not a "standard" like `grouping`.
|
|
57
|
+
|
|
58
|
+
An example could be to add multiple genres separated by a comma: `Rock,Hard Rock`
|
|
59
|
+
|
|
60
|
+
Some external tools also supports custom separator splitting like
|
|
61
|
+
[navidrome](https://www.navidrome.org/docs/usage/customtags/#changing-separators)
|
|
62
|
+
since v0.55.0.
|
|
63
|
+
|
|
64
|
+
By default, no field is considered a string multi-value. Each one must be
|
|
65
|
+
explicitly defined in the configuration with the expected separator:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
multivalue:
|
|
69
|
+
string_fields:
|
|
70
|
+
grouping: ";"
|
|
71
|
+
genre: ","
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Multi Modify Command
|
|
75
|
+
|
|
76
|
+
### Usage
|
|
77
|
+
|
|
78
|
+
```shell
|
|
79
|
+
# Initial: genre: Rock
|
|
80
|
+
|
|
81
|
+
# Add a value
|
|
82
|
+
beet multimodify grouping+="Hard Rock" <query>
|
|
83
|
+
# genre: Rock,Hard Rock
|
|
84
|
+
|
|
85
|
+
# Add and remove values
|
|
86
|
+
beet mmod genre+="Classic Rock" genre-="Hard Rock" <query>
|
|
87
|
+
# genre: Rock,Classic Rock
|
|
88
|
+
|
|
89
|
+
# Original modify command still applies
|
|
90
|
+
beet mmod genre+="Classic Rock" genre-="Hard Rock" year! title="Best song"
|
|
91
|
+
|
|
92
|
+
# Adding the same value is detected and avoided. By default, exact match is applied.
|
|
93
|
+
# It is easy to remember, there is the "=", the same used in a regular query.
|
|
94
|
+
# Initial: genre: Rock
|
|
95
|
+
beet multimodify grouping+="Rock" <query>
|
|
96
|
+
# genre: Rock
|
|
97
|
+
|
|
98
|
+
# For case insensitivity, add "~" as in a regular query.
|
|
99
|
+
beet multimodify grouping+=~rock <query>
|
|
100
|
+
# genre: Rock
|
|
101
|
+
|
|
102
|
+
# It is also possible to use the ones from a plugin by adding the equivalent prefix.
|
|
103
|
+
# For example with bareasc set to the prefix "#".
|
|
104
|
+
# Initial: artists: [Eric]
|
|
105
|
+
beet multimodify artists+=#Éric <query>
|
|
106
|
+
# no change
|
|
107
|
+
|
|
108
|
+
# The same work for removing
|
|
109
|
+
|
|
110
|
+
# Initial: genre: Rock
|
|
111
|
+
beet multimodify grouping-="Blues" <query>
|
|
112
|
+
# No change
|
|
113
|
+
|
|
114
|
+
# For case insensitivity, add "~" as in a regular query
|
|
115
|
+
# Initial: genre: Rock
|
|
116
|
+
beet multimodify grouping-=~rock <query>
|
|
117
|
+
# genre: ""
|
|
118
|
+
|
|
119
|
+
# For bareasc set to the prefix "#"
|
|
120
|
+
# Initial: artists: [Éric]
|
|
121
|
+
beet multimodify artists-=#Eric <query>
|
|
122
|
+
# artists: []
|
|
123
|
+
|
|
124
|
+
# Removing is also supporting Regex
|
|
125
|
+
# Initial: artists: [Eric]
|
|
126
|
+
beet multimodify artists-=:E?ic <query>
|
|
127
|
+
# artists: []
|
|
128
|
+
|
|
129
|
+
# Adding can not support regex as else the regex itself would be added.
|
|
130
|
+
# If you want to harmonize the data, you may remove and add at the same time.
|
|
131
|
+
# Initial: genre: Rock&Roll
|
|
132
|
+
beet multimodify 'genre-=:Rock.+' genre+=Rock <query>
|
|
133
|
+
# genre: Rock
|
|
134
|
+
|
|
135
|
+
# Order of execution
|
|
136
|
+
# genre: original
|
|
137
|
+
beet mm genre+=base genre-=base genre=base,pivot
|
|
138
|
+
# genre: pivot,base
|
|
139
|
+
|
|
140
|
+
# Deletion always win
|
|
141
|
+
beet mm genre! genre+=base genre-=base genre=base,pivot
|
|
142
|
+
# genre:
|
|
143
|
+
|
|
144
|
+
# Reset field first
|
|
145
|
+
beet mm genre= genre+=new genre-=new2
|
|
146
|
+
# genre: new,new2
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The command is influenced by the `modify` one and provides the same flags. By
|
|
150
|
+
default, a confirmation after showing the diff is requested and highly
|
|
151
|
+
recommended to avoid any data loss.
|
|
152
|
+
|
|
153
|
+
### Order of execution
|
|
154
|
+
|
|
155
|
+
1. Direct assignment are always run first: `genre=Rock`. If multiple assignments
|
|
156
|
+
on the same field are written, the last one win.
|
|
157
|
+
|
|
158
|
+
2. Values are removed: `genre-=Rock`. It allows to do some pre-cleaning before
|
|
159
|
+
adding values in the same run. Always with capital R: `genre-=rock
|
|
160
|
+
genre+=Rock`. Values are removed in order from the CLI `genre-=Rock
|
|
161
|
+
genre-=Classic`: first `Rock` then `Classic`.
|
|
162
|
+
|
|
163
|
+
3. Values are added in order from the CLI `genre+=Rock genre+=Classic`: first
|
|
164
|
+
`Rock` then `Classic`.
|
|
165
|
+
|
|
166
|
+
The order above is not impacted if the CLI options are unordered: `genre+=Rock
|
|
167
|
+
genre=Blues` would still do the assignment first.
|
|
168
|
+
|
|
169
|
+
The deletion `genre!` is always run last whatever its position. It keeps the
|
|
170
|
+
compatibility with the actual `modify` command behavior. To reset a field before
|
|
171
|
+
adding values, one must use `genre= genre+=Rock` or `artists= artists+=Eric`.
|
|
172
|
+
|
|
173
|
+
### Performance
|
|
174
|
+
|
|
175
|
+
To optimize performance and avoid iterating over a lot of data, the query should
|
|
176
|
+
prune items as much as possible.
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
# Only iterate over items without the Rock word in genre
|
|
180
|
+
beet multimodify genre+=Rock '^genre:Rock'
|
|
181
|
+
|
|
182
|
+
# Only iterate over items with the Rock word in genre
|
|
183
|
+
beet multimodify genre-=Rock 'genre:Rock'
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Limitation
|
|
187
|
+
|
|
188
|
+
A/ Sub-Optimal Diff
|
|
189
|
+
|
|
190
|
+
The diff may be sub-optimal as it does not know about the separator. In the
|
|
191
|
+
following example "Classic Rock" is not highlighted continuously although the
|
|
192
|
+
final change is still accurate: `genre: Hard Rock,**Classic** Rock,**Rock** ->
|
|
193
|
+
Hard Rock,Rock`.
|
|
194
|
+
|
|
195
|
+
B/ Relation to the original modify command
|
|
196
|
+
|
|
197
|
+
The modify command has been "copied" and upstream changes won't apply without a
|
|
198
|
+
port in this plugin.
|
|
199
|
+
|
|
200
|
+
C/ No order support
|
|
201
|
+
|
|
202
|
+
I have no need for ordering the values in the tag, as a result, it is always
|
|
203
|
+
added last.
|
|
204
|
+
|
|
205
|
+
## Grouping/Work fields
|
|
206
|
+
|
|
207
|
+
`mediafile` the file level library is using the wrong tag name for MP3 (see
|
|
208
|
+
[issue](https://github.com/beetbox/mediafile/issues/15)). As the `grouping`
|
|
209
|
+
field was my first motivation to use string multi-value, this plugin is also
|
|
210
|
+
able to fix the field.
|
|
211
|
+
|
|
212
|
+
The changes are:
|
|
213
|
+
- `grouping`: for MP3 is changed to `GRP1` and ASF does not support it as
|
|
214
|
+
without a defined value
|
|
215
|
+
- `work` field is added for MP3 (`TIT1`), MP4 (`@wrk`), Vorbis (`WORK`) and ASF
|
|
216
|
+
(`WM/ContentGroupDescription`). The values for MP3 and ASF were previously
|
|
217
|
+
assigned to `grouping`.
|
|
218
|
+
|
|
219
|
+
The tags are chosen from [Kid3
|
|
220
|
+
table](https://kid3.sourceforge.io/kid3_en.html#frame-list).
|
|
221
|
+
|
|
222
|
+
It is disabled by default so to enable it:
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
multivalue:
|
|
226
|
+
fix_media_fields: true
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
It is required to make beets read the tags from the file again as else the kept
|
|
230
|
+
values in DB are the old ones from the potential wrong fields:
|
|
231
|
+
|
|
232
|
+
```shell
|
|
233
|
+
beet update -F work -F grouping
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
WARNING: As the `work` field was not saved to the file previously, by reading
|
|
237
|
+
from the files, it may remove all the `work` fetched from Musicbrainz and only
|
|
238
|
+
stored in the DB.
|
|
239
|
+
|