|
24 | 24 | from beets import config
|
25 | 25 | from beets.autotag import hooks
|
26 | 26 | from beets.dbcore import types
|
27 |
| -from beets.library import Album, Item |
| 27 | +from beets.library import Album, Item, Library |
28 | 28 | from beets.plugins import BeetsPlugin
|
29 | 29 | from beets.ui import Subcommand, decargs, print_
|
30 | 30 |
|
| 31 | +MB_ARTIST_QUERY = r"mb_albumartistid::^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$" |
| 32 | + |
31 | 33 |
|
32 | 34 | def _missing_count(album):
|
33 | 35 | """Return number of missing items in `album`."""
|
@@ -166,65 +168,51 @@ def _missing_tracks(self, lib, query):
|
166 | 168 | for item in self._missing(album):
|
167 | 169 | print_(format(item, fmt))
|
168 | 170 |
|
169 |
| - def _missing_albums(self, lib, query): |
| 171 | + def _missing_albums(self, lib: Library, query: list[str]) -> None: |
170 | 172 | """Print a listing of albums missing from each artist in the library
|
171 | 173 | matching query.
|
172 | 174 | """
|
173 |
| - total = self.config["total"].get() |
174 |
| - |
175 |
| - albums = lib.albums(query) |
176 |
| - # build dict mapping artist to list of their albums in library |
177 |
| - albums_by_artist = defaultdict(list) |
178 |
| - for alb in albums: |
179 |
| - artist = (alb["albumartist"], alb["mb_albumartistid"]) |
180 |
| - albums_by_artist[artist].append(alb) |
| 175 | + query.append(MB_ARTIST_QUERY) |
| 176 | + |
| 177 | + # build dict mapping artist to set of their album ids in library |
| 178 | + album_ids_by_artist = defaultdict(set) |
| 179 | + for album in lib.albums(query): |
| 180 | + # TODO(@snejus): Some releases have different `albumartist` for the |
| 181 | + # same `mb_albumartistid`. Since we're grouping by the combination |
| 182 | + # of these two fields, we end up processing the same |
| 183 | + # `mb_albumartistid` multiple times: calling MusicBrainz API and |
| 184 | + # reporting the same set of missing albums. Instead, we should |
| 185 | + # group by `mb_albumartistid` field only. |
| 186 | + artist = (album["albumartist"], album["mb_albumartistid"]) |
| 187 | + album_ids_by_artist[artist].add(album) |
181 | 188 |
|
182 | 189 | total_missing = 0
|
183 |
| - |
184 |
| - # build dict mapping artist to list of all albums |
185 |
| - for artist, albums in albums_by_artist.items(): |
186 |
| - if artist[1] is None or artist[1] == "": |
187 |
| - albs_no_mbid = ["'" + a["album"] + "'" for a in albums] |
188 |
| - self._log.info( |
189 |
| - "No musicbrainz ID for artist '{}' found in album(s) {}; " |
190 |
| - "skipping", |
191 |
| - artist[0], |
192 |
| - ", ".join(albs_no_mbid), |
193 |
| - ) |
194 |
| - continue |
195 |
| - |
| 190 | + calculating_total = self.config["total"].get() |
| 191 | + for (artist, artist_id), album_ids in album_ids_by_artist.items(): |
196 | 192 | try:
|
197 |
| - resp = musicbrainzngs.browse_release_groups(artist=artist[1]) |
198 |
| - release_groups = resp["release-group-list"] |
| 193 | + resp = musicbrainzngs.browse_release_groups(artist=artist_id) |
199 | 194 | except MusicBrainzError as err:
|
200 | 195 | self._log.info(
|
201 | 196 | "Couldn't fetch info for artist '{}' ({}) - '{}'",
|
202 |
| - artist[0], |
203 |
| - artist[1], |
| 197 | + artist, |
| 198 | + artist_id, |
204 | 199 | err,
|
205 | 200 | )
|
206 | 201 | continue
|
207 | 202 |
|
208 |
| - missing = [] |
209 |
| - present = [] |
210 |
| - for rg in release_groups: |
211 |
| - missing.append(rg) |
212 |
| - for alb in albums: |
213 |
| - if alb["mb_releasegroupid"] == rg["id"]: |
214 |
| - missing.remove(rg) |
215 |
| - present.append(rg) |
216 |
| - break |
217 |
| - |
218 |
| - total_missing += len(missing) |
219 |
| - if total: |
220 |
| - continue |
221 |
| - |
222 |
| - missing_titles = {rg["title"] for rg in missing} |
| 203 | + missing_titles = [ |
| 204 | + f"{artist} - {rg['title']}" |
| 205 | + for rg in resp["release-group-list"] |
| 206 | + if rg["id"] not in album_ids |
| 207 | + ] |
223 | 208 |
|
224 |
| - for release_title in missing_titles: |
225 |
| - print_("{} - {}".format(artist[0], release_title)) |
| 209 | + if calculating_total: |
| 210 | + total_missing += len(missing_titles) |
| 211 | + else: |
| 212 | + for title in missing_titles: |
| 213 | + print(title) |
226 | 214 |
|
227 |
| - if total: |
| 215 | + if calculating_total: |
228 | 216 | print(total_missing)
|
229 | 217 |
|
230 | 218 | def _missing(self, album: Album) -> Iterator[Item]:
|
|
0 commit comments