From 1a4642de273343820e50780aac6273eda6e074b0 Mon Sep 17 00:00:00 2001 From: darrell-k Date: Sat, 2 May 2026 16:54:47 +0100 Subject: [PATCH 01/25] display artist Signed-off-by: darrell-k --- Slim/Control/Queries.pm | 24 +++++++++++++++++++ Slim/Schema.pm | 51 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/Slim/Control/Queries.pm b/Slim/Control/Queries.pm index e03481c007..4f89862147 100644 --- a/Slim/Control/Queries.pm +++ b/Slim/Control/Queries.pm @@ -647,6 +647,9 @@ sub albumsQuery { # We need the main albums.contributor name for favorites, so always do this join unless getting count only. $sql .= 'JOIN contributors ON contributors.id = albums.contributor '; $c->{'contributors.name'} = 1; + $c->{'albums.label'} = 1; +# $sql .= 'LEFT JOIN contributor_display ON albums.display_contributor = contributor_display.id '; +# $c->{'contributor_display.name'} = 1; } if ( $tags =~ /s/ ) { @@ -837,6 +840,8 @@ sub albumsQuery { utf8::decode( $c->{'composer.name'} ) if exists $c->{'composer.name'}; utf8::decode( $c->{'tracks.performance'} ) if exists $c->{'tracks.performance'}; utf8::decode( $c->{'contributors.name'} ) if exists $c->{'contributors.name'}; +# utf8::decode( $c->{'contributor_display.name'} ) if exists $c->{'contributor_display.name'}; + utf8::decode( $c->{'albums.label'} ) if exists $c->{'albums.label'}; $request->addResultLoop($loopname, $chunkCount, 'id', $c->{'albums.id'}); $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'work_id', $c->{'tracks.work'}); $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'work_name', $c->{'works.title'}); @@ -900,6 +905,8 @@ sub albumsQuery { if ( !$work ) { $tags =~ /S/ && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'artist_id', $c->{'albums.contributor'}); $tags =~ /a/ && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'artist', $c->{'contributors.name'}); +# $tags =~ /a/ && $c->{'contributor_display.name'} && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'display_artist', $c->{'contributor_display.name'}); + $tags =~ /a/ && $c->{'albums.label'} && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'display_artist', $c->{'albums.label'}); $tags =~ /4/ && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'portraitid', $c->{'contributors.portraitid'}); } @@ -5792,6 +5799,9 @@ sub _songDataFromHash { $returnHash{$role} = $res->{$role}; } } + # also return contributor_display.name +# $returnHash{'display_artist'} = $res->{'contributor_display.name'} if $res->{'contributor_display.name'}; + $returnHash{'display_artist'} = $res->{'albums.label'} if $res->{'albums.label'}; } elsif ( $tag eq 'S' ) { for my $role ( @contributorRoles ) { @@ -6392,6 +6402,13 @@ sub _getTagDataForTracks { } }; +# my $join_contributor_display = sub { +# if ( $sql !~ /JOIN contributor_display/ ) { +# $join_albums->(); +# $sql .= 'LEFT JOIN contributor_display ON albums.display_contributor = contributor_display.id '; +# } +# }; + if ( my $year = $args->{year} ) { push @{$w}, 'tracks.year = ?'; push @{$p}, $year; @@ -6497,6 +6514,12 @@ sub _getTagDataForTracks { $tags =~ /[as]/ && do { $join_contributors->(); $c->{'contributors.name'} = 1 if $tags =~ /a/; + +# $join_contributor_display->(); +# $c->{'contributor_display.name'} = 1; + $c->{'albums.label'} = 1; + + # only albums on which the contributor has a specific role? my @roles; @@ -6656,6 +6679,7 @@ sub _getTagDataForTracks { utf8::decode( $c->{'tracks.lyrics'} ) if exists $c->{'tracks.lyrics'}; utf8::decode( $c->{'albums.title'} ) if exists $c->{'albums.title'}; utf8::decode( $c->{'contributors.name'} ) if exists $c->{'contributors.name'}; + utf8::decode( $c->{'contributor_display.name'} ) if exists $c->{'contributor_display.name'}; utf8::decode( $c->{'genres.name'} ) if exists $c->{'genres.name'}; utf8::decode( $c->{'comments.value'} ) if exists $c->{'comments.value'}; utf8::decode( $c->{'tracks.discsubtitle'}) if exists $c->{'tracks.discsubtitle'}; diff --git a/Slim/Schema.pm b/Slim/Schema.pm index a04164313d..038c329c7b 100644 --- a/Slim/Schema.pm +++ b/Slim/Schema.pm @@ -930,7 +930,7 @@ sub _objForDbUrl { } sub _createOrUpdateAlbum { - my ($self, $attributes, $trackColumns, $isCompilation, $contributorId, $hasAlbumArtist, $create, $track, $basename) = @_; + my ($self, $attributes, $trackColumns, $isCompilation, $contributorId, $hasAlbumArtist, $create, $track, $basename, $aaDisplayID) = @_; my $dbh = $self->dbh; @@ -1296,6 +1296,8 @@ sub _createOrUpdateAlbum { } $albumHash->{musicbrainz_id} = $attributes->{MUSICBRAINZ_ALBUM_ID}; +# $albumHash->{display_contributor} = $aaDisplayID; + $albumHash->{label} = $aaDisplayID; # Handle album gain tags. for my $gainTag ( qw(REPLAYGAIN_ALBUM_GAIN REPLAYGAIN_ALBUM_PEAK) ) { @@ -1706,7 +1708,18 @@ sub _newTrack { $LAST_ERROR = 'Track is DRM-protected'; return; } - +### + if ( $attributeHash->{ALBUMARTIST} && $attributeHash->{ALBUMARTISTS} ) { + my $temp = $attributeHash->{ALBUMARTISTS}; + $attributeHash->{ALBUMARTISTS} = $attributeHash->{ALBUMARTIST}; + $attributeHash->{ALBUMARTIST} = $temp; + } + if ( $attributeHash->{ARTIST} && $attributeHash->{ARTISTS} ) { + my $temp = $attributeHash->{ARTISTS}; + $attributeHash->{ARTISTS} = $attributeHash->{ARTIST}; + $attributeHash->{ARTIST} = $temp; + } +### ($attributeHash, $deferredAttributes) = $self->_preCheckAttributes({ 'url' => $url, 'attributes' => $attributeHash, @@ -1793,6 +1806,22 @@ sub _newTrack { # Walk through the valid contributor roles, adding them to the database. my $contributors = $self->_mergeAndCreateContributors($deferredAttributes, $isCompilation, 1); +### + my $aaDisplayID = $deferredAttributes->{ALBUMARTISTS}; +# my $aaDisplayID; +# if ( $deferredAttributes->{ALBUMARTISTS} ) { +# my $contributor_display_sth = $self->dbh->prepare_cached('SELECT id FROM contributor_display WHERE name = ?'); +# $contributor_display_sth->execute($deferredAttributes->{ALBUMARTISTS}); +# ($aaDisplayID) = $contributor_display_sth->fetchrow_array; +# $contributor_display_sth->finish; +# if ( !$aaDisplayID ) { +# my $sth_insert = $self->dbh->prepare_cached("INSERT INTO contributor_display (name) VALUES (?)"); +# $sth_insert->execute( $deferredAttributes->{ALBUMARTISTS} ); +# $aaDisplayID = $self->dbh->last_insert_id(undef, undef, undef, undef); +# } +# } +### + # Set primary_artist for the track if ( my $artist = $contributors->{ARTIST} || $contributors->{TRACKARTIST} ) { $columnValueHash{primary_artist} = $artist->[0]; @@ -1833,6 +1862,7 @@ sub _newTrack { 1, # create undef, # Track $dirname, + $aaDisplayID, ); ### Create Track row @@ -1841,7 +1871,7 @@ sub _newTrack { $trackId = $self->_createTrack(\%columnValueHash, \%persistentColumnValueHash, $source); ### Create ContributorTrack & ContributorAlbum rows - $self->_createContributorRoleRelationships($contributors, $trackId, $albumId); + $self->_createContributorRoleRelationships($contributors, $trackId, $albumId, $aaDisplayID); ### Create Genre rows $self->_createGenre($deferredAttributes->{'GENRE'}, $trackId, 1); @@ -2842,6 +2872,7 @@ sub _preCheckAttributes { MUSICBRAINZ_ARTIST_ID MUSICBRAINZ_ALBUMARTIST_ID MUSICBRAINZ_ALBUM_ID MUSICBRAINZ_ALBUM_TYPE MUSICBRAINZ_ALBUM_STATUS RELEASETYPE ALBUM_EXTID ARTIST_EXTID WORK WORKSORT + ARTISTS ALBUMARTISTS )) { @@ -3191,7 +3222,7 @@ sub _mergeAndCreateContributors { sub _createContributorRoleRelationships { - my ($self, $contributors, $trackId, $albumId) = @_; + my ($self, $contributors, $trackId, $albumId, $aaDisplayID) = @_; if (!keys %$contributors) { main::DEBUGLOG && $log->debug('Attempt to set empty contributor set for trackid=', $trackId); @@ -3231,7 +3262,14 @@ sub _createContributorRoleRelationships { VALUES (?, ?, ?) } ); - +# my $sth_album_display = $self->dbh->prepare_cached( qq{ +# REPLACE INTO contributor_album_display +# (contributor_display, contributor, album) +# VALUES +# (?, ?, ?) +# } ) if $aaDisplayID; + +Slim::Utils::Log::logError("DK \$contributors=" . Data::Dump::dump($contributors)); while (my ($role, $contributorList) = each %{$contributors}) { my $roleId = Slim::Schema::Contributor->typeToRole($role); for my $contributor (@{$contributorList}) { @@ -3242,6 +3280,9 @@ sub _createContributorRoleRelationships { # The following is retained at present to add mappings for BMF, entries created will be deleted in the optimise phase $sth_album->execute( $roleId, $contributor, $albumId ); +# if ( $aaDisplayID && $role eq 'ALBUMARTIST' ) { +# $sth_album_display->execute( $aaDisplayID, $contributor, $albumId ); +# } } } } From 33893cd1740ff03d2c87a8e3ca1e523deb0bdebf Mon Sep 17 00:00:00 2001 From: darrell-k Date: Thu, 7 May 2026 22:41:11 +0100 Subject: [PATCH 02/25] album join fix Signed-off-by: darrell-k --- Slim/Control/Queries.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/Slim/Control/Queries.pm b/Slim/Control/Queries.pm index 4f89862147..2cde8a3cbf 100644 --- a/Slim/Control/Queries.pm +++ b/Slim/Control/Queries.pm @@ -6517,6 +6517,7 @@ sub _getTagDataForTracks { # $join_contributor_display->(); # $c->{'contributor_display.name'} = 1; + $join_albums->(); $c->{'albums.label'} = 1; From 53b9540e4baffb6212f147e96a75c68fb929c835 Mon Sep 17 00:00:00 2001 From: darrell-k Date: Mon, 18 May 2026 14:55:14 +0100 Subject: [PATCH 03/25] draft schema changes Signed-off-by: darrell-k --- SQL/SQLite/schema_27_down.sql | 0 SQL/SQLite/schema_27_up.sql | 36 +++++++++++++++++++++++++++++++++++ SQL/mysql/schema_27_down.sql | 36 +++++++++++++++++++++++++++++++++++ SQL/mysql/schema_27_up.sql | 36 +++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 SQL/SQLite/schema_27_down.sql create mode 100644 SQL/SQLite/schema_27_up.sql create mode 100644 SQL/mysql/schema_27_down.sql create mode 100644 SQL/mysql/schema_27_up.sql diff --git a/SQL/SQLite/schema_27_down.sql b/SQL/SQLite/schema_27_down.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/SQL/SQLite/schema_27_up.sql b/SQL/SQLite/schema_27_up.sql new file mode 100644 index 0000000000..c9d106bb73 --- /dev/null +++ b/SQL/SQLite/schema_27_up.sql @@ -0,0 +1,36 @@ +-- full relational solution for display_artist. commented out for now + +--DROP TABLE IF EXISTS contributor_display; +--CREATE TABLE contributor_display ( +-- id INTEGER PRIMARY KEY AUTOINCREMENT, +-- name blob); +--ALTER TABLE albums ADD display_contributor integer; + +--DROP TABLE IF EXISTS contributor_album_display; +--CREATE TABLE contributor_album_display ( +-- contributor_display int(10), +-- contributor int(10), +-- album int(10), +-- PRIMARY KEY (contributor_display,contributor,album), +-- FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, +-- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +--); +--CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); +--CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); +--CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); + +--ALTER TABLE tracks ADD display_contributor integer; + +--DROP TABLE IF EXISTS contributor_track_display; +--CREATE TABLE contributor_track_display ( +-- contributor_display int(10), +-- contributor int(10), +-- track int(10), +-- PRIMARY KEY (contributor_display,contributor,track), +-- FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, +-- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +--); +--CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); +--CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); +--CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); + diff --git a/SQL/mysql/schema_27_down.sql b/SQL/mysql/schema_27_down.sql new file mode 100644 index 0000000000..c9d106bb73 --- /dev/null +++ b/SQL/mysql/schema_27_down.sql @@ -0,0 +1,36 @@ +-- full relational solution for display_artist. commented out for now + +--DROP TABLE IF EXISTS contributor_display; +--CREATE TABLE contributor_display ( +-- id INTEGER PRIMARY KEY AUTOINCREMENT, +-- name blob); +--ALTER TABLE albums ADD display_contributor integer; + +--DROP TABLE IF EXISTS contributor_album_display; +--CREATE TABLE contributor_album_display ( +-- contributor_display int(10), +-- contributor int(10), +-- album int(10), +-- PRIMARY KEY (contributor_display,contributor,album), +-- FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, +-- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +--); +--CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); +--CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); +--CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); + +--ALTER TABLE tracks ADD display_contributor integer; + +--DROP TABLE IF EXISTS contributor_track_display; +--CREATE TABLE contributor_track_display ( +-- contributor_display int(10), +-- contributor int(10), +-- track int(10), +-- PRIMARY KEY (contributor_display,contributor,track), +-- FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, +-- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +--); +--CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); +--CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); +--CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); + diff --git a/SQL/mysql/schema_27_up.sql b/SQL/mysql/schema_27_up.sql new file mode 100644 index 0000000000..c9d106bb73 --- /dev/null +++ b/SQL/mysql/schema_27_up.sql @@ -0,0 +1,36 @@ +-- full relational solution for display_artist. commented out for now + +--DROP TABLE IF EXISTS contributor_display; +--CREATE TABLE contributor_display ( +-- id INTEGER PRIMARY KEY AUTOINCREMENT, +-- name blob); +--ALTER TABLE albums ADD display_contributor integer; + +--DROP TABLE IF EXISTS contributor_album_display; +--CREATE TABLE contributor_album_display ( +-- contributor_display int(10), +-- contributor int(10), +-- album int(10), +-- PRIMARY KEY (contributor_display,contributor,album), +-- FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, +-- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +--); +--CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); +--CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); +--CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); + +--ALTER TABLE tracks ADD display_contributor integer; + +--DROP TABLE IF EXISTS contributor_track_display; +--CREATE TABLE contributor_track_display ( +-- contributor_display int(10), +-- contributor int(10), +-- track int(10), +-- PRIMARY KEY (contributor_display,contributor,track), +-- FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, +-- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +--); +--CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); +--CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); +--CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); + From 45888ca502f4d1587c4236dad2f6f09e44ad083b Mon Sep 17 00:00:00 2001 From: darrell-k Date: Mon, 18 May 2026 15:06:18 +0100 Subject: [PATCH 04/25] oops Signed-off-by: darrell-k --- SQL/mysql/schema_27_down.sql | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/SQL/mysql/schema_27_down.sql b/SQL/mysql/schema_27_down.sql index c9d106bb73..e69de29bb2 100644 --- a/SQL/mysql/schema_27_down.sql +++ b/SQL/mysql/schema_27_down.sql @@ -1,36 +0,0 @@ --- full relational solution for display_artist. commented out for now - ---DROP TABLE IF EXISTS contributor_display; ---CREATE TABLE contributor_display ( --- id INTEGER PRIMARY KEY AUTOINCREMENT, --- name blob); ---ALTER TABLE albums ADD display_contributor integer; - ---DROP TABLE IF EXISTS contributor_album_display; ---CREATE TABLE contributor_album_display ( --- contributor_display int(10), --- contributor int(10), --- album int(10), --- PRIMARY KEY (contributor_display,contributor,album), --- FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, --- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE ---); ---CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); ---CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); ---CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); - ---ALTER TABLE tracks ADD display_contributor integer; - ---DROP TABLE IF EXISTS contributor_track_display; ---CREATE TABLE contributor_track_display ( --- contributor_display int(10), --- contributor int(10), --- track int(10), --- PRIMARY KEY (contributor_display,contributor,track), --- FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, --- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE ---); ---CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); ---CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); ---CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); - From e2ea8434cd2e18f1527da8c01816b10fbce0db5c Mon Sep 17 00:00:00 2001 From: darrell-k Date: Mon, 18 May 2026 17:16:16 +0100 Subject: [PATCH 05/25] implement 3nf schema changes Signed-off-by: darrell-k --- SQL/SQLite/schema_27_up.sql | 62 ++++++++++++++++++------------------- SQL/mysql/schema_27_up.sql | 62 ++++++++++++++++++------------------- Slim/Control/Queries.pm | 34 ++++++++------------ Slim/Schema.pm | 53 ++++++++++++++----------------- 4 files changed, 98 insertions(+), 113 deletions(-) diff --git a/SQL/SQLite/schema_27_up.sql b/SQL/SQLite/schema_27_up.sql index c9d106bb73..4c6efeee9f 100644 --- a/SQL/SQLite/schema_27_up.sql +++ b/SQL/SQLite/schema_27_up.sql @@ -1,36 +1,36 @@ --- full relational solution for display_artist. commented out for now +-- full relational solution for display_artist ---DROP TABLE IF EXISTS contributor_display; ---CREATE TABLE contributor_display ( --- id INTEGER PRIMARY KEY AUTOINCREMENT, --- name blob); ---ALTER TABLE albums ADD display_contributor integer; +DROP TABLE IF EXISTS contributor_display; +CREATE TABLE contributor_display ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name blob); +ALTER TABLE albums ADD display_contributor integer; ---DROP TABLE IF EXISTS contributor_album_display; ---CREATE TABLE contributor_album_display ( --- contributor_display int(10), --- contributor int(10), --- album int(10), --- PRIMARY KEY (contributor_display,contributor,album), --- FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, --- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE ---); ---CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); ---CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); ---CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); +DROP TABLE IF EXISTS contributor_album_display; +CREATE TABLE contributor_album_display ( + contributor_display int(10), + contributor int(10), + album int(10), + PRIMARY KEY (contributor_display,contributor,album), + FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +); +CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); +CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); +CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); ---ALTER TABLE tracks ADD display_contributor integer; +ALTER TABLE tracks ADD display_contributor integer; ---DROP TABLE IF EXISTS contributor_track_display; ---CREATE TABLE contributor_track_display ( --- contributor_display int(10), --- contributor int(10), --- track int(10), --- PRIMARY KEY (contributor_display,contributor,track), --- FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, --- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE ---); ---CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); ---CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); ---CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); +DROP TABLE IF EXISTS contributor_track_display; +CREATE TABLE contributor_track_display ( + contributor_display int(10), + contributor int(10), + track int(10), + PRIMARY KEY (contributor_display,contributor,track), + FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +); +CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); +CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); +CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); diff --git a/SQL/mysql/schema_27_up.sql b/SQL/mysql/schema_27_up.sql index c9d106bb73..3c18edcbcc 100644 --- a/SQL/mysql/schema_27_up.sql +++ b/SQL/mysql/schema_27_up.sql @@ -1,36 +1,36 @@ --- full relational solution for display_artist. commented out for now +-- full relational solution for display_artist. ---DROP TABLE IF EXISTS contributor_display; ---CREATE TABLE contributor_display ( --- id INTEGER PRIMARY KEY AUTOINCREMENT, --- name blob); ---ALTER TABLE albums ADD display_contributor integer; +DROP TABLE IF EXISTS contributor_display; +CREATE TABLE contributor_display ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name blob); +ALTER TABLE albums ADD display_contributor integer; ---DROP TABLE IF EXISTS contributor_album_display; ---CREATE TABLE contributor_album_display ( --- contributor_display int(10), --- contributor int(10), --- album int(10), --- PRIMARY KEY (contributor_display,contributor,album), --- FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, --- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE ---); ---CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); ---CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); ---CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); +DROP TABLE IF EXISTS contributor_album_display; +CREATE TABLE contributor_album_display ( + contributor_display int(10), + contributor int(10), + album int(10), + PRIMARY KEY (contributor_display,contributor,album), + FOREIGN KEY (`album`) REFERENCES `albums` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +); +CREATE INDEX contributor_album_displayContribIndex ON contributor_album_display (contributor); +CREATE INDEX contributor_album_displayAlbumIndex ON contributor_album_display (album); +CREATE INDEX contributor_album_displayContributorDisplayIndex ON contributor_album_display (contributor_display); ---ALTER TABLE tracks ADD display_contributor integer; +ALTER TABLE tracks ADD display_contributor integer; ---DROP TABLE IF EXISTS contributor_track_display; ---CREATE TABLE contributor_track_display ( --- contributor_display int(10), --- contributor int(10), --- track int(10), --- PRIMARY KEY (contributor_display,contributor,track), --- FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, --- FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE ---); ---CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); ---CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); ---CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); +DROP TABLE IF EXISTS contributor_track_display; +CREATE TABLE contributor_track_display ( + contributor_display int(10), + contributor int(10), + track int(10), + PRIMARY KEY (contributor_display,contributor,track), + FOREIGN KEY (`track`) REFERENCES `tracks` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`contributor`) REFERENCES `contributors` (`id`) ON DELETE CASCADE +); +CREATE INDEX contributor_track_displayContribIndex ON contributor_track_display (contributor); +CREATE INDEX contributor_track_displayTrackIndex ON contributor_track_display (track); +CREATE INDEX contributor_track_displayContributorDisplayIndex ON contributor_track_display (contributor_display); diff --git a/Slim/Control/Queries.pm b/Slim/Control/Queries.pm index 2cde8a3cbf..8a818eac84 100644 --- a/Slim/Control/Queries.pm +++ b/Slim/Control/Queries.pm @@ -648,8 +648,8 @@ sub albumsQuery { $sql .= 'JOIN contributors ON contributors.id = albums.contributor '; $c->{'contributors.name'} = 1; $c->{'albums.label'} = 1; -# $sql .= 'LEFT JOIN contributor_display ON albums.display_contributor = contributor_display.id '; -# $c->{'contributor_display.name'} = 1; + $sql .= 'LEFT JOIN contributor_display ON albums.display_contributor = contributor_display.id '; + $c->{'contributor_display.name'} = 1; } if ( $tags =~ /s/ ) { @@ -840,8 +840,7 @@ sub albumsQuery { utf8::decode( $c->{'composer.name'} ) if exists $c->{'composer.name'}; utf8::decode( $c->{'tracks.performance'} ) if exists $c->{'tracks.performance'}; utf8::decode( $c->{'contributors.name'} ) if exists $c->{'contributors.name'}; -# utf8::decode( $c->{'contributor_display.name'} ) if exists $c->{'contributor_display.name'}; - utf8::decode( $c->{'albums.label'} ) if exists $c->{'albums.label'}; + utf8::decode( $c->{'contributor_display.name'} ) if exists $c->{'contributor_display.name'}; $request->addResultLoop($loopname, $chunkCount, 'id', $c->{'albums.id'}); $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'work_id', $c->{'tracks.work'}); $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'work_name', $c->{'works.title'}); @@ -905,8 +904,7 @@ sub albumsQuery { if ( !$work ) { $tags =~ /S/ && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'artist_id', $c->{'albums.contributor'}); $tags =~ /a/ && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'artist', $c->{'contributors.name'}); -# $tags =~ /a/ && $c->{'contributor_display.name'} && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'display_artist', $c->{'contributor_display.name'}); - $tags =~ /a/ && $c->{'albums.label'} && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'display_artist', $c->{'albums.label'}); + $tags =~ /a/ && $c->{'contributor_display.name'} && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'display_artist', $c->{'contributor_display.name'}); $tags =~ /4/ && $request->addResultLoopIfValueDefined($loopname, $chunkCount, 'portraitid', $c->{'contributors.portraitid'}); } @@ -5800,8 +5798,7 @@ sub _songDataFromHash { } } # also return contributor_display.name -# $returnHash{'display_artist'} = $res->{'contributor_display.name'} if $res->{'contributor_display.name'}; - $returnHash{'display_artist'} = $res->{'albums.label'} if $res->{'albums.label'}; + $returnHash{'display_artist'} = $res->{'contributor_display.name'} if $res->{'contributor_display.name'}; } elsif ( $tag eq 'S' ) { for my $role ( @contributorRoles ) { @@ -6402,12 +6399,12 @@ sub _getTagDataForTracks { } }; -# my $join_contributor_display = sub { -# if ( $sql !~ /JOIN contributor_display/ ) { -# $join_albums->(); -# $sql .= 'LEFT JOIN contributor_display ON albums.display_contributor = contributor_display.id '; -# } -# }; + my $join_contributor_display = sub { + if ( $sql !~ /JOIN contributor_display/ ) { + $join_albums->(); + $sql .= 'LEFT JOIN contributor_display ON albums.display_contributor = contributor_display.id '; + } + }; if ( my $year = $args->{year} ) { push @{$w}, 'tracks.year = ?'; @@ -6514,13 +6511,8 @@ sub _getTagDataForTracks { $tags =~ /[as]/ && do { $join_contributors->(); $c->{'contributors.name'} = 1 if $tags =~ /a/; - -# $join_contributor_display->(); -# $c->{'contributor_display.name'} = 1; - $join_albums->(); - $c->{'albums.label'} = 1; - - + $join_contributor_display->(); + $c->{'contributor_display.name'} = 1; # only albums on which the contributor has a specific role? my @roles; diff --git a/Slim/Schema.pm b/Slim/Schema.pm index 038c329c7b..ffbb54ca4d 100644 --- a/Slim/Schema.pm +++ b/Slim/Schema.pm @@ -1296,8 +1296,7 @@ sub _createOrUpdateAlbum { } $albumHash->{musicbrainz_id} = $attributes->{MUSICBRAINZ_ALBUM_ID}; -# $albumHash->{display_contributor} = $aaDisplayID; - $albumHash->{label} = $aaDisplayID; + $albumHash->{display_contributor} = $aaDisplayID; # Handle album gain tags. for my $gainTag ( qw(REPLAYGAIN_ALBUM_GAIN REPLAYGAIN_ALBUM_PEAK) ) { @@ -1708,7 +1707,6 @@ sub _newTrack { $LAST_ERROR = 'Track is DRM-protected'; return; } -### if ( $attributeHash->{ALBUMARTIST} && $attributeHash->{ALBUMARTISTS} ) { my $temp = $attributeHash->{ALBUMARTISTS}; $attributeHash->{ALBUMARTISTS} = $attributeHash->{ALBUMARTIST}; @@ -1719,7 +1717,6 @@ sub _newTrack { $attributeHash->{ARTISTS} = $attributeHash->{ARTIST}; $attributeHash->{ARTIST} = $temp; } -### ($attributeHash, $deferredAttributes) = $self->_preCheckAttributes({ 'url' => $url, 'attributes' => $attributeHash, @@ -1806,21 +1803,18 @@ sub _newTrack { # Walk through the valid contributor roles, adding them to the database. my $contributors = $self->_mergeAndCreateContributors($deferredAttributes, $isCompilation, 1); -### - my $aaDisplayID = $deferredAttributes->{ALBUMARTISTS}; -# my $aaDisplayID; -# if ( $deferredAttributes->{ALBUMARTISTS} ) { -# my $contributor_display_sth = $self->dbh->prepare_cached('SELECT id FROM contributor_display WHERE name = ?'); -# $contributor_display_sth->execute($deferredAttributes->{ALBUMARTISTS}); -# ($aaDisplayID) = $contributor_display_sth->fetchrow_array; -# $contributor_display_sth->finish; -# if ( !$aaDisplayID ) { -# my $sth_insert = $self->dbh->prepare_cached("INSERT INTO contributor_display (name) VALUES (?)"); -# $sth_insert->execute( $deferredAttributes->{ALBUMARTISTS} ); -# $aaDisplayID = $self->dbh->last_insert_id(undef, undef, undef, undef); -# } -# } -### + my $aaDisplayID; + if ( $deferredAttributes->{ALBUMARTISTS} ) { + my $contributor_display_sth = $self->dbh->prepare_cached('SELECT id FROM contributor_display WHERE name = ?'); + $contributor_display_sth->execute($deferredAttributes->{ALBUMARTISTS}); + ($aaDisplayID) = $contributor_display_sth->fetchrow_array; + $contributor_display_sth->finish; + if ( !$aaDisplayID ) { + my $sth_insert = $self->dbh->prepare_cached("INSERT INTO contributor_display (name) VALUES (?)"); + $sth_insert->execute( $deferredAttributes->{ALBUMARTISTS} ); + $aaDisplayID = $self->dbh->last_insert_id(undef, undef, undef, undef); + } + } # Set primary_artist for the track if ( my $artist = $contributors->{ARTIST} || $contributors->{TRACKARTIST} ) { @@ -3262,14 +3256,13 @@ sub _createContributorRoleRelationships { VALUES (?, ?, ?) } ); -# my $sth_album_display = $self->dbh->prepare_cached( qq{ -# REPLACE INTO contributor_album_display -# (contributor_display, contributor, album) -# VALUES -# (?, ?, ?) -# } ) if $aaDisplayID; - -Slim::Utils::Log::logError("DK \$contributors=" . Data::Dump::dump($contributors)); + my $sth_album_display = $self->dbh->prepare_cached( qq{ + REPLACE INTO contributor_album_display + (contributor_display, contributor, album) + VALUES + (?, ?, ?) + } ) if $aaDisplayID; + while (my ($role, $contributorList) = each %{$contributors}) { my $roleId = Slim::Schema::Contributor->typeToRole($role); for my $contributor (@{$contributorList}) { @@ -3280,9 +3273,9 @@ Slim::Utils::Log::logError("DK \$contributors=" . Data::Dump::dump($contributors # The following is retained at present to add mappings for BMF, entries created will be deleted in the optimise phase $sth_album->execute( $roleId, $contributor, $albumId ); -# if ( $aaDisplayID && $role eq 'ALBUMARTIST' ) { -# $sth_album_display->execute( $aaDisplayID, $contributor, $albumId ); -# } + if ( $aaDisplayID && $role eq 'ALBUMARTIST' ) { + $sth_album_display->execute( $aaDisplayID, $contributor, $albumId ); + } } } } From 8e0f88a261f113f64f6600c08510751a55c3a4dc Mon Sep 17 00:00:00 2001 From: darrell-k Date: Mon, 18 May 2026 20:55:55 +0100 Subject: [PATCH 06/25] new table clear Signed-off-by: darrell-k --- SQL/SQLite/schema_clear.sql | 8 +++++++- SQL/mysql/schema_clear.sql | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/SQL/SQLite/schema_clear.sql b/SQL/SQLite/schema_clear.sql index f06c00f1f4..bd614e1a7c 100644 --- a/SQL/SQLite/schema_clear.sql +++ b/SQL/SQLite/schema_clear.sql @@ -31,6 +31,12 @@ DELETE FROM library_album; DELETE FROM library_contributor; DELETE FROM library_genre; +DELETE FROM contributor_display; + +DELETE FROM contributor_album_display; + +DELETE FROM contributor_track_display; + -- these table are created by the Fulltext Search plugin DROP TABLE IF EXISTS fulltext; DROP TABLE IF EXISTS fulltext_terms; @@ -40,4 +46,4 @@ UPDATE metainformation SET value = 0 WHERE name = 'lastRescanTime'; -- start the contributor ID at the old offset to prevent ID overlap between scans UPDATE SQLITE_SEQUENCE SET seq = (SELECT old.seq FROM old_autoincrement AS old WHERE old.name = 'contributors') WHERE name = 'contributors'; -DROP TABLE old_autoincrement; \ No newline at end of file +DROP TABLE old_autoincrement; diff --git a/SQL/mysql/schema_clear.sql b/SQL/mysql/schema_clear.sql index f0a7d886c0..ce470cf1a1 100644 --- a/SQL/mysql/schema_clear.sql +++ b/SQL/mysql/schema_clear.sql @@ -29,6 +29,12 @@ DELETE FROM scanned_files; DELETE FROM works; +DELETE FROM contributor_display; + +DELETE FROM contributor_album_display; + +DELETE FROM contributor_track_display; + UPDATE metainformation SET value = 0 WHERE name = 'lastRescanTime'; UPDATE tracks_persistent SET track = NULL; From 71ad0ff138bdcc159fe12124354a37c886af7224 Mon Sep 17 00:00:00 2001 From: Rouzax Date: Mon, 18 May 2026 19:05:34 +0200 Subject: [PATCH 07/25] Extract _getOrCreateDisplayContributor helper from inline _newTrack code Signed-off-by: Rouzax --- Slim/Schema.pm | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/Slim/Schema.pm b/Slim/Schema.pm index 06aa1d28de..35ab17c071 100644 --- a/Slim/Schema.pm +++ b/Slim/Schema.pm @@ -1804,18 +1804,7 @@ sub _newTrack { # Walk through the valid contributor roles, adding them to the database. my $contributors = $self->_mergeAndCreateContributors($deferredAttributes, $isCompilation, 1); - my $aaDisplayID; - if ( $deferredAttributes->{ALBUMARTISTS} ) { - my $contributor_display_sth = $self->dbh->prepare_cached('SELECT id FROM contributor_display WHERE name = ?'); - $contributor_display_sth->execute($deferredAttributes->{ALBUMARTISTS}); - ($aaDisplayID) = $contributor_display_sth->fetchrow_array; - $contributor_display_sth->finish; - if ( !$aaDisplayID ) { - my $sth_insert = $self->dbh->prepare_cached("INSERT INTO contributor_display (name) VALUES (?)"); - $sth_insert->execute( $deferredAttributes->{ALBUMARTISTS} ); - $aaDisplayID = $self->dbh->last_insert_id(undef, undef, undef, undef); - } - } + my $aaDisplayID = $self->_getOrCreateDisplayContributor($deferredAttributes->{ALBUMARTISTS}); # Set primary_artist for the track if ( my $artist = $contributors->{ARTIST} || $contributors->{TRACKARTIST} ) { @@ -3217,6 +3206,28 @@ sub _mergeAndCreateContributors { return \%contributors; } +sub _getOrCreateDisplayContributor { + my ($self, $displayName) = @_; + return unless defined $displayName && $displayName ne ''; + + my $sth = $self->dbh->prepare_cached( + 'SELECT id FROM contributor_display WHERE name = ?' + ); + $sth->execute($displayName); + my ($id) = $sth->fetchrow_array; + $sth->finish; + + if (!$id) { + my $insert = $self->dbh->prepare_cached( + 'INSERT INTO contributor_display (name) VALUES (?)' + ); + $insert->execute($displayName); + $id = $self->dbh->last_insert_id(undef, undef, undef, undef); + } + + return $id; +} + sub _createContributorRoleRelationships { my ($self, $contributors, $trackId, $albumId, $aaDisplayID) = @_; From 22d8f5e20e1c303e57f8acf5db1448b6a46cdf09 Mon Sep 17 00:00:00 2001 From: Rouzax Date: Mon, 18 May 2026 19:06:57 +0200 Subject: [PATCH 08/25] Replace tag swap with display string capture in _newTrack Signed-off-by: Rouzax --- Slim/Schema.pm | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Slim/Schema.pm b/Slim/Schema.pm index 35ab17c071..8d0cff5546 100644 --- a/Slim/Schema.pm +++ b/Slim/Schema.pm @@ -1708,15 +1708,22 @@ sub _newTrack { $LAST_ERROR = 'Track is DRM-protected'; return; } - if ( $attributeHash->{ALBUMARTIST} && $attributeHash->{ALBUMARTISTS} ) { - my $temp = $attributeHash->{ALBUMARTISTS}; - $attributeHash->{ALBUMARTISTS} = $attributeHash->{ALBUMARTIST}; - $attributeHash->{ALBUMARTIST} = $temp; - } - if ( $attributeHash->{ARTIST} && $attributeHash->{ARTISTS} ) { - my $temp = $attributeHash->{ARTISTS}; - $attributeHash->{ARTISTS} = $attributeHash->{ARTIST}; - $attributeHash->{ARTIST} = $temp; + my $albumDisplayArtist; + if ( $attributeHash->{ALBUMARTIST} ) { + $albumDisplayArtist = ref $attributeHash->{ALBUMARTIST} eq 'ARRAY' + ? $attributeHash->{ALBUMARTIST}->[0] + : $attributeHash->{ALBUMARTIST}; + } elsif ( $attributeHash->{ALBUMARTISTS} && ref $attributeHash->{ALBUMARTISTS} eq 'ARRAY' ) { + $albumDisplayArtist = join(', ', grep { defined $_ && $_ ne '' } @{$attributeHash->{ALBUMARTISTS}}); + } + + my $trackDisplayArtist; + if ( $attributeHash->{ARTIST} ) { + $trackDisplayArtist = ref $attributeHash->{ARTIST} eq 'ARRAY' + ? $attributeHash->{ARTIST}->[0] + : $attributeHash->{ARTIST}; + } elsif ( $attributeHash->{ARTISTS} && ref $attributeHash->{ARTISTS} eq 'ARRAY' ) { + $trackDisplayArtist = join(', ', grep { defined $_ && $_ ne '' } @{$attributeHash->{ARTISTS}}); } ($attributeHash, $deferredAttributes) = $self->_preCheckAttributes({ 'url' => $url, @@ -1804,12 +1811,14 @@ sub _newTrack { # Walk through the valid contributor roles, adding them to the database. my $contributors = $self->_mergeAndCreateContributors($deferredAttributes, $isCompilation, 1); - my $aaDisplayID = $self->_getOrCreateDisplayContributor($deferredAttributes->{ALBUMARTISTS}); + my $aaDisplayID = $self->_getOrCreateDisplayContributor($albumDisplayArtist); + my $taDisplayID = $self->_getOrCreateDisplayContributor($trackDisplayArtist); # Set primary_artist for the track if ( my $artist = $contributors->{ARTIST} || $contributors->{TRACKARTIST} ) { $columnValueHash{primary_artist} = $artist->[0]; } + $columnValueHash{display_contributor} = $taDisplayID if $taDisplayID; ### Create Work rows my $workID; @@ -1855,7 +1864,7 @@ sub _newTrack { $trackId = $self->_createTrack(\%columnValueHash, \%persistentColumnValueHash, $source); ### Create ContributorTrack & ContributorAlbum rows - $self->_createContributorRoleRelationships($contributors, $trackId, $albumId, $aaDisplayID); + $self->_createContributorRoleRelationships($contributors, $trackId, $albumId, $aaDisplayID, $taDisplayID); ### Create Genre rows $self->_createGenre($deferredAttributes->{'GENRE'}, $trackId, 1); From 9f31f1c10bc7fd1ac8f0316f09e5ede3f560a20e Mon Sep 17 00:00:00 2001 From: Rouzax Date: Mon, 18 May 2026 19:09:30 +0200 Subject: [PATCH 09/25] Fix rescan path: populate display_contributor on track update In updateOrCreateBase, capture albumDisplayArtist and trackDisplayArtist from the attributeHash before _preCheckAttributes strips/defers them. Pass both values to _postCheckAttributes, which now calls _getOrCreateDisplayContributor for each, writes display_contributor to the track row, and forwards both IDs to _createOrUpdateAlbum and _createContributorRoleRelationships. Without this, rescanning an existing track never updated display_contributor. Signed-off-by: Martijn Frazer Signed-off-by: Rouzax --- Slim/Schema.pm | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/Slim/Schema.pm b/Slim/Schema.pm index 8d0cff5546..38f259d4c6 100644 --- a/Slim/Schema.pm +++ b/Slim/Schema.pm @@ -2020,6 +2020,24 @@ sub updateOrCreateBase { $attributeHash = { %{Slim::Formats->readTags($url)}, %$attributeHash }; } + my $albumDisplayArtist; + if ( $attributeHash->{ALBUMARTIST} ) { + $albumDisplayArtist = ref $attributeHash->{ALBUMARTIST} eq 'ARRAY' + ? $attributeHash->{ALBUMARTIST}->[0] + : $attributeHash->{ALBUMARTIST}; + } elsif ( $attributeHash->{ALBUMARTISTS} && ref $attributeHash->{ALBUMARTISTS} eq 'ARRAY' ) { + $albumDisplayArtist = join(', ', grep { defined $_ && $_ ne '' } @{$attributeHash->{ALBUMARTISTS}}); + } + + my $trackDisplayArtist; + if ( $attributeHash->{ARTIST} ) { + $trackDisplayArtist = ref $attributeHash->{ARTIST} eq 'ARRAY' + ? $attributeHash->{ARTIST}->[0] + : $attributeHash->{ARTIST}; + } elsif ( $attributeHash->{ARTISTS} && ref $attributeHash->{ARTISTS} eq 'ARRAY' ) { + $trackDisplayArtist = join(', ', grep { defined $_ && $_ ne '' } @{$attributeHash->{ARTISTS}}); + } + my $deferredAttributes; ($attributeHash, $deferredAttributes) = $self->_preCheckAttributes({ 'url' => $url, @@ -2070,9 +2088,11 @@ sub updateOrCreateBase { if (!$playlist) { $self->_postCheckAttributes({ - 'track' => $track, - 'attributes' => $deferredAttributes, - 'integrateRemote' => $integrateRemote + 'track' => $track, + 'attributes' => $deferredAttributes, + 'integrateRemote' => $integrateRemote, + 'albumDisplayArtist' => $albumDisplayArtist, + 'trackDisplayArtist' => $trackDisplayArtist, }); } @@ -3049,6 +3069,9 @@ sub _postCheckAttributes { my $attributes = $args->{'attributes'}; my $create = $args->{'create'} || 0; + my $albumDisplayArtist = $args->{'albumDisplayArtist'}; + my $trackDisplayArtist = $args->{'trackDisplayArtist'}; + # Don't bother with directories / lnks. This makes sure "No Artist", # etc don't show up if you don't have any. my %cols = $track->get_columns; @@ -3090,6 +3113,13 @@ sub _postCheckAttributes { $cols{primary_artist} = $artist->[0]; } + my $aaDisplayID = $self->_getOrCreateDisplayContributor($albumDisplayArtist); + my $taDisplayID = $self->_getOrCreateDisplayContributor($trackDisplayArtist); + + if ($taDisplayID) { + $track->set_column('display_contributor', $taDisplayID); + } + #Work if (defined $attributes->{'WORK'}) { if ( _workRequired($attributes->{'GENRE'}) ) { @@ -3109,10 +3139,12 @@ sub _postCheckAttributes { my $albumId = $self->_createOrUpdateAlbum($attributes, \%cols, # trackColumns $isCompilation, - $artist->[0], # primary contributor-id + $artist->[0], # primary contributor-id defined $contributors->{'ALBUMARTIST'}->[0] ? 1 : 0, # hasAlbumArtist $create, # create $track, # Track + undef, # basename + $aaDisplayID, ); # Don't add an album to container tracks - See bug 2337 @@ -3120,7 +3152,7 @@ sub _postCheckAttributes { $track->album($albumId); } - $self->_createContributorRoleRelationships($contributors, $trackId, $albumId); + $self->_createContributorRoleRelationships($contributors, $trackId, $albumId, $aaDisplayID, $taDisplayID); # Save any changes - such as album. $track->update; From b7490c9dca1999f3905992c6fb2a23a6429fddfb Mon Sep 17 00:00:00 2001 From: Rouzax Date: Mon, 18 May 2026 19:10:33 +0200 Subject: [PATCH 10/25] Populate contributor_track_display junction for track-level display Signed-off-by: Rouzax --- Slim/Schema.pm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Slim/Schema.pm b/Slim/Schema.pm index 38f259d4c6..b64893c913 100644 --- a/Slim/Schema.pm +++ b/Slim/Schema.pm @@ -3271,7 +3271,7 @@ sub _getOrCreateDisplayContributor { sub _createContributorRoleRelationships { - my ($self, $contributors, $trackId, $albumId, $aaDisplayID) = @_; + my ($self, $contributors, $trackId, $albumId, $aaDisplayID, $taDisplayID) = @_; if (!keys %$contributors) { main::DEBUGLOG && $log->debug('Attempt to set empty contributor set for trackid=', $trackId); @@ -3318,6 +3318,13 @@ sub _createContributorRoleRelationships { (?, ?, ?) } ) if $aaDisplayID; + my $sth_track_display = $self->dbh->prepare_cached( qq{ + REPLACE INTO contributor_track_display + (contributor_display, contributor, track) + VALUES + (?, ?, ?) + } ) if $taDisplayID; + while (my ($role, $contributorList) = each %{$contributors}) { my $roleId = Slim::Schema::Contributor->typeToRole($role); for my $contributor (@{$contributorList}) { @@ -3331,6 +3338,9 @@ sub _createContributorRoleRelationships { if ( $aaDisplayID && $role eq 'ALBUMARTIST' ) { $sth_album_display->execute( $aaDisplayID, $contributor, $albumId ); } + if ( $taDisplayID && ($role eq 'ARTIST' || $role eq 'TRACKARTIST') ) { + $sth_track_display->execute( $taDisplayID, $contributor, $trackId ); + } } } } From 5c64d697fca6f7fc6fca9df48b584d46fd100217 Mon Sep 17 00:00:00 2001 From: Rouzax Date: Mon, 18 May 2026 19:12:20 +0200 Subject: [PATCH 11/25] Add track-level display_artist to queries with aliased JOINs Signed-off-by: Rouzax --- Slim/Control/Queries.pm | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Slim/Control/Queries.pm b/Slim/Control/Queries.pm index 8a818eac84..63ec992211 100644 --- a/Slim/Control/Queries.pm +++ b/Slim/Control/Queries.pm @@ -5797,8 +5797,8 @@ sub _songDataFromHash { $returnHash{$role} = $res->{$role}; } } - # also return contributor_display.name - $returnHash{'display_artist'} = $res->{'contributor_display.name'} if $res->{'contributor_display.name'}; + my $da = $res->{'tcd.name'} || $res->{'acd.name'}; + $returnHash{display_artist} = $da if $da; } elsif ( $tag eq 'S' ) { for my $role ( @contributorRoles ) { @@ -6400,9 +6400,15 @@ sub _getTagDataForTracks { }; my $join_contributor_display = sub { - if ( $sql !~ /JOIN contributor_display/ ) { + if ( $sql !~ /JOIN contributor_display AS acd/ ) { $join_albums->(); - $sql .= 'LEFT JOIN contributor_display ON albums.display_contributor = contributor_display.id '; + $sql .= 'LEFT JOIN contributor_display AS acd ON albums.display_contributor = acd.id '; + } + }; + + my $join_track_contributor_display = sub { + if ( $sql !~ /JOIN contributor_display AS tcd/ ) { + $sql .= 'LEFT JOIN contributor_display AS tcd ON tracks.display_contributor = tcd.id '; } }; @@ -6512,7 +6518,9 @@ sub _getTagDataForTracks { $join_contributors->(); $c->{'contributors.name'} = 1 if $tags =~ /a/; $join_contributor_display->(); - $c->{'contributor_display.name'} = 1; + $join_track_contributor_display->(); + $c->{'acd.name'} = 1; + $c->{'tcd.name'} = 1; # only albums on which the contributor has a specific role? my @roles; @@ -6672,7 +6680,8 @@ sub _getTagDataForTracks { utf8::decode( $c->{'tracks.lyrics'} ) if exists $c->{'tracks.lyrics'}; utf8::decode( $c->{'albums.title'} ) if exists $c->{'albums.title'}; utf8::decode( $c->{'contributors.name'} ) if exists $c->{'contributors.name'}; - utf8::decode( $c->{'contributor_display.name'} ) if exists $c->{'contributor_display.name'}; + utf8::decode( $c->{'acd.name'} ) if exists $c->{'acd.name'}; + utf8::decode( $c->{'tcd.name'} ) if exists $c->{'tcd.name'}; utf8::decode( $c->{'genres.name'} ) if exists $c->{'genres.name'}; utf8::decode( $c->{'comments.value'} ) if exists $c->{'comments.value'}; utf8::decode( $c->{'tracks.discsubtitle'}) if exists $c->{'tracks.discsubtitle'}; From 9414398701c01a0f0392c74d84f3cd1fa297ad1e Mon Sep 17 00:00:00 2001 From: Rouzax Date: Mon, 18 May 2026 19:34:37 +0200 Subject: [PATCH 12/25] Add usePluralArtistTags preference for plural tag contributor creation Signed-off-by: Rouzax --- HTML/EN/settings/server/behavior.html | 9 +++++++++ Slim/Schema.pm | 12 ++++++++++++ Slim/Utils/Prefs.pm | 1 + strings.txt | 12 ++++++++++++ 4 files changed, 34 insertions(+) diff --git a/HTML/EN/settings/server/behavior.html b/HTML/EN/settings/server/behavior.html index f06310cf95..f346c5aca3 100644 --- a/HTML/EN/settings/server/behavior.html +++ b/HTML/EN/settings/server/behavior.html @@ -195,6 +195,15 @@ [% END %] + [% WRAPPER setting title="SETUP_USEPLURALARTISTTAGS" desc="SETUP_USEPLURALARTISTTAGS_DESC"%] + + [% END %] + [% WRAPPER setting title="SETUP_YEARMENU" desc="SETUP_YEARMENU_DESC"%]