[music] Make artists_m2m field source of artist truth (05a24455)
All checks were successful
build & deploy / test (push) Successful in 2m0s
build & deploy / build-and-deploy (push) Successful in 32s

This commit is contained in:
2026-05-29 08:26:30 -04:00
parent 22956c7c7f
commit 1928acf8a6
6 changed files with 76 additions and 17 deletions

View File

@ -93,7 +93,7 @@ fetching and simple saving.
:LOGBOOK:
CLOCK: [2025-07-09 Wed 09:55]--[2025-07-09 Wed 10:15] => 0:20
:END:
* Backlog [32/47] :vrobbler:project:personal:
* Backlog [33/49] :vrobbler:project:personal:
** TODO [#C] Add sentiment parsing for Scrobbles with notes :vrobbler:project:scrobbles:sentiment:
:PROPERTIES:
:ID: 37781d6a-f3b0-48b2-bf98-33c2c791cf85
@ -400,6 +400,20 @@ CLOCK: [2025-07-09 Wed 09:55]--[2025-07-09 Wed 10:15] => 0:20
:PROPERTIES:
:ID: 133bcf71-078f-4efa-a029-1eae4b4d146d
:END:
** TODO [#C] Fix exporting so it works reliably :exporting:project:feature:
*** Description
The existing export function is very naieve. It runs in the web process, takes
too long and just dumps tracks. We should make it more robust by creating one
CSV file per scrobble media type and writing them into a zip file that gets
placed in the media directory:
`/media/exports/user_<user_id>/<timestamp>-export.zip`
And this should all be done in a celery task that is just kicked off by the
"Export" button on the frontend
** TODO [#B] Add importer class for IMAP imports :vrobbler:feature:imap:importers:project:personal:
:PROPERTIES:
:ID: b1426d92-2feb-4d15-9738-d5b7b0594f96
@ -475,6 +489,18 @@ to GMT to save it in the database.
It would be nice to not duplicate comments that exist on a task when it's first scrobbled.
** DONE [#B] Make artists_m2m field source of artist truth for albums :music:bug:albums:
:PROPERTIES:
:ID: 05a24455-0e71-45ef-ac6e-c1b7b843047d
:END:
*** Description
Albums have an FK for album_artist, but like artists, the M2M should be the
source of truth. We should migrate all uses of album_artist to an `artist`
property on the Album model and use a data migration to populate artists with
the album_artist value.
** DONE [#A] Fix various artist album problem with Superwolves (track with multiple artists) :vrobbler:project:music:bug:artists:
:PROPERTIES:
:ID: 590bc038-745f-710b-8272-4d8a3d2efa01

View File

@ -9,7 +9,7 @@ class AlbumAdmin(admin.ModelAdmin):
list_display = (
"name",
"year",
"album_artist",
"artist",
"theaudiodb_genre",
"theaudiodb_mood",
"musicbrainz_id",

View File

@ -0,0 +1,29 @@
from django.db import migrations
def backfill_album_artist_to_artists(apps, schema_editor):
Album = apps.get_model("music", "Album")
AlbumArtist = Album.artists.through
AlbumArtist.objects.bulk_create(
[
AlbumArtist(album_id=r["id"], artist_id=r["album_artist_id"])
for r in Album.objects.filter(album_artist__isnull=False).values(
"id", "album_artist_id"
)
],
ignore_conflicts=True,
)
class Migration(migrations.Migration):
dependencies = [
("music", "0033_backfill_track_artists"),
]
operations = [
migrations.RunPython(
backfill_album_artist_to_artists,
reverse_code=migrations.RunPython.noop,
),
]

View File

@ -294,7 +294,7 @@ class Album(TimeStampedModel):
alt_names = models.TextField(**BNULL)
def __str__(self) -> str:
return "{} by {}".format(self.name, self.album_artist)
return "{} by {}".format(self.name, self.artist)
def get_absolute_url(self):
return reverse("music:album_detail", kwargs={"slug": self.uuid})
@ -320,6 +320,10 @@ class Album(TimeStampedModel):
.order_by("-scrobble_count")
)
@property
def artist(self):
return self.artists.first() or self.album_artist
def fix_album_artist(self):
from music.utils import get_or_create_various_artists
@ -338,10 +342,10 @@ class Album(TimeStampedModel):
)
return
if self.album_artist and (not self.allmusic_id or force):
slug = get_allmusic_slug(self.album_artist.name, self.name)
if self.artist and (not self.allmusic_id or force):
slug = get_allmusic_slug(self.artist.name, self.name)
if not slug:
logger.info(f"No allmsuic link for {self} by {self.album_artist}")
logger.info(f"No allmsuic link for {self} by {self.artist}")
return
self.allmusic_id = slug
self.save(update_fields=["allmusic_id"])
@ -351,7 +355,7 @@ class Album(TimeStampedModel):
allmusic_data = scrape_data_from_allmusic(self.allmusic_link)
if not allmusic_data:
logger.info(f"No allmsuic data for {self} by {self.album_artist}")
logger.info(f"No allmsuic data for {self} by {self.artist}")
return
self.allmusic_review = allmusic_data["review"]
@ -360,8 +364,8 @@ class Album(TimeStampedModel):
def scrape_theaudiodb(self) -> None:
artist = "Various Artists"
if self.album_artist:
artist = self.album_artist.name
if self.artist:
artist = self.artist.name
album_data = lookup_album_from_tadb(self.name, artist)
if not album_data.get("theaudiodb_id"):
logger.info(f"No data for {self} found in TheAudioDB")
@ -374,7 +378,7 @@ class Album(TimeStampedModel):
def scrape_bandcamp(self, force=False) -> None:
if not self.bandcamp_id or force:
slug = get_bandcamp_slug(self.album_artist.name, self.name)
slug = get_bandcamp_slug(self.artist.name, self.name)
if not slug:
logger.info(f"No bandcamp link for {self}")
return
@ -485,19 +489,19 @@ class Album(TimeStampedModel):
@property
def rym_link(self):
artist_slug = self.album_artist.name.lower().replace(" ", "-")
artist_slug = self.artist.name.lower().replace(" ", "-")
album_slug = self.name.lower().replace(" ", "-")
return f"https://rateyourmusic.com/release/album/{artist_slug}/{album_slug}/"
@property
def bandcamp_link(self):
if self.bandcamp_id and self.album_artist.bandcamp_id:
return f"https://{self.album_artist.bandcamp_id}.bandcamp.com/album/{self.bandcamp_id}"
if self.bandcamp_id and self.artist.bandcamp_id:
return f"https://{self.artist.bandcamp_id}.bandcamp.com/album/{self.bandcamp_id}"
return ""
@property
def bandcamp_search_link(self):
artist = self.album_artist.name.lower()
artist = self.artist.name.lower()
album = self.name.lower()
return f"https://bandcamp.com/search?q={album} {artist}&item_type=a"
@ -565,7 +569,7 @@ class Album(TimeStampedModel):
album_artist=artist,
alt_names=alt_name,
)
album.artists.add(*extra_artists)
album.artists.add(artist, *extra_artists)
album.fetch_artwork()
return album

View File

@ -50,7 +50,7 @@ def export_scrobbles(start_date=None, end_date=None, format="AS"):
track = scrobble.track
track_number = 0 # TODO Add track number
track_rating = "S" # TODO implement ratings?
track_artist = track.artist or track.album.album_artist
track_artist = track.artist or track.album.artist
row = [
track_artist,
track.album.name,

View File

@ -56,7 +56,7 @@
<tr>
<td>{{album.scrobbles.count}}</td>
<td><a href="{{album.get_absolute_url}}">{{album}}</a></td>
<td><a href="{{album.album_artist.get_absolute_url}}">{{album.album_artist}}</a></td>
<td><a href="{{album.artist.get_absolute_url}}">{{album.artist}}</a></td>
</tr>
{% endfor %}
</tbody>