1414
1515"""Facilities for automatically determining files' correct metadata."""
1616
17- from __future__ import annotations
18-
1917import warnings
2018from importlib import import_module
21- from typing import TYPE_CHECKING
22-
23- from beets import config , logging
24-
25- # Parts of external interface.
26- from beets .util import unique_list
2719
2820from ..util import deprecate_imports
2921from .hooks import AlbumInfo , AlbumMatch , TrackInfo , TrackMatch
3022from .match import Proposal , Recommendation , tag_album , tag_item
3123
32- if TYPE_CHECKING :
33- from collections .abc import Sequence
34-
35- from beets .library import Album , Item , LibModel
36-
3724
3825def __getattr__ (name : str ):
3926 if name == "current_metadata" :
@@ -59,282 +46,6 @@ def __getattr__(name: str):
5946 "Recommendation" ,
6047 "TrackInfo" ,
6148 "TrackMatch" ,
62- "apply_album_metadata" ,
63- "apply_item_metadata" ,
64- "apply_metadata" ,
6549 "tag_album" ,
6650 "tag_item" ,
6751]
68-
69- # Global logger.
70- log = logging .getLogger ("beets" )
71-
72- # Metadata fields that are already hardcoded, or where the tag name changes.
73- SPECIAL_FIELDS = {
74- "album" : (
75- "va" ,
76- "releasegroup_id" ,
77- "artist_id" ,
78- "artists_ids" ,
79- "album_id" ,
80- "mediums" ,
81- "tracks" ,
82- "year" ,
83- "month" ,
84- "day" ,
85- "artist" ,
86- "artists" ,
87- "artist_credit" ,
88- "artists_credit" ,
89- "artist_sort" ,
90- "artists_sort" ,
91- "data_url" ,
92- ),
93- "track" : (
94- "track_alt" ,
95- "artist_id" ,
96- "artists_ids" ,
97- "release_track_id" ,
98- "medium" ,
99- "index" ,
100- "medium_index" ,
101- "title" ,
102- "artist_credit" ,
103- "artists_credit" ,
104- "artist_sort" ,
105- "artists_sort" ,
106- "artist" ,
107- "artists" ,
108- "track_id" ,
109- "medium_total" ,
110- "data_url" ,
111- "length" ,
112- ),
113- }
114-
115-
116- # Additional utilities for the main interface.
117-
118-
119- def _apply_metadata (
120- info : AlbumInfo | TrackInfo ,
121- db_obj : Album | Item ,
122- null_fields : bool = True ,
123- ):
124- """Set the db_obj's metadata to match the info."""
125- key = "album" if isinstance (info , AlbumInfo ) else "track"
126- special_fields = set (SPECIAL_FIELDS [key ])
127- nullable_fields = set (config ["overwrite_null" ][key ].as_str_seq ())
128-
129- for field , value in info .items ():
130- # We only overwrite fields that are not already hardcoded.
131- if field in special_fields :
132- continue
133-
134- # Don't overwrite fields with empty values unless the
135- # field is explicitly allowed to be overwritten.
136- if null_fields and value is None and field not in nullable_fields :
137- continue
138-
139- db_obj [field ] = value
140-
141-
142- def correct_list_fields (m : LibModel ) -> None :
143- """Synchronise single and list values for the list fields that we use.
144-
145- That is, ensure the same value in the single field and the first element
146- in the list.
147-
148- For context, the value we set as, say, ``mb_artistid`` is simply ignored:
149- Under the current :class:`MediaFile` implementation, fields ``albumtype``,
150- ``mb_artistid`` and ``mb_albumartistid`` are mapped to the first element of
151- ``albumtypes``, ``mb_artistids`` and ``mb_albumartistids`` respectively.
152-
153- This means setting ``mb_artistid`` has no effect. However, beets
154- functionality still assumes that ``mb_artistid`` is independent and stores
155- its value in the database. If ``mb_artistid`` != ``mb_artistids[0]``,
156- ``beet write`` command thinks that ``mb_artistid`` is modified and tries to
157- update the field in the file. Of course nothing happens, so the same diff
158- is shown every time the command is run.
159-
160- We can avoid this issue by ensuring that ``mb_artistid`` has the same value
161- as ``mb_artistids[0]``, and that's what this function does.
162-
163- Note: :class:`Album` model does not have ``mb_artistids`` and
164- ``mb_albumartistids`` fields therefore we need to check for their presence.
165- """
166-
167- def ensure_first_value (single_field : str , list_field : str ) -> None :
168- """Ensure the first ``list_field`` item is equal to ``single_field``."""
169- single_val , list_val = getattr (m , single_field ), getattr (m , list_field )
170- if single_val :
171- setattr (m , list_field , unique_list ([single_val , * list_val ]))
172- elif list_val :
173- setattr (m , single_field , list_val [0 ])
174-
175- ensure_first_value ("albumtype" , "albumtypes" )
176-
177- if hasattr (m , "mb_artistids" ):
178- ensure_first_value ("mb_artistid" , "mb_artistids" )
179-
180- if hasattr (m , "mb_albumartistids" ):
181- ensure_first_value ("mb_albumartistid" , "mb_albumartistids" )
182-
183- if hasattr (m , "artists_sort" ):
184- ensure_first_value ("artist_sort" , "artists_sort" )
185-
186- if hasattr (m , "artists_credit" ):
187- ensure_first_value ("artist_credit" , "artists_credit" )
188-
189- if hasattr (m , "albumartists_credit" ):
190- ensure_first_value ("albumartist_credit" , "albumartists_credit" )
191-
192- if hasattr (m , "artists" ):
193- ensure_first_value ("artist" , "artists" )
194-
195- if hasattr (m , "albumartists_sort" ):
196- ensure_first_value ("albumartist_sort" , "albumartists_sort" )
197-
198-
199- def apply_item_metadata (item : Item , track_info : TrackInfo ):
200- """Set an item's metadata from its matched TrackInfo object."""
201- item .artist = track_info .artist
202- item .artists = track_info .artists
203- item .artist_sort = track_info .artist_sort
204- item .artists_sort = track_info .artists_sort
205- item .artist_credit = track_info .artist_credit
206- item .artists_credit = track_info .artists_credit
207- item .title = track_info .title
208- item .mb_trackid = track_info .track_id
209- item .mb_releasetrackid = track_info .release_track_id
210- if track_info .artist_id :
211- item .mb_artistid = track_info .artist_id
212- if track_info .artists_ids :
213- item .mb_artistids = track_info .artists_ids
214-
215- _apply_metadata (track_info , item )
216- correct_list_fields (item )
217-
218- # At the moment, the other metadata is left intact (including album
219- # and track number). Perhaps these should be emptied?
220-
221-
222- def apply_album_metadata (album_info : AlbumInfo , album : Album ):
223- """Set the album's metadata to match the AlbumInfo object."""
224- _apply_metadata (album_info , album , null_fields = False )
225- correct_list_fields (album )
226-
227-
228- def apply_metadata (
229- album_info : AlbumInfo , item_info_pairs : list [tuple [Item , TrackInfo ]]
230- ):
231- """Set items metadata to match corresponding tagged info."""
232- for item , track_info in item_info_pairs :
233- # Artist or artist credit.
234- if config ["artist_credit" ]:
235- item .artist = (
236- track_info .artist_credit
237- or track_info .artist
238- or album_info .artist_credit
239- or album_info .artist
240- )
241- item .artists = (
242- track_info .artists_credit
243- or track_info .artists
244- or album_info .artists_credit
245- or album_info .artists
246- )
247- item .albumartist = album_info .artist_credit or album_info .artist
248- item .albumartists = album_info .artists_credit or album_info .artists
249- else :
250- item .artist = track_info .artist or album_info .artist
251- item .artists = track_info .artists or album_info .artists
252- item .albumartist = album_info .artist
253- item .albumartists = album_info .artists
254-
255- # Album.
256- item .album = album_info .album
257-
258- # Artist sort and credit names.
259- item .artist_sort = track_info .artist_sort or album_info .artist_sort
260- item .artists_sort = track_info .artists_sort or album_info .artists_sort
261- item .artist_credit = (
262- track_info .artist_credit or album_info .artist_credit
263- )
264- item .artists_credit = (
265- track_info .artists_credit or album_info .artists_credit
266- )
267- item .albumartist_sort = album_info .artist_sort
268- item .albumartists_sort = album_info .artists_sort
269- item .albumartist_credit = album_info .artist_credit
270- item .albumartists_credit = album_info .artists_credit
271-
272- # Release date.
273- for prefix in "" , "original_" :
274- if config ["original_date" ] and not prefix :
275- # Ignore specific release date.
276- continue
277-
278- for suffix in "year" , "month" , "day" :
279- key = f"{ prefix } { suffix } "
280- value = getattr (album_info , key ) or 0
281-
282- # If we don't even have a year, apply nothing.
283- if suffix == "year" and not value :
284- break
285-
286- # Otherwise, set the fetched value (or 0 for the month
287- # and day if not available).
288- item [key ] = value
289-
290- # If we're using original release date for both fields,
291- # also set item.year = info.original_year, etc.
292- if config ["original_date" ]:
293- item [suffix ] = value
294-
295- # Title.
296- item .title = track_info .title
297-
298- if config ["per_disc_numbering" ]:
299- # We want to let the track number be zero, but if the medium index
300- # is not provided we need to fall back to the overall index.
301- if track_info .medium_index is not None :
302- item .track = track_info .medium_index
303- else :
304- item .track = track_info .index
305- item .tracktotal = track_info .medium_total or len (album_info .tracks )
306- else :
307- item .track = track_info .index
308- item .tracktotal = len (album_info .tracks )
309-
310- # Disc and disc count.
311- item .disc = track_info .medium
312- item .disctotal = album_info .mediums
313-
314- # MusicBrainz IDs.
315- item .mb_trackid = track_info .track_id
316- item .mb_releasetrackid = track_info .release_track_id or item .mb_trackid
317-
318- item .mb_albumid = album_info .album_id
319- item .mb_releasegroupid = album_info .releasegroup_id
320-
321- item .mb_albumartistid = album_info .artist_id
322- item .mb_albumartistids = album_info .artists_ids or (
323- [ai ] if (ai := item .mb_albumartistid ) else []
324- )
325-
326- item .mb_artistid = track_info .artist_id or item .mb_albumartistid
327- item .mb_artistids = track_info .artists_ids or (
328- [iai ] if (iai := item .mb_artistid ) else []
329- )
330-
331- # Compilation flag.
332- item .comp = album_info .va
333-
334- # Track alt.
335- item .track_alt = track_info .track_alt
336-
337- _apply_metadata (album_info , item )
338- _apply_metadata (track_info , item )
339-
340- correct_list_fields (item )
0 commit comments