From ce1f3595d14c8939020102af582ff27d569f1ece Mon Sep 17 00:00:00 2001 From: Michael Herger Date: Thu, 4 Jan 2024 08:27:57 +0100 Subject: [PATCH] Remove AllMusic - it's been a long rid. * remove album credits and details, artist details, genre lookup, image lookup * use Last.fm for album reviews, related artists --- AlbumInfo.pm | 131 +-- AllMusic.pm | 779 ------------------ AllMusic/Common.pm | 14 - AllMusic/Sync.pm | 77 -- ArtistInfo.pm | 159 +--- HTML/EN/plugins/MusicArtistInfo/settings.html | 5 - Importer.pm | 75 +- LFM.pm | 63 +- Settings.pm | 2 +- 9 files changed, 89 insertions(+), 1216 deletions(-) delete mode 100644 AllMusic.pm delete mode 100644 AllMusic/Common.pm delete mode 100644 AllMusic/Sync.pm diff --git a/AlbumInfo.pm b/AlbumInfo.pm index 825e8c1..2f79e3e 100644 --- a/AlbumInfo.pm +++ b/AlbumInfo.pm @@ -8,7 +8,6 @@ use Slim::Utils::Strings qw(string cstring); use Slim::Utils::Log; use Plugins::MusicArtistInfo::ArtistInfo; -use Plugins::MusicArtistInfo::AllMusic; use Plugins::MusicArtistInfo::Common qw(CLICOMMAND CAN_IMAGEPROXY); use Plugins::MusicArtistInfo::Discogs; use Plugins::MusicArtistInfo::LFM; @@ -57,17 +56,7 @@ sub getAlbumMenu { my $pt = [$args]; - my $items = [ { - name => cstring($client, 'PLUGIN_MUSICARTISTINFO_ALBUMDETAILS'), - type => 'link', - url => \&getAlbumInfo, - passthrough => $pt, - },{ - name => cstring($client, 'PLUGIN_MUSICARTISTINFO_ALBUMCREDITS'), - type => 'link', - url => \&getAlbumCredits, - passthrough => $pt, - } ]; + my $items = []; if ( !$params->{isButton} ) { unshift @$items, { @@ -104,7 +93,7 @@ sub getAlbumReview { return; } - Plugins::MusicArtistInfo::AllMusic->getAlbumReview($client, + Plugins::MusicArtistInfo::LFM->getAlbumReview($client, sub { my $review = shift; my $items = []; @@ -177,7 +166,7 @@ sub getAlbumCovers { if ( !scalar @$items ) { $items = [{ - name => $covers->{lfm}->{error} || $covers->{allmusic}->{error} || $covers->{discogs}->{error} || cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND'), + name => $covers->{lfm}->{error} || $covers->{discogs}->{error} || cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND'), type => 'text' }]; } @@ -240,12 +229,11 @@ sub getAlbumCoversCLI { my $covers = shift; # only continue once we have results from all services. - return unless $covers->{lfm} && $covers->{allmusic} && $covers->{discogs} && $covers->{musicbrainz}; + return unless $covers->{lfm} && $covers->{discogs} && $covers->{musicbrainz}; my $i = 0; - if ( $covers->{lfm}->{images} || $covers->{allmusic}->{images} || $covers->{discogs}->{images} || $covers->{musicbrainz}->{images} ) { + if ( $covers->{lfm}->{images} || $covers->{discogs}->{images} || $covers->{musicbrainz}->{images} ) { my @covers; - push @covers, @{$covers->{allmusic}->{images}} if ref $covers->{allmusic}->{images} eq 'ARRAY'; push @covers, @{$covers->{lfm}->{images}} if ref $covers->{lfm}->{images} eq 'ARRAY'; push @covers, @{$covers->{discogs}->{images}} if ref $covers->{discogs}->{images} eq 'ARRAY'; push @covers, @{$covers->{musicbrainz}->{images}} if ref $covers->{musicbrainz}->{images} eq 'ARRAY'; @@ -297,11 +285,6 @@ sub getAlbumCoversCLI { $results->{discogs} = {}; } - Plugins::MusicArtistInfo::AllMusic->getAlbumCovers($client, sub { - $results->{allmusic} = shift || {}; - $getAlbumCoversCb->($results); - }, $args); - Plugins::MusicArtistInfo::MusicBrainz->getAlbumCovers($client, sub { $results->{musicbrainz} = shift || {}; $getAlbumCoversCb->($results); @@ -313,110 +296,6 @@ sub getAlbumCoversCLI { }, $args); } -sub getAlbumInfo { - my ($client, $cb, $params, $args) = @_; - - Plugins::MusicArtistInfo::AllMusic->getAlbumDetails($client, - sub { - my $details = shift; - my $items = []; - - if ($details->{error}) { - $items = [{ - name => $details->{error}, - type => 'text' - }] - } - elsif ( $details->{items} ) { - my $colon = cstring($client, 'COLON'); - - $items = [ map { - my ($k, $v) = each %{$_}; - - ref $v eq 'ARRAY' ? { - name => $k, - type => 'outline', - items => [ map { - my $item = { - name => $_, - type => 'text' - }; - - if ( $k =~ /genre|style/i && (my ($genre) = Slim::Schema->rs('Genre')->search( namesearch => Slim::Utils::Text::ignoreCaseArticles($_, 1, 1) )) ) { - $item->{type} = 'link'; - $item->{url} = \&Slim::Menu::BrowseLibrary::_artists; - $item->{passthrough} = [{ - searchTags => ["genre_id:" . $genre->id] - }]; - } - - $item; - } @$v ], - }:{ - name => "$k$colon $v", - type => 'text' - } - } @{$details->{items}} ]; - - main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump($items)); - } - - $cb->($items); - }, - $args, - ); -} - -sub getAlbumCredits { - my ($client, $cb, $params, $args) = @_; - - Plugins::MusicArtistInfo::AllMusic->getAlbumCredits($client, - sub { - my $credits = shift || {}; - - my $items = []; - - if ($credits->{error}) { - $items = [{ - name => $credits->{error}, - type => 'text' - }] - } - elsif ( $credits->{items} ) { - $items = [ map { - my $name = $_->{name}; - - if ($_->{credit}) { - $name .= cstring($client, 'COLON') . ' ' . $_->{credit}; - } - - my $item = { - name => $name, - type => 'text', - }; - - if ($_->{url} || $_->{id}) { - $item->{url} = \&Plugins::MusicArtistInfo::ArtistInfo::getArtistMenu; - $item->{passthrough} = [{ - url => $_->{url}, - id => $_->{id}, - name => $_->{name} - }]; - $item->{type} = 'link'; - } - - $item; - } @{$credits->{items}} ] if $credits->{items}; - - main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump($items)); - } - - $cb->($items); - }, - $args, - ); -} - sub getAlbumReviewCLI { my $request = shift; diff --git a/AllMusic.pm b/AllMusic.pm deleted file mode 100644 index 2566b97..0000000 --- a/AllMusic.pm +++ /dev/null @@ -1,779 +0,0 @@ -package Plugins::MusicArtistInfo::AllMusic; - -use strict; -use File::Spec::Functions qw(catdir); -use JSON::XS::VersionOneAndTwo; - -use Encode; -use FindBin qw($Bin); -use lib catdir($Bin, 'Plugins', 'MusicArtistInfo', 'lib'); -use HTML::Entities; -use HTML::TreeBuilder; -use URI::Escape; - -use Slim::Networking::SimpleAsyncHTTP; -use Slim::Utils::Log; -use Slim::Utils::Strings qw(string cstring); - -use Plugins::MusicArtistInfo::AllMusic::Common qw(BASE_URL ALBUMSEARCH_URL); - -use constant SEARCH_URL => BASE_URL . 'search/typeahead/all/%s'; -use constant ARTISTSEARCH_URL => BASE_URL . 'search/artists/%s'; -use constant ARTIST_URL => BASE_URL . 'artist/%s'; -use constant BIOGRAPHY_URL => BASE_URL . 'artist/%s/biographyAjax'; -use constant RELATED_URL => BASE_URL . 'artist/%s/relatedArtistsAjax'; -use constant ALBUMREVIEW_URL => BASE_URL . 'album/%s/reviewAjax'; -use constant ALBUMDETAILS_URL => BASE_URL . 'album/%s'; -use constant ALBUMCREDITS_URL => BASE_URL . ALBUMREVIEW_URL . '/creditsAjax'; - -my $log = logger('plugin.musicartistinfo'); - -sub getBiography { - my ( $class, $client, $cb, $args ) = @_; - - my $getBiographyCB = sub { - my $args = shift; - my $url = _getBioUrl($args) || return _nothingFound($client, $cb); - my $referer = $args->{url}; - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = {}; - - main::DEBUGLOG && $log->is_debug && $tree->dump; - - if ( my $bio = $tree->look_down('_tag', 'div', 'id', 'biography') ) { - main::DEBUGLOG && $log->is_debug && $log->debug('Found biography - parsing'); - - $bio = _cleanupLinksAndImages($bio); - - $result->{bio} = _decodeHTML($bio->as_HTML); - $result->{bio} =~ s/\bh3\b/h4/ig; - $result->{bio} =~ s/"headline"//g; - - $result->{bioText} = join('\n\n', map { - $_->as_trimmed_text; - } $bio->content_list); - - if (Slim::Utils::Unicode::looks_like_utf8($result->{bioText})) { - $result->{bioText} = Encode::encode( 'utf8', $result->{bioText} ); - }; - - $result->{bio} || $log->warn('Failed to find biography for ' . $url); - } - - return $result; - } - }, ['referer', $referer] ); - }; - - if ( $args->{url} || $args->{id} ) { - $getBiographyCB->( $args ); - } - else { - $class->getArtist($client, sub { - $getBiographyCB->( shift ); - }, $args); - } -} - -sub getArtistPhotos { - my ( $class, $client, $cb, $args ) = @_; - - my $getArtistPhotosCB = sub { - my $url = _getArtistUrl(shift) || return _nothingFound($client, $cb); - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - - my $result = []; - - if ( my $imageContainer = $tree->look_down('_tag', 'aside') ) { - my $images = eval { - my ($script) = map { /(\[.*\])/sg; $1; } grep { $_ } $imageContainer->look_down('_tag', 'script')->content_list; - from_json($script); - }; - - if ( $@ ) { - return { error => $@ }; - } - elsif ( $images && ref $images ) { - $result = [ map { - { - author => $_->{author} || $_->{copyrightOwner} || 'AllMusic.com', - url => _makeLinkAbsolute($_->{zoomURL} || $_->{url}), - height => $_->{zoomURL} ? undef : $_->{height}, - width => $_->{zoomURL} ? undef : $_->{width}, - } - } @$images ]; - } - } - - return { - photos => $result - }; - } - } ); - }; - - if ( $args->{url} || $args->{id} ) { - $getArtistPhotosCB->( $args ); - } - else { - $class->getArtist($client, sub { - $getArtistPhotosCB->( shift ); - }, $args); - } -} - -sub getArtistDetails { - my ( $class, $client, $cb, $args ) = @_; - - my $getArtistDetailsCB = sub { - my $url = _getArtistUrl(shift) || return _nothingFound($client, $cb); - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = []; - - my $details = $tree->look_down('_tag', 'div', 'id', 'basicInfoMeta'); - - foreach ( 'activeDates', 'birth', 'genre', 'styles', 'group-members', 'aliases', 'member-of' ) { - if ( my $item = $details->look_down('_tag', 'div', 'class', $_) ) { - my $title = $item->look_down('_tag', 'h4'); - my $value = $item->look_down('_tag', 'div', 'class', undef); - - next unless $title && $value; - - if ( /genre|styles/ ) { - # XXX - link to genre/artist pages? - my $values = []; - foreach ( $value->look_down('_tag', 'a') ) { - push @$values, Encode::decode('utf8', $_->as_trimmed_text); - } - - $value = $values if scalar @$values; - } - elsif ( /member/ ) { - my $values = []; - foreach ( $value->look_down('_tag', 'a') ) { - push @$values, { - HTML::Entities::decode(Encode::decode('utf8', $_->as_trimmed_text)) => $_->attr('href') - }; - } - - $value = $values if scalar @$values; - } - elsif ( /aliases/ ) { - my $values = []; - foreach ( $value->look_down('_tag', 'div', sub { !$_[0]->descendents }) ) { - push @$values, Encode::decode('utf8', $_->as_trimmed_text); - } - - $value = $values if scalar @$values; - } - - push @$result, { - Encode::decode('utf8', $title->as_trimmed_text) => ref $value eq 'ARRAY' ? $value : Encode::decode('utf8', $value->as_trimmed_text), - } if $title && $value; - } - } - - return { - items => $result - }; - } - } ); - }; - - if ( $args->{url} || $args->{id} ) { - $getArtistDetailsCB->( $args ); - } - else { - $class->getArtist($client, sub { - $getArtistDetailsCB->( shift ); - }, $args); - } -} - -sub getRelatedArtists { - my ( $class, $client, $cb, $args ) = @_; - - my $getRelatedArtistsCB = sub { - my $url = _getSomeUrl(shift, '/relatedArtistsAjax', RELATED_URL) || return _nothingFound($client, $cb); - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = []; - - foreach ( 'similars', 'influencers', 'followers', 'associatedwith', 'collaboratorwith' ) { - my $related = $tree->look_down('_tag', 'div', 'class', "related $_ clearfix") || next; - my $title = $related->look_down('_tag', 'h2') || next; - - push @$result, { - $title->as_trimmed_text => [ map { - _parseArtistInfo($_); - } $related->look_down("_tag", "a") ] - }; - } - - return { - items => $result - }; - } - } ); - }; - - if ( $args->{url} || $args->{id} ) { - $getRelatedArtistsCB->( $args ); - } - else { - $class->getArtist($client, sub { - $getRelatedArtistsCB->( shift ); - }, $args); - } -} - -sub getArtist { - my ( $class, $client, $cb, $args ) = @_; - - my $artist = Slim::Utils::Text::ignoreCaseArticles($args->{artist}, 1); - my $artistLC = lc($args->{artist}); - - if (!$artist) { - $cb->(); - return; - } - - my $artist2 = $args->{artist}; - $artist2 =~ s/&/and/g; - $artist2 = Slim::Utils::Text::ignoreCaseArticles($artist2, 1); - - $class->searchArtists($client, sub { - my $items = shift; - - if (!$items || ref $items ne 'ARRAY') { - $cb->(); - return; - } - - my $artistInfo; - - foreach (@$items) { - my $current = lc( Slim::Utils::Unicode::utf8decode($_->{name}) ); - - # immediately stop if names are identical - if ( $current eq $artist || $current eq $artistLC || $current eq $artist2 ) { - $artistInfo = $_; - last; - } - - $current = Slim::Utils::Text::ignoreCaseArticles($current, 1); - - # alternatively pick first to partially match the name - if ( !$artistInfo && $current =~ /(\Q$artist\E|\Q$artist2\E)/i ) { - $artistInfo = $_; - } - } - - $cb->($artistInfo); - }, $args) -} - -sub searchArtists { - my ( $class, $client, $cb, $args ) = @_; - - my $url = sprintf(ARTISTSEARCH_URL, URI::Escape::uri_escape_utf8($args->{artist})); - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = []; - - my $results = $tree->look_down("_tag" => "div", "id" => "resultsContainer"); - - return $result unless $results; - - foreach ($results->content_list) { - my $artist = $_->look_down('_tag', 'div', 'class', 'name') || next; - - my $artistData = _parseArtistInfo($artist); - - push @$result, $artistData if $artistData->{url}; - } - - return $result; - } - } ); -} - -sub getAlbumReview { - my ( $class, $client, $cb, $args ) = @_; - - my $getAlbumReviewCB = sub { - my $args = shift; - my $url = _getAlbumReviewUrl($args) || return _nothingFound($client, $cb); - my $referer = $args->{url}; - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = {}; - - main::DEBUGLOG && $log->is_debug && $tree->dump; - - if ( my $review = $tree->look_down('_tag', 'div', 'id', 'review') ) { - main::DEBUGLOG && $log->is_debug && $log->debug('Found reviewBody - parsing'); - - $review = _cleanupLinksAndImages($review); - - $result->{review} = _decodeHTML($review->as_HTML); - $result->{reviewText} = Encode::decode( 'utf8', join('\n\n', map { - $_->as_trimmed_text; - } $review->content_list) ); - } - - return $result; - } - }, ['referer', $referer] ); - }; - - if ( $args->{url} || $args->{id} ) { - $getAlbumReviewCB->( $args ); - } - else { - $class->getAlbum($client, sub { - $getAlbumReviewCB->( shift ); - }, $args); - } -} - -sub getAlbumDetails { - my ( $class, $client, $cb, $args ) = @_; - - my $getAlbumDetailsCB = sub { - my $url = _getAlbumDetailsUrl(shift) || return _nothingFound($client, $cb); - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = []; - - if ( my $details = $tree->look_down('_tag', 'div', 'id', 'basicInfoMeta') ) { - foreach ( 'release-date', 'recording-date', 'duration', 'genre', 'styles', 'recording-location' ) { - if ( my $item = $details->look_down('_tag', 'div', 'class', $_) ) { - my $title = $item->look_down('_tag', 'h4'); - my $value = $item->look_down('_tag', 'span'); - my $values = $item->look_down('_tag', 'div', 'class', undef); - - next unless $title && ($value || $values); - $title = $title->as_trimmed_text; - - if ( $values && /genre|styles/ ) { - # XXX - link to genre/artist pages? - my @values; - foreach ( $values->look_down('_tag', 'a') ) { - push @values, $_->as_trimmed_text; - } - - $value = \@values if scalar @values; - } - elsif ( $values && /location/ ) { - my @values; - foreach ( $item->look_down('_tag', 'div', 'class', undef) ) { - push @values, $_->as_trimmed_text; - } - $value = join(' & ', @values) if scalar @values; - } - elsif ($values) { - $value = $values->look_down('_tag', 'div', 'class', undef)->as_trimmed_text; - } - elsif ($value) { - $value = $value->as_trimmed_text; - } - - push @$result, { - $title => $value, - } if $title && $value; - } - } - } - -=pod - if ( my $item = $tree->look_down('_tag', 'section', 'class', 'moods') ) { - my $title = $item->look_down('_tag', 'h4'); - my $value = $item->look_down('_tag', 'div', 'class', undef); - - if ( $title && $value ) { - my $values = []; - foreach ( $value->look_down('_tag', 'a') ) { - push @$values, $_->as_trimmed_text; - } - - $value = $values if scalar @$values; - } - - push @$result, { - $title->as_trimmed_text => ref $value eq 'ARRAY' ? $value : $value->as_trimmed_text, - } if $title && $value; - } -=cut - - return { - items => $result - }; - } - } ); - }; - - if ( $args->{url} || $args->{id} ) { - $getAlbumDetailsCB->( $args ); - } - else { - $class->getAlbum($client, sub { - $getAlbumDetailsCB->( shift ); - }, $args); - } -} - -sub getAlbumCovers { - my ( $class, $client, $cb, $args ) = @_; - - my $getAlbumCoverCB = sub { - my $url = _getAlbumDetailsUrl(shift) || return _nothingFound($client, $cb); - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = {}; - - #main::DEBUGLOG && $log->is_debug && $tree->dump; - - if ( my $cover = $tree->look_down('_tag', 'aside') ) { - #main::DEBUGLOG && $log->is_debug && $cover->dump; - - my $images = eval { - my ($script) = map { /(\[.*\])/sg; $1; } grep { $_ } $cover->look_down('_tag', 'script')->content_list; - from_json($script); - }; - - if ( $@ ) { - $result->{error} = $@; - } - elsif ( $images && ref $images ) { - $result->{images} = [ map { - { - author => $_->{author} || $_->{copyrightOwner} || 'AllMusic.com', - url => _makeLinkAbsolute($_->{zoomURL} || $_->{url}), - height => $_->{zoomURL} ? undef : $_->{height}, - width => $_->{zoomURL} ? undef : $_->{width}, - } - } @$images ]; - } - } - - if ( !$result->{images} ) { - $result->{error} ||= cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND'); - } - - return $result; - } - } ); - }; - - if ( $args->{url} || $args->{id} ) { - $getAlbumCoverCB->( $args ); - } - else { - $class->getAlbum($client, sub { - $getAlbumCoverCB->( shift ); - }, $args); - } -} - -sub getAlbumCredits { - my ( $class, $client, $cb, $args ) = @_; - - my $getAlbumDetailsCB = sub { - my $url = _getSomeUrl(shift, '/creditsAjax', ALBUMCREDITS_URL) || return _nothingFound($client, $cb); - my $referer = $url; - $referer =~ s|/creditsAjax||; - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = []; - - if ( my $credits = $tree->look_down('_tag', 'table', 'class', 'creditsTable') ) { - foreach ($credits->look_down('_tag', 'tr')) { - my $artist = $_->look_down('_tag', 'td', 'class', 'singleCredit') || next; - - my $artistData = _parseArtistInfo($artist->look_down('_tag', 'span', 'class', 'artist')); - - if ( my $credit = $artist->look_down('_tag', 'span', 'class', 'artistCredits') ) { - $artistData->{credit} = $credit->as_trimmed_text; - } - - push @$result, $artistData if $artistData->{name}; - } - } - - return { - items => [ sort { $a->{name} cmp $b->{name} } @$result ] - }; - } - }, ['referer', $referer] ); - }; - - if ( $args->{url} || $args->{id} ) { - $getAlbumDetailsCB->( $args ); - } - else { - $class->getAlbum($client, sub { - $getAlbumDetailsCB->( shift ); - }, $args); - } -} - -sub getAlbum { - my ( $class, $client, $cb, $args ) = @_; - - if (!$args->{artist} || !$args->{album}) { - $cb->(); - return; - } - - $class->searchAlbums($client, sub { - my $items = shift; - - if (!$items || ref $items ne 'ARRAY') { - $cb->(); - return; - } - - my $albumInfo; - - foreach (@$items) { - my $candidate = { - artist => $_->{artist}->{name}, - album => $_->{name} - }; - - if (Plugins::MusicArtistInfo::Common->matchAlbum($args, $candidate, 'strict')) { - $albumInfo = $_; - last; - } - - if ( !$albumInfo && Plugins::MusicArtistInfo::Common->matchAlbum($args, $candidate)) { - $albumInfo = $_; - } - } - - $cb->($albumInfo); - }, $args) -} - -sub searchAlbums { - my ( $class, $client, $cb, $args ) = @_; - - my $url = sprintf( - ALBUMSEARCH_URL, - URI::Escape::uri_escape_utf8($args->{artist}), - URI::Escape::uri_escape_utf8($args->{album}) - ); - - _get( $client, $cb, { - url => $url, - parseCB => sub { - my $tree = shift; - my $result = []; - - foreach ( $tree->look_down("_tag" => "div", "class" => "album") ) { - my $title = $_->look_down('_tag', 'div', 'class', 'title') || next; - my $url = $title->look_down('_tag', 'a') || next; - my $artist = $_->look_down('_tag', 'div', 'class', 'artist') || next; - - my $albumData = { - name => $title->as_text, - url => $url->attr('href'), - artist => _parseArtistInfo($artist), - }; - - if ( my $year = $_->look_down('_tag', 'div', 'class', 'year') ) { - $albumData->{year} = $year->as_text + 0; - } - - push @$result, $albumData; - } - - return $result; - } - } ); -} - -sub _cleanupLinksAndImages { - my $tree = shift; - - # clean up links and images - foreach ( $tree->look_down('_tag', 'a') ) { - # make external links absolute - $_->attr('href', _makeLinkAbsolute($_->attr('href'))); - # open links in new window - $_->attr('target', 'allmusic'); - } - - foreach ( $tree->look_down('_tag', 'img', 'loading', 'lazy') ) { - $_->attr('onerror', "this.style.display='none'"); - $_->attr('style', 'display:block;margin:"10px 0;'); - } - - return $tree; -} - -sub _makeLinkAbsolute { - my $src = shift; - - if ($src !~ /^http/) { - $src =~ s/^\///; # remove leading slash to prevent double slashes - $src = BASE_URL . $src; - } - - return $src; -} - -sub _parseArtistInfo { - my $data = shift; - - my $artistInfo = { - name => Encode::decode('utf8', $data->as_text), - }; - - if ( my $url = $data->look_down('_tag', 'a') ) { - $artistInfo->{url} = _makeLinkAbsolute($url->attr('href')); - - my $id = _getIdFromUrl($artistInfo->{url}); - $artistInfo->{id} = $id if $id; - } - - if ( my $decades = $_->look_down('_tag', 'div', 'class', 'decades') ) { - $artistInfo->{decades} = $decades->as_trimmed_text; - } - - return $artistInfo; -} - -sub _getArtistUrl { _getSomeUrl(shift, '', ARTIST_URL) } - -sub _getBioUrl { _getSomeUrl(shift, '/biographyAjax', BIOGRAPHY_URL) } - -sub _getAlbumReviewUrl { _getSomeUrl(shift, '/reviewAjax', ALBUMREVIEW_URL) } - -sub _getAlbumDetailsUrl { _getSomeUrl(shift, '', ALBUMREVIEW_URL) } - -sub _getSomeUrl { - my ($data, $suffix, $template) = @_; - return unless $data->{url} || $data->{id}; - return $data->{url} - ? ($data->{url} . ($suffix || '')) - : sprintf($template, $data->{id}); -} - -sub _getIdFromUrl { - $_[0] =~ /\b(\w+)$/; - return $1; -} - -sub _decodeHTML { - my $content = HTML::Entities::decode(shift); - - if (Slim::Utils::Unicode::looks_like_utf8($content)) { - $content = Encode::decode('utf8', $content); - } - - return $content; -} - -sub _nothingFound { - my ($client, $cb) = @_; - $cb->({ error => cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND') }) -} - -sub _get { - my ( $client, $cb, $args, $headers ) = @_; - - main::INFOLOG && $log->info('Getting ' . $args->{url}); - - my $url = $args->{url} || return; - my $parseCB = $args->{parseCB}; - - Slim::Networking::SimpleAsyncHTTP->new( - sub { - my $response = shift; - - my $result; - my $error; - - main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump($response->content)); - - if ( $response->headers->content_type =~ /html/ && $response->content ) { - my $tree = HTML::TreeBuilder->new; - $tree->ignore_unknown(0); # allmusic.com uses unknown "section" tag - $tree->parse_content( $response->content ); - - $result = $parseCB->($tree) if $parseCB; - } - - if (!$result) { - $result = { error => 'Error: Invalid data' }; - $log->error($result->{error}); - } - - main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump($result)); - - $cb->($result); - }, - sub { - my $response = shift; - my $error = shift || ''; - - my $item = { - error => 'Unknown error', - }; - - if ($response->code == 404 || $error =~ /404/) { - $item = { - error => cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND'), - }; - } - else { - main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump($response)); - - $log->warn("error: $error"); - $item = { error => 'Unknown error: ' . $error } - } - - $cb->($item); - }, - { - client => $client, - cache => 1, - expires => 86400, # set expiration, as allmusic doesn't provide it - timeout => 15, - }, - )->get($url, $headers ? @$headers : undef); -} - -1; \ No newline at end of file diff --git a/AllMusic/Common.pm b/AllMusic/Common.pm deleted file mode 100644 index 6b9cada..0000000 --- a/AllMusic/Common.pm +++ /dev/null @@ -1,14 +0,0 @@ -package Plugins::MusicArtistInfo::AllMusic::Common; - -use strict; -use Exporter::Lite; - -BEGIN { - use constant BASE_URL => 'http://www.allmusic.com/'; - use constant ALBUMSEARCH_URL => BASE_URL . 'search/albums/%s%%2C%%20%s'; - - use Exporter::Lite; - our @EXPORT_OK = qw( BASE_URL ALBUMSEARCH_URL ); -} - -1; \ No newline at end of file diff --git a/AllMusic/Sync.pm b/AllMusic/Sync.pm deleted file mode 100644 index 4a22bd0..0000000 --- a/AllMusic/Sync.pm +++ /dev/null @@ -1,77 +0,0 @@ -package Plugins::MusicArtistInfo::AllMusic::Sync; - -use strict; - -use Slim::Networking::SimpleSyncHTTP; -use Slim::Utils::Cache; - -use Plugins::MusicArtistInfo::AllMusic::Common qw(BASE_URL ALBUMSEARCH_URL); -use Plugins::MusicArtistInfo::Common; - -my $cache = Slim::Utils::Cache->new(); - -my $http = Slim::Networking::SimpleSyncHTTP->new({ - expires => 86399, - cache => 1 -}); - -sub searchAlbums { - my ($class, $args) = @_; - - my $url = sprintf( - ALBUMSEARCH_URL, - URI::Escape::uri_escape_utf8($args->{artist}), - URI::Escape::uri_escape_utf8($args->{album}) - ); - - my $result = $http->get($url); - my $bodyRef = $result->contentRef; - - my $albums = []; - - while ($$bodyRef =~ m{
(.*?
.*).*?>\s*(.*?)\s*{album} = $1; - } - if ($snippet =~ m{class="artist">.*?>\s*(.*?)\s*{artist} = $1; - } - if ($snippet =~ m{"year">\s*?(\d+)}gs) { - $album->{year} = $1; - } - if ($snippet =~ m{class="genres">\s*(.*?)\s*{genres} = $1; - } - - push @$albums, $album if keys %$album; - } - - return $albums; -} - -sub getAlbumInfo { - my ($class, $args) = @_; - - my $cacheKey = 'mai_albuminfo_' . Slim::Utils::Text::ignoreCaseArticles($args->{artist}, 1) . '|' . Slim::Utils::Text::ignoreCaseArticles($args->{album}, 1); - - if (my $cached = $cache->get($cacheKey)) { - return $cached; - } - - my $albums = $class->searchAlbums($args); - my ($album) = grep { - Plugins::MusicArtistInfo::Common->matchAlbum($args, $_, 'strict') - || Plugins::MusicArtistInfo::Common->matchAlbum($args, $_); - } @$albums; - - if ($album) { - $cache->set($cacheKey, $album, time() + 90 * 86400 + rand(7) * 86400); - } - - return $album; -} - -1; \ No newline at end of file diff --git a/ArtistInfo.pm b/ArtistInfo.pm index 0a97c47..6f63e3d 100644 --- a/ArtistInfo.pm +++ b/ArtistInfo.pm @@ -9,7 +9,6 @@ use Slim::Menu::GlobalSearch; use Slim::Utils::Strings qw(string cstring); use Slim::Utils::Log; -use Plugins::MusicArtistInfo::AllMusic; use Plugins::MusicArtistInfo::Common qw(CLICOMMAND CAN_IMAGEPROXY); use Plugins::MusicArtistInfo::Discogs; use Plugins::MusicArtistInfo::LFM; @@ -89,11 +88,6 @@ sub getArtistMenu { my $pt = [$args]; my $items = [ { - name => cstring($client, 'PLUGIN_MUSICARTISTINFO_ARTISTDETAILS'), - type => 'link', - url => \&getArtistInfo, - passthrough => $pt, - },{ name => cstring($client, 'PLUGIN_MUSICARTISTINFO_RELATED_ARTISTS'), type => 'link', url => \&getRelatedArtists, @@ -151,39 +145,21 @@ sub getBiography { $args->{lang} ||= cstring($client, 'PLUGIN_MUSICARTISTINFO_LASTFM_LANGUAGE'); - # prefer AllMusic.com if language is EN - it's richer than Last.fm - if ($args->{lang} eq 'en') { - Plugins::MusicArtistInfo::AllMusic->getBiography($client, sub { - $cb->(_getBioItems(shift, $client, $params)); - }, $args); - } - else { - Plugins::MusicArtistInfo::LFM->getBiography($client, sub { - my $bio = shift; - - if ($bio->{error} || !$bio->{bio}) { - # in case of error or lack of Bio, try to fall back to English - delete $args->{lang}; + Plugins::MusicArtistInfo::LFM->getBiography($client, sub { + my $bio = shift; - Plugins::MusicArtistInfo::LFM->getBiography($client, sub { - $bio = shift; + if ($bio->{error} || !$bio->{bio}) { + # in case of error or lack of Bio, try to fall back to English + delete $args->{lang}; - if ($bio->{error} || !$bio->{bio}) { - # fall back to AllMusic - Plugins::MusicArtistInfo::AllMusic->getBiography($client, sub { - $cb->(_getBioItems(shift, $client, $params)); - }, $args); - } - else { - $cb->(_getBioItems($bio, $client, $params)); - } - }, $args); - } - else { - $cb->(_getBioItems($bio, $client, $params)); - } - }, $args); - } + Plugins::MusicArtistInfo::LFM->getBiography($client, sub { + $cb->(_getBioItems($_[0], $client, $params)); + }, $args); + } + else { + $cb->(_getBioItems($bio, $client, $params)); + } + }, $args); } sub _getBioItems { @@ -344,7 +320,7 @@ sub getArtistPhotosCLI { sub _getArtistPhotos { my ($client, $artist, $artist_id, $cb, $services) = @_; - $services ||= [ 'local', 'allmusic', 'discogs', 'lfm' ]; + $services ||= [ 'local', 'discogs', 'lfm' ]; my $results = {}; @@ -384,11 +360,6 @@ sub _getArtistPhotos { $_ => 1 } @$services; - Plugins::MusicArtistInfo::AllMusic->getArtistPhotos($client, sub { - $results->{allmusic} = shift; - $gotArtistPhotosCb->($results); - }, $args ) if $services{allmusic}; - Plugins::MusicArtistInfo::LFM->getArtistPhotos($client, sub { $results->{lfm} = shift || { photos => [] }; $gotArtistPhotosCb->($results); @@ -465,85 +436,10 @@ sub getArtistPhotoCLI { $request->setStatusProcessing(); } -sub getArtistInfo { - my ($client, $cb, $params, $args) = @_; - - Plugins::MusicArtistInfo::AllMusic->getArtistDetails($client, - sub { - my $details = shift; - my $items = []; - - if ($details->{error}) { - $items = [{ - name => $details->{error}, - type => 'text' - }] - } - elsif ( $details->{items} ) { - my $colon = cstring($client, 'COLON'); - - $items = [ map { - my ($k, $v) = each %{$_}; - - ref $v eq 'ARRAY' ? { - name => $k, - type => 'outline', - items => [ map { - if (ref $_ eq 'HASH') { - my ($k, $v) = each %$_; - { - name => $k, - type => 'link', - url => \&getArtistMenu, - passthrough => [{ - url => $v, - name => $k - }] - } - } - else { - my $item = { - name => $_, - type => 'text' - }; - - if ( $k =~ /genre|style/i && (my ($genre) = Slim::Schema->rs('Genre')->search( namesearch => Slim::Utils::Text::ignoreCaseArticles($_, 1, 1) )) ) { - $item->{type} = 'link'; - $item->{url} = \&Slim::Menu::BrowseLibrary::_artists; - $item->{passthrough} = [{ - searchTags => ["genre_id:" . $genre->id] - }]; - } - elsif ( $k =~ /also known as/i ) { - $item->{type} = 'link'; - $item->{url} = \&_getSearchItem; - $item->{passthrough} = [{ - search => $_ - }]; - } - - $item; - } - } @$v ], - }:{ - name => "$k$colon $v", - type => 'text' - } - } @{$details->{items}} ]; - - main::DEBUGLOG && $log->is_debug && $log->debug(Data::Dump::dump($items)); - } - - $cb->($items); - }, - $args, - ); -} - sub getRelatedArtists { my ($client, $cb, $params, $args) = @_; - Plugins::MusicArtistInfo::AllMusic->getRelatedArtists($client, + Plugins::MusicArtistInfo::LFM->getRelatedArtists($client, sub { my $relations = shift; my $items = []; @@ -556,23 +452,14 @@ sub getRelatedArtists { } elsif ( $relations->{items} ) { $items = [ map { - my ($k, $v) = each %{$_}; - { - name => $k, - type => 'outline', - items => [ map { - { - name => $_->{name}, - type => 'link', - url => \&getArtistMenu, - passthrough => [{ - url => $_->{url}, - id => $_->{id}, - name => $_->{name} - }] - } - } @$v ], + name => $_->{name}, + type => 'link', + url => \&getArtistMenu, + passthrough => [{ + url => $_->{url}, + name => $_->{name} + }] } } @{$relations->{items}} ]; } @@ -849,7 +736,7 @@ sub _artworkUrl { if (CAN_IMAGEPROXY) { $cb->($url); # we don't use discogs here, as we easily get rate limited - }, [ 'allmusic', 'lfm' ]); + }, [ 'lfm' ]); return; } } diff --git a/HTML/EN/plugins/MusicArtistInfo/settings.html b/HTML/EN/plugins/MusicArtistInfo/settings.html index 860d17b..667c241 100644 --- a/HTML/EN/plugins/MusicArtistInfo/settings.html +++ b/HTML/EN/plugins/MusicArtistInfo/settings.html @@ -12,11 +12,6 @@ [% END %] - [% IF canOnlineLibrary; WRAPPER settingGroup title="" desc="PLUGIN_MUSICARTISTINFO_IMPORTER_GENRES_DESC" %] - - - [% END; END %] - [% WRAPPER settingGroup title="" desc="PLUGIN_MUSICARTISTINFO_IMPORTER_COVERART_DESC" %] diff --git a/Importer.pm b/Importer.pm index 86c1745..2a742b2 100644 --- a/Importer.pm +++ b/Importer.pm @@ -25,7 +25,7 @@ my $serverprefs = preferences('server'); sub initPlugin { my $class = shift; - return unless $prefs->get('runImporter') && ($serverprefs->get('precacheArtwork') || $prefs->get('lookupArtistPictures') || $prefs->get('lookupCoverArt') || $prefs->get('replaceOnlineGenres')); + return unless $prefs->get('runImporter') && ($serverprefs->get('precacheArtwork') || $prefs->get('lookupArtistPictures') || $prefs->get('lookupCoverArt')); Slim::Music::Import->addImporter($class, { 'type' => 'post', @@ -42,7 +42,6 @@ sub startScan { my $class = shift; $class->_scanAlbumCovers(); - $class->_scanAlbumGenre() if CAN_ONLINE_LIBRARY && $prefs->get('replaceOnlineGenres'); if (CAN_IMAGEPROXY && $prefs->get('lookupArtistPictures')) { require Plugins::MusicArtistInfo::Importer2; @@ -245,78 +244,6 @@ sub _setAlbumCover { } } -sub _scanAlbumGenre { if (CAN_ONLINE_LIBRARY) { - my $class = shift; - - require Plugins::MusicArtistInfo::AllMusic::Sync; - - my $extIdCondition = join(' OR ', map { - "albums.extid LIKE '$_%'"; - } @{GENRE_REPLACE_ID()}); - - my $dbh = Slim::Schema->dbh or return; - my $sth = $dbh->prepare_cached("SELECT COUNT(1) FROM albums WHERE $extIdCondition;"); - $sth->execute(); - my ($count) = $sth->fetchrow_array; - $sth->finish; - - my $progress = Slim::Utils::Progress->new({ - 'type' => 'importer', - 'name' => 'plugin_musicartistinfo_genre_replacement', - 'total' => $count, - 'bar' => 1, - }); - - my $sql = qq(SELECT albums.id, albums.title, contributors.name - FROM albums JOIN contributors ON contributors.id = albums.contributor - WHERE $extIdCondition;); - - my ($albumId, $title, $name); - - $sth = $dbh->prepare_cached($sql); - $sth->execute(); - $sth->bind_columns(\$albumId, \$title, \$name); - - my $mappings = {}; - my $selectSQL = q(SELECT tracks.id - FROM tracks - WHERE tracks.album = ? AND tracks.extid IS NOT NULL;); - - my $trackId; - my $tracks_sth = $dbh->prepare_cached($selectSQL); - $tracks_sth->bind_columns(\$trackId); - - while ( $sth->fetch ) { - utf8::decode($title); - utf8::decode($name); - - $progress->update(sprintf('%s - %s', $title, $name)); - Slim::Schema->forceCommit; - - my $albumInfo = Plugins::MusicArtistInfo::AllMusic::Sync->getAlbumInfo({ - artist => $name, - album => $title - }) || {}; - - if (my $genreName = $albumInfo->{genres}) { - $tracks_sth->execute($albumId); - - while ($tracks_sth->fetch) { - foreach (split /,\s*/, $genreName) { - Slim::Schema::Genre->add($_, $trackId + 0); - } - } - } - } - - if ($progress) { - $progress->final($count) ; - $log->error(sprintf(' finished in %.3f seconds', $progress->duration)); - } - - Slim::Schema->forceCommit; -} } - sub _cleanupEmbeddedImagesFolder { require Plugins::MusicArtistInfo::LocalArtwork; Plugins::MusicArtistInfo::LocalArtwork->init(); diff --git a/LFM.pm b/LFM.pm index 298f58e..f9a3486 100644 --- a/LFM.pm +++ b/LFM.pm @@ -48,15 +48,21 @@ sub getLargestPhotoFromList { return $url; } -sub getBiography { - my ( $class, $client, $cb, $args ) = @_; +sub _getArtistInfo { + my ( $cb, $args ) = @_; _call({ method => 'artist.getInfo', artist => $args->{artist}, lang => $args->{lang} || 'en', autocorrect => 1, - }, sub { + }, $cb); +} + +sub getBiography { + my ( $class, $client, $cb, $args ) = @_; + + _getArtistInfo(sub { my $artistInfo = shift; if ( $artistInfo && ref $artistInfo && $artistInfo->{artist} && $artistInfo->{artist}->{bio} && (my $content = $artistInfo->{artist}->{bio}->{content})) { @@ -68,7 +74,32 @@ sub getBiography { else { $cb->({ error => cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND') }) } - }); + }, $args); +} + +sub getRelatedArtists { + my ( $class, $client, $cb, $args ) = @_; + + _getArtistInfo(sub { + my $artistInfo = shift; + + if ( $artistInfo && ref $artistInfo && $artistInfo->{artist} && $artistInfo->{artist}->{similar} && (my $similar = $artistInfo->{artist}->{similar}->{artist})) { + my $artists = [ map { + { + name => $_->{name}, + # image => $class->getLargestPhotoFromList($_->{image}, 'extralarge'), + url => $_->{url}, + } + } @$similar ]; + + $cb->({ + items => $artists, + }); + } + else { + $cb->({ error => cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND') }) + } + }, $args); } sub getArtistPhotos { @@ -233,6 +264,30 @@ sub getAlbumCovers { }, $args); } +sub getAlbumReview { + my ( $class, $client, $cb, $args ) = @_; + + $class->getAlbum(sub { + my $albumInfo = shift; + my $result = {}; + + if ( $albumInfo && ref $albumInfo && $albumInfo->{album} && (my $review = $albumInfo->{album}->{wiki}) ) { + $result->{review} = $result->{reviewText} = $review->{content}; + + if ( my $image = $albumInfo->{album}->{image} ) { + my $url = $class->getLargestPhotoFromList($image, 'extralarge'); + $result->{image} = $url if $url; + } + } + + if ( !$result->{review} && !main::SCANNER ) { + $result->{error} ||= cstring($client, 'PLUGIN_MUSICARTISTINFO_NOT_FOUND'); + } + + $cb->($result); + }, $args); +} + sub getAlbum { my ( $class, $cb, $args ) = @_; diff --git a/Settings.pm b/Settings.pm index d92da5b..42ab10a 100644 --- a/Settings.pm +++ b/Settings.pm @@ -16,7 +16,7 @@ sub name { sub prefs { return ($prefs, qw(browseArtistPictures runImporter lookupArtistPictures lookupCoverArt reviewFolder artistImageFolder lyricsFolder bioFolder - lookupAlbumArtistPicturesOnly saveMissingArtistPicturePlaceholder replaceOnlineGenres)); + lookupAlbumArtistPicturesOnly saveMissingArtistPicturePlaceholder)); } sub page {