@@ -16,9 +16,12 @@ internal class LyricsFreakProvider : ExternalProviderBase
1616 {
1717 private ILogger < LyricsFreakProvider > ? _logger ;
1818 private readonly IExternalUriConverter _uriConverter ;
19- private readonly string LyricsHrefXPath = "//a[translate(@title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '{0} lyrics']" ;
19+
20+ private const string LyricsHrefXPath = "//a[contains(translate(@title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{0} lyrics')]" ;
2021 private const string LyricsDivXPath = "//div[@data-container-id='lyrics']" ;
2122
23+ private const string PageNotFoundText = "#404 - Page Not Found" ;
24+
2225 public override IExternalProviderOptions Options { get ; }
2326
2427 #region Constructors
@@ -90,11 +93,17 @@ protected override async Task<SearchResult> SearchLyricAsync(string artist, stri
9093
9194 cancellationToken . ThrowIfCancellationRequested ( ) ;
9295
96+ if ( htmlResponse . Contains ( PageNotFoundText ) )
97+ {
98+ _logger ? . LogWarning ( $ "LyricsFreak. Artist's page not found (404). [{ artist } ]. Song name: [{ song } ]") ;
99+ return new SearchResult ( Models . ExternalProviderType . LyricsFreak ) ;
100+ }
101+
93102 // 2. Find song on the artist page and get link to the web page.
94103 var songHref = GetSongHrefFromHtmlBody ( htmlResponse , song ) ;
95104 if ( string . IsNullOrEmpty ( songHref ) )
96105 {
97- _logger ? . LogWarning ( $ "LyricsFreak. Can't find song Uri for song : [{ song } ]") ;
106+ _logger ? . LogWarning ( $ "LyricsFreak. Can't find song Uri for artist: [ { artist } ]. Song name : [{ song } ]") ;
98107 return new SearchResult ( Models . ExternalProviderType . LyricsFreak ) ;
99108 }
100109 var songUri = new Uri ( LyricsFreakUriConverter . BaseUrl + songHref ) ;
@@ -104,14 +113,14 @@ protected override async Task<SearchResult> SearchLyricAsync(string artist, stri
104113
105114 protected async override Task < SearchResult > SearchLyricAsync ( Uri uri , CancellationToken cancellationToken = default )
106115 {
107- var text = await WebClient . LoadAsync ( uri , cancellationToken ) ;
116+ var htmlBodyContent = await WebClient . LoadAsync ( uri , cancellationToken ) ;
108117
109118 cancellationToken . ThrowIfCancellationRequested ( ) ;
110119
111- var songLyrics = GetSongLyricsFromHtmlBody ( text ) ;
120+ var songLyrics = GetSongLyricsFromHtmlBody ( htmlBodyContent ) ;
112121 if ( string . IsNullOrEmpty ( songLyrics ) )
113122 {
114- _logger ? . LogWarning ( $ "LyricsFreak. Can't find song lyrics for song uri: [{ uri . AbsoluteUri } ]") ;
123+ _logger ? . LogWarning ( $ "LyricsFreak. Can't find lyrics for song's uri: [{ uri } ]") ;
115124 return new SearchResult ( Models . ExternalProviderType . LyricsFreak ) ;
116125 }
117126
@@ -126,12 +135,20 @@ protected async override Task<SearchResult> SearchLyricAsync(Uri uri, Cancellati
126135
127136 private string GetSongHrefFromHtmlBody ( string htmlBody , string song )
128137 {
138+ // Encoded needed for songs like "Devil's Calling". Title in htmlBody will be: "Devil's Calling Lyrics"
129139 string formattedXPath = string . Format ( LyricsHrefXPath , GetEncodedSong ( song ) ) ;
130- var linkNode = htmlBody . SelectSingleNodeByXPath ( formattedXPath ) ;
140+
141+ // In other cases tried lowercase search of the original song name.
142+ // Example. Artist: "Zé Ramalho". Song: "Batendo Na Porta Do Céu (Versão II)"
143+ string originalXPath = string . Format ( LyricsHrefXPath , song . ToLowerInvariant ( ) ) ;
144+
145+ var linkNode = htmlBody . SelectSingleNodeByXPath ( formattedXPath )
146+ ?? ( ! song . Contains ( "'" )
147+ ? htmlBody . SelectSingleNodeByXPath ( originalXPath )
148+ : null ) ;
149+
131150 if ( linkNode == null )
132- {
133151 return string . Empty ;
134- }
135152
136153 string hrefSong = linkNode . GetAttributeValue ( "href" , string . Empty ) ;
137154 return hrefSong ;
@@ -140,11 +157,10 @@ private string GetSongHrefFromHtmlBody(string htmlBody, string song)
140157 private string GetSongLyricsFromHtmlBody ( string htmlBody )
141158 {
142159 var lyricsNode = htmlBody . SelectSingleNodeByXPath ( LyricsDivXPath ) ;
160+
143161 if ( lyricsNode == null )
144- {
145162 return string . Empty ;
146163
147- }
148164 string lyricsText = lyricsNode . InnerText . Trim ( ) ;
149165 return lyricsText ;
150166 }
0 commit comments