-
Notifications
You must be signed in to change notification settings - Fork 361
#1555 - Use plural ALBUMARTISTS / ARTISTS as authoritative source of individual contributors #1557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: public/9.2
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2842,6 +2842,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 TRACKARTISTS | ||
| )) | ||
| { | ||
|
|
||
|
|
@@ -3126,34 +3127,109 @@ sub _mergeAndCreateContributors { | |
| $attributes->{'TRACKARTISTSORT'} = delete $attributes->{'ARTISTSORT'}; | ||
| $attributes->{'MUSICBRAINZ_TRACKARTIST_ID'} = delete $attributes->{'MUSICBRAINZ_ARTIST_ID'} if $attributes->{'MUSICBRAINZ_ARTIST_ID'}; | ||
|
|
||
| # Issue #1555 - carry the plural ARTISTS tag across too, so the | ||
| # per-role plural lookup below finds it under TRACKARTISTS. | ||
| $attributes->{'TRACKARTISTS'} = delete $attributes->{'ARTISTS'} if $attributes->{'ARTISTS'}; | ||
|
|
||
| main::DEBUGLOG && $isDebug && $log->debug(sprintf("-- Contributor '%s' of role 'ARTIST' transformed to role 'TRACKARTIST'", | ||
| $attributes->{'TRACKARTIST'}, | ||
| ref($attributes->{'TRACKARTIST'}) eq 'ARRAY' | ||
| ? join(' / ', @{$attributes->{'TRACKARTIST'}}) | ||
| : $attributes->{'TRACKARTIST'}, | ||
| )); | ||
| } | ||
| } | ||
|
|
||
| my %contributors = (); | ||
|
|
||
| # Issue #1555 - roles for which a plural Picard tag (ARTISTS, | ||
| # ALBUMARTISTS, TRACKARTISTS) is the authoritative individual-contributor | ||
| # list. The singular tag remains the display string; the plural tag, | ||
| # when present as a non-empty arrayref after trimming, supersedes | ||
| # splitList parsing of the singular for contributor-row creation. | ||
| my %pluralRoles = map { $_ => 1 } qw(ARTIST ALBUMARTIST TRACKARTIST); | ||
|
|
||
| for my $tag (Slim::Schema::Contributor->contributorRoles) { | ||
|
|
||
| my $contributor = $attributes->{$tag} || next; | ||
| my $contributor = $attributes->{$tag}; | ||
| my $brainzID = $attributes->{"MUSICBRAINZ_${tag}_ID"}; | ||
| my $sortBy = $attributes->{$tag . 'SORT'}; | ||
| my $usedPlural = 0; | ||
|
|
||
| # Issue #1555 - prefer the plural Picard tag as the individual | ||
| # contributor list when it is a non-empty arrayref with at least | ||
| # one non-blank name. Trim, drop empty-name slots, and drop the | ||
| # corresponding MBID slot so positional alignment is preserved. | ||
| # Empty-string MBID slots (for contributors without an MBID, | ||
| # Picard convention) are kept; Contributor->add's `if ($mbid)` | ||
| # guard handles them. A scalar plural value is treated as "not | ||
| # plural" and falls through to the legacy singular path. | ||
| # | ||
| # Sort alignment: Picard has no standard plural sort tag, but a | ||
| # user script can overwrite the singular ALBUMARTISTSORT / | ||
| # ARTISTSORT tag with a multi-value list aligned to the plural | ||
| # names (using %_albumartists_sort% / %_artists_sort%). Accept | ||
| # that if the sort tag is an arrayref of matching length after | ||
| # empty-slot filtering. Otherwise pass undef so Contributor->add | ||
| # falls back to sort-by-name rather than polluting the first | ||
| # contributor with the joined singular sort string. | ||
| if ($pluralRoles{$tag}) { | ||
| my $plural = $attributes->{$tag . 'S'}; | ||
| if (ref($plural) eq 'ARRAY' && @$plural) { | ||
| my $pluralMbid = $attributes->{"MUSICBRAINZ_${tag}_ID"}; | ||
| my $pluralSort = $attributes->{$tag . 'SORT'}; | ||
| my @mbids = ref($pluralMbid) eq 'ARRAY' ? @$pluralMbid : (); | ||
| my @sorts = ref($pluralSort) eq 'ARRAY' ? @$pluralSort : (); | ||
| my (@names, @alignedMbids, @alignedSorts); | ||
| for (my $i = 0; $i < @$plural; $i++) { | ||
| my $name = $plural->[$i]; | ||
| next unless defined $name; | ||
| $name =~ s/^\s+//; $name =~ s/\s+$//; | ||
| next if $name eq ''; | ||
| push @names, $name; | ||
| push @alignedMbids, $mbids[$i] if @mbids; | ||
| push @alignedSorts, $sorts[$i] if @sorts; | ||
| } | ||
| if (@names) { | ||
| $contributor = \@names; | ||
| $brainzID = @mbids ? \@alignedMbids : $brainzID; | ||
| $sortBy = (@sorts && @alignedSorts == @names) ? \@alignedSorts : undef; | ||
| $usedPlural = 1; | ||
| main::DEBUGLOG && $isDebug && $log->debug(sprintf( | ||
| "-- Using plural %sS (%d entries) as contributor source for role '%s'%s", | ||
| $tag, scalar @names, $tag, | ||
| $sortBy ? ' with aligned sort' : '', | ||
| )); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| # Skip unless we have a source of contributor names for this role. | ||
| # Empty string, undef, or an empty plural array all skip. | ||
| next unless $usedPlural || (defined $contributor && (ref $contributor || $contributor ne '')); | ||
|
|
||
| # Bug 17322, strip leading/trailing spaces from name | ||
| $contributor =~ s/^ +//; | ||
| $contributor =~ s/ +$//; | ||
| # Bug 17322, strip leading/trailing spaces from name. splitTag | ||
| # already trims arrayref elements, so the regex strip is scalar | ||
| # only. | ||
| unless (ref $contributor) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like how some variables can be either a scalar or a list ref. Can't we have separate variables for them? And if they were list refs, wouldn't we have to run the cleanup on all of its items? I see this pattern in other places, like line 3135.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, both fair points. Taking them separately: Scalar-or-arrayref variables. Agreed that it's awkward, but the polymorphism predates this PR: Cleanup on arrayref items. This one is a real gap and I'll fix it in this PR. The existing if (ref $contributor eq 'ARRAY') {
for my $n (@$contributor) {
next unless defined $n;
$n =~ s/^\s+//;
$n =~ s/\s+$//;
}
} else {
$contributor =~ s/^ +//;
$contributor =~ s/ +$//;
}That way the cleanup runs regardless of which path populated Re: the line 3135 reference, that one is a debug-log formatter rather than a cleanup site, so I don't think it needs a behavioural change, but I'll fold it into the follow-up refactor along with the other polymorphism sites. |
||
| $contributor =~ s/^ +//; | ||
| $contributor =~ s/ +$//; | ||
| } | ||
|
|
||
| # Is ARTISTSORT/TSOP always right for non-artist | ||
| # contributors? I think so. ID3 doesn't have | ||
| # "BANDSORT" or similar at any rate. | ||
| push @{ $contributors{$tag} }, Slim::Schema::Contributor->add({ | ||
| 'artist' => $contributor, | ||
| 'brainzID' => $attributes->{"MUSICBRAINZ_${tag}_ID"}, | ||
| 'sortBy' => $attributes->{$tag.'SORT'}, | ||
| 'brainzID' => $brainzID, | ||
| 'sortBy' => $sortBy, | ||
| # only store EXTID for track artist, as we don't have it for other roles | ||
| 'extid' => $tag eq 'ARTIST' && $attributes->{'ARTIST_EXTID'}, | ||
| }); | ||
|
|
||
| main::DEBUGLOG && $isDebug && $log->is_debug && $log->debug(sprintf("-- Track has contributor '$contributor' of role '$tag'")); | ||
| main::DEBUGLOG && $isDebug && $log->is_debug && $log->debug(sprintf( | ||
| "-- Track has contributor '%s' of role '$tag'", | ||
| ref($contributor) eq 'ARRAY' ? join(' / ', @$contributor) : $contributor, | ||
| )); | ||
| } | ||
|
|
||
| # Bug 15553, Primary contributor can only be Album Artist or Artist, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you de-reference these two variables and not just use them directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dereference is there to normalise the shape once so the rest of the loop can treat it as a plain list. Two concrete reasons:
$attributes->{"MUSICBRAINZ_${tag}_ID"}/...SORTcan arrive as a scalar (single MBID from a legacy singular tag), an arrayref (Picard multi-value), or undef, independently of whether the plural names tag is an arrayref. Flattening to@mbids/@sortscollapses all the "not a usable list" cases to an empty array, so$mbids[$i]at line 3189 is safe regardless of input shape.if @mbids(3189) and@mbids ? ... : $brainzID(3194) mean "has at least one element". Using$pluralMbiddirectly has the wrong truthiness: an empty arrayref is truthy, soif ($pluralMbid)would wrongly enter the branch for[]. I'd needref($pluralMbid) eq 'ARRAY' && @$pluralMbidat every use site.If you'd rather keep arrayrefs throughout for consistency with the rest of the function I'm happy to switch, but the guards at every use site would get noisier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PS: I'm signing out for the day and will be away the coming days so won't be able to respond directly