123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import logging
- import re
- from typing import Optional
- from music.musicbrainz import (
- lookup_album_dict_from_mb,
- lookup_artist_from_mb,
- lookup_track_from_mb,
- )
- from music.constants import VARIOUS_ARTIST_DICT
- from scrobbles.utils import convert_to_seconds
- logger = logging.getLogger(__name__)
- from music.models import Album, Artist, Track
- def clean_artist_name(name: str) -> str:
- """Remove featured names from artist string."""
- if "feat." in name.lower():
- name = re.split("feat.", name, flags=re.IGNORECASE)[0].strip()
- if "featuring" in name.lower():
- name = re.split("featuring", name, flags=re.IGNORECASE)[0].strip()
- if "&" in name.lower():
- name = re.split("&", name, flags=re.IGNORECASE)[0].strip()
- return name
- # TODO These are depreacted, remove them eventually
- def get_or_create_artist(name: str, mbid: str = "") -> Artist:
- """Get an Artist object from the database.
- Check if an artist with this name or Musicbrainz ID already exists.
- Otherwise, go lookup artist data from Musicbrainz and create one.
- """
- artist = None
- name = clean_artist_name(name)
- # Check for name/mbid combo, just mbid and then just name
- artist = Artist.objects.filter(name=name, mbid=mbid).first()
- if not artist:
- artist = Artist.objects.filter(musicbrainz_id=mbid).first()
- if not artist:
- artist = Artist.objects.filter(name=name).first()
- # Does not exist, look it up from Musicbrainz
- if not artist:
- artist_dict = lookup_artist_from_mb(name)
- mbid = mbid or artist_dict.get("id", "")
- if mbid:
- artist = Artist.objects.filter(musicbrainz_id=mbid).first()
- if not artist:
- artist = Artist.objects.create(name=name, musicbrainz_id=mbid)
- # TODO maybe this should be spun off into an async task?
- artist.fix_metadata()
- return artist
- # TODO These are depreacted, remove them eventually
- def get_or_create_album(
- name: str, artist: Artist, mbid: str = None
- ) -> Optional[Album]:
- album = None
- album_dict = lookup_album_dict_from_mb(name, artist_name=artist.name)
- name = name or album_dict.get("title", None)
- if not name:
- logger.debug(
- f"Cannot get or create album by {artist} with no name ({name})"
- )
- return
- album = Album.objects.filter(
- musicbrainz_id=mbid, name=name, artists__in=[artist]
- ).first()
- if not album:
- mbid_group = album_dict.get("mb_group_id")
- album = Album.objects.filter(
- musicbrainz_releasegroup_id=mbid_group
- ).first()
- if not album and name:
- mbid = mbid or album_dict["mb_id"]
- album, album_created = Album.objects.get_or_create(musicbrainz_id=mbid)
- if album_created:
- album.name = name
- album.year = album_dict["year"]
- album.musicbrainz_releasegroup_id = album_dict["mb_group_id"]
- album.musicbrainz_albumartist_id = artist.musicbrainz_id
- album.save(
- update_fields=[
- "name",
- "musicbrainz_id",
- "year",
- "musicbrainz_releasegroup_id",
- "musicbrainz_albumartist_id",
- ]
- )
- album.artists.add(artist)
- album.fix_album_artist()
- album.fetch_artwork()
- album.scrape_allmusic()
- if not album:
- logger.warn(f"No album found for {name} and {mbid}")
- album.fix_album_artist()
- return album
- # TODO These are depreacted, remove them eventually
- def get_or_create_track(post_data: dict, post_keys: dict) -> Track:
- try:
- track_run_time_seconds = int(
- post_data.get(post_keys.get("RUN_TIME"), 0)
- )
- except ValueError: # Sometimes we get run time as a string like "01:35"
- track_run_time_seconds = convert_to_seconds(
- post_data.get(post_keys.get("RUN_TIME"), 0)
- )
- artist_name = post_data.get(post_keys.get("ARTIST_NAME"), "")
- artist_mb_id = post_data.get(post_keys.get("ARTIST_MB_ID"), "")
- album_title = post_data.get(post_keys.get("ALBUM_NAME"), "")
- album_mb_id = post_data.get(post_keys.get("ALBUM_MB_ID"), "")
- track_title = post_data.get(post_keys.get("TRACK_TITLE"), "")
- track_mb_id = post_data.get(post_keys.get("TRACK_MB_ID"), "")
- artist = Artist.find_or_create(artist_name, artist_mb_id)
- album = None
- # We may get no album ID or title, in which case, skip
- if album_mb_id or album_title:
- album = Album.find_or_create(
- album_title, str(artist.name), album_mb_id
- )
- track = None
- if not track_mb_id and album:
- try:
- track_mb_id = lookup_track_from_mb(
- track_title,
- artist.musicbrainz_id,
- album.musicbrainz_id,
- ).get("id", 0)
- except TypeError:
- pass
- if not track_title and not track_mb_id:
- logger.info(
- "Cannot find track without either title or MB ID",
- extra={"post_data": post_data},
- )
- return
- if track_mb_id:
- track = Track.objects.filter(musicbrainz_id=track_mb_id).first()
- if not track and track_title:
- track = Track.objects.filter(title=track_title, artist=artist).first()
- if not track:
- track = Track.objects.create(
- title=track_title,
- artist=artist,
- album=album,
- musicbrainz_id=track_mb_id,
- run_time_seconds=track_run_time_seconds,
- )
- return track
- def get_or_create_various_artists() -> Artist:
- artist = Artist.objects.filter(name="Various Artists").first()
- if not artist:
- artist = Artist.objects.create(**VARIOUS_ARTIST_DICT)
- logger.info("Created Various Artists placeholder")
- return artist
|