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.
@@ -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
+