Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion Managers/Database/DMCategoryQueries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ extension DatabaseManager {
func getAlbumEntities() -> [AlbumEntity] {
do {
return try dbQueue.read { db in
// Prefer the album's primary artist from the album_artists junction
// (which findOrCreateAlbum populates, including "Various Artists" for compilations)
// before falling back to per-track tag aggregates.
let sql = """
SELECT
albums.id,
Expand All @@ -76,7 +79,17 @@ extension DatabaseManager {
albums.artwork_data,
albums.release_year,
COALESCE(SUM(tracks.duration), 0) as totalDuration,
COALESCE(NULLIF(MAX(tracks.album_artist), ''), MAX(tracks.artist)) as artistName,
COALESCE(
(SELECT artists.name
FROM album_artists
JOIN artists ON artists.id = album_artists.artist_id
WHERE album_artists.album_id = albums.id
AND album_artists.role = 'primary'
ORDER BY album_artists.position
LIMIT 1),
NULLIF(MAX(tracks.album_artist), ''),
MAX(tracks.artist)
) as artistName,
albums.created_at
FROM albums
LEFT JOIN tracks ON albums.id = tracks.album_id AND tracks.is_duplicate = 0
Expand Down
17 changes: 13 additions & 4 deletions Managers/Database/DMCleanup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,24 @@ extension DatabaseManager {

// 2. Now clean up main tables using GRDB where possible

// Get all artist IDs that are still referenced in track_artists
// Get all artist IDs that are still referenced in track_artists OR album_artists.
// Album-only artists (e.g. the canonical "Various Artists" used to anchor
// compilations) have no track_artists rows but must not be considered orphaned.
let artistsWithTracks = try TrackArtist
.select(TrackArtist.Columns.artistId, as: Int64.self)
.distinct()
.fetchSet(db)

// Delete artists that have NO tracks

let artistsWithAlbums = try AlbumArtist
.select(AlbumArtist.Columns.artistId, as: Int64.self)
.distinct()
.fetchSet(db)

let referencedArtists = artistsWithTracks.union(artistsWithAlbums)

// Delete artists that have NO references in either junction
let allArtistIds = try Artist.select(Artist.Columns.id, as: Int64.self).fetchSet(db)
let artistsToDelete = allArtistIds.subtracting(artistsWithTracks)
let artistsToDelete = allArtistIds.subtracting(referencedArtists)

if !artistsToDelete.isEmpty {
let orphanedArtists = try Artist
Expand Down
6 changes: 5 additions & 1 deletion Managers/Database/DMNormalization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,14 @@ extension DatabaseManager {
func processTrackAlbum(_ track: inout FullTrack, in db: Database, cache: ScanLookupCache? = nil) throws {
guard !track.album.isEmpty && track.album != "Unknown Album" else { return }

// Determine the album artist (prefer albumArtist field, fallback to first artist from multi-artist string)
// Determine the album artist (prefer albumArtist field, then compilation flag, then first artist from multi-artist string)
let albumArtistName: String?
if let albumArtist = track.albumArtist, !albumArtist.isEmpty {
albumArtistName = albumArtist
} else if track.compilation {
// Compilation flag set but no album artist tag; group all tracks under a canonical name
// so a Various Artists comp doesn't fragment into one album per track artist.
albumArtistName = "Various Artists"
} else if !track.artist.isEmpty && track.artist != "Unknown Artist" {
// Parse the artist field and use the first artist as the album artist
let artists = ArtistParser.parse(track.artist)
Expand Down
Loading