Skip to content

Commit

Permalink
#17 Fixed instrumental song results for all providers except AZLyrics.
Browse files Browse the repository at this point in the history
  • Loading branch information
skuill committed Dec 17, 2023
1 parent 8851b32 commit d8d9d44
Show file tree
Hide file tree
Showing 29 changed files with 2,435 additions and 42 deletions.
16 changes: 10 additions & 6 deletions LyricsScraperNET.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ static async Task Main()
string artistToSearch = "Parkway Drive";
string songToSearch = "Idols And Anchors";

//// The case when a song contains only an instrumental, without vocals.
//string artistToSearch = "Rush";
//string songToSearch = "YYZ";

//// How to configure for ASP.NET applications:
var result = ExampleWithHostConfiguration(artistToSearch, songToSearch);

Expand All @@ -37,14 +41,14 @@ static async Task Main()
if (result.Instrumental)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine("This song is instrumental.\r\nIt does not contain any lyrics");
Console.WriteLine($"This song [{artistToSearch} - {songToSearch}] is instrumental.\r\nIt does not contain any lyrics");
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Can't find lyrics for: [ {artistToSearch} - {songToSearch} ]. " +
$"Status code: [ {result.ResponseStatusCode} ]. " +
$"Response message: [ {result.ResponseMessage} ].");
Console.WriteLine($"Can't find lyrics for: [{artistToSearch} - {songToSearch}]. " +
$"Status code: [{result.ResponseStatusCode}]. " +
$"Response message: [{result.ResponseMessage}].");
}
Console.ResetColor();

Expand All @@ -54,13 +58,13 @@ static async Task Main()

//// Output result to console
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"[ {artistToSearch} - {songToSearch} ]\r\n");
Console.WriteLine($"[{artistToSearch} - {songToSearch}]\r\n");
Console.ResetColor();

Console.WriteLine(result.LyricText);

Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine($"\r\nThis lyric was found by [ {result.ExternalProviderType} ]\r\n");
Console.WriteLine($"\r\nThis lyric was found by [{result.ExternalProviderType}]\r\n");
Console.ResetColor();

Console.ReadLine();
Expand Down
7 changes: 5 additions & 2 deletions LyricsScraperNET.TestShared/TestModel/LyricsTestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ public class LyricsTestData
public string SongName { get; set; }
public string SongUri { get; set; }

public string LyricPageData => File.ReadAllText(LyricPagePath);
public string LyricPageData => ReadFileData(LyricPagePath);

public string LyricResultData => File.ReadAllText(LyricResultPath);
public string LyricResultData => ReadFileData(LyricResultPath);

private string ReadFileData(string path) =>
!string.IsNullOrEmpty(path) ? File.ReadAllText(path) : string.Empty;
}
}
8 changes: 4 additions & 4 deletions LyricsScraperNET/LyricsScraperClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ public SearchResult SearchLyric(SearchRequest searchRequest)
{
return providerSearchResult;
}
_logger?.LogWarning($"Can't find lyric by provider: {externalProvider}.");
_logger?.LogWarning($"Can't find lyric by provider: [{externalProvider.Options?.ExternalProviderType}].");
}

searchResult.AddNoDataFoundMessage(Constants.ResponseMessages.NotFoundLyric);
_logger?.LogError($"Can't find lyrics for searchRequest: {searchRequest}.");
_logger?.LogError($"Can't find lyrics for searchRequest: [{searchRequest}].");

return searchResult;
}
Expand Down Expand Up @@ -106,11 +106,11 @@ public async Task<SearchResult> SearchLyricAsync(SearchRequest searchRequest)
{
return providerSearchResult;
}
_logger?.LogWarning($"Can't find lyric by provider: {externalProvider}.");
_logger?.LogWarning($"Can't find lyric by provider: [{externalProvider.Options?.ExternalProviderType}].");
}

searchResult.AddNoDataFoundMessage(Constants.ResponseMessages.NotFoundLyric);
_logger?.LogError($"Can't find lyrics for searchRequest: {searchRequest}.");
_logger?.LogError($"Can't find lyrics for searchRequest: [{searchRequest}].");

return searchResult;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,10 @@ public ArtistAndSongSearchRequest(string artist, string song, ExternalProviderTy
{
Provider = provider;
}

public override string ToString()
{
return $"Artist: [{Artist}]. Song: [{Song}]. Provider: [{Provider}]";
}
}
}
5 changes: 5 additions & 0 deletions LyricsScraperNET/Models/Requests/UriSearchRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,10 @@ public UriSearchRequest(string uri) : this(new Uri(uri))

public UriSearchRequest(string uri, ExternalProviderType provider) : this(new Uri(uri), provider)
{ }

public override string ToString()
{
return $"Uri: [{Uri}]. Provider: [{Provider}]";
}
}
}
12 changes: 6 additions & 6 deletions LyricsScraperNET/Providers/AZLyrics/AZLyricsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ protected override SearchResult SearchLyric(Uri uri)
if (WebClient == null || Parser == null)
{
_logger?.LogWarning($"AZLyrics. Please set up WebClient and Parser first");
return new SearchResult();
return new SearchResult(Models.ExternalProviderType.AZLyrics);
}
var text = WebClient.Load(uri);
return PostProcessLyric(uri, text);
Expand All @@ -88,7 +88,7 @@ protected override async Task<SearchResult> SearchLyricAsync(Uri uri)
if (WebClient == null || Parser == null)
{
_logger?.LogWarning($"AZLyrics. Please set up WebClient and Parser first");
return new SearchResult();
return new SearchResult(Models.ExternalProviderType.AZLyrics);
}
var text = await WebClient.LoadAsync(uri);
return PostProcessLyric(uri, text);
Expand All @@ -105,16 +105,16 @@ private SearchResult PostProcessLyric(Uri uri, string text)
{
if (string.IsNullOrEmpty(text))
{
_logger?.LogWarning($"AZLyrics. Text is empty for {uri}");
return new SearchResult();
_logger?.LogWarning($"AZLyrics. Text is empty for Uri: [{uri}]");
return new SearchResult(Models.ExternalProviderType.AZLyrics);
}

var startIndex = text.IndexOf(_lyricStart);
var endIndex = text.IndexOf(_lyricEnd);
if (startIndex <= 0 || endIndex <= 0)
{
_logger?.LogWarning($"AZLyrics. Can't find lyrics for {uri}");
return new SearchResult();
_logger?.LogWarning($"AZLyrics. Can't find lyrics for Uri: [{uri}]");
return new SearchResult(Models.ExternalProviderType.AZLyrics);
}

string result = Parser.Parse(text.Substring(startIndex, endIndex - startIndex));
Expand Down
57 changes: 48 additions & 9 deletions LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using LyricsScraperNET.Helpers;
using LyricsScraperNET.Extensions;
using LyricsScraperNET.Helpers;
using LyricsScraperNET.Models.Responses;
using LyricsScraperNET.Network;
using LyricsScraperNET.Providers.Abstract;
Expand All @@ -14,8 +15,16 @@ public sealed class LyricFindProvider : ExternalProviderBase
private ILogger<LyricFindProvider> _logger;
private readonly IExternalUriConverter _uriConverter;

/// <summary>
/// Field name in json format that contains lyric text.
/// </summary>
private const string _lyricStart = "\"lyrics\"";

// "instrumental":true
private const string _instrumentalStart = "\"instrumental\"";
// "songIsInstrumental":true
private const string _songIsInstrumentalStart = "\"songIsInstrumental\"";

#region Constructors

public LyricFindProvider()

Check warning on line 30 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

Non-nullable field '_logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
Expand Down Expand Up @@ -67,7 +76,7 @@ protected override SearchResult SearchLyric(Uri uri)
if (WebClient == null || Parser == null)
{
_logger?.LogWarning($"LyricFind. Please set up WebClient and Parser first");
return new SearchResult();
return new SearchResult(Models.ExternalProviderType.LyricFind);
}
var text = WebClient.Load(uri);
return PostProcessLyric(uri, text);
Expand All @@ -87,7 +96,7 @@ protected override async Task<SearchResult> SearchLyricAsync(Uri uri)
if (WebClient == null || Parser == null)
{
_logger?.LogWarning($"LyricFind. Please set up WebClient and Parser first");
return new SearchResult();
return new SearchResult(Models.ExternalProviderType.LyricFind);
}
var text = await WebClient.LoadAsync(uri);
return PostProcessLyric(uri, text);
Expand All @@ -104,15 +113,19 @@ private SearchResult PostProcessLyric(Uri uri, string text)
{
if (string.IsNullOrEmpty(text))
{
_logger?.LogWarning($"LyricFind. Text is empty for {uri}");
return new SearchResult();
_logger?.LogWarning($"LyricFind. Text is empty for Uri: [{uri}]");
return new SearchResult(Models.ExternalProviderType.LyricFind);
}

var startIndex = text.IndexOf(_lyricStart);
if (startIndex <= 0)
{
_logger?.LogWarning($"LyricFind. Can't find lyrics for {uri}");
return new SearchResult();
// In case of instrumental song it could not contains lyric field.
if (IsInstumentalLyric(text))
return new SearchResult(Models.ExternalProviderType.LyricFind).AddInstrumental(true);

_logger?.LogWarning($"LyricFind. Can't find lyrics for Uri: [{uri}]");
return new SearchResult(Models.ExternalProviderType.LyricFind);
}

// Trim the beginning of the text to the lyrics
Expand All @@ -126,13 +139,39 @@ private SearchResult PostProcessLyric(Uri uri, string text)
int endOfLyricInJson = Math.Max(Math.Min(endOfFieldValue, endOfJsonObject), -1);
if (endOfLyricInJson < 0)
{
_logger?.LogWarning($"LyricFind. Can't parse lyrics for {uri}");
return new SearchResult();
// In case of instrumental song it could not contains lyric field.
if (IsInstumentalLyric(text))
return new SearchResult(Models.ExternalProviderType.LyricFind).AddInstrumental(true);

_logger?.LogWarning($"LyricFind. Can't parse lyrics for Uri: [{uri}]");
return new SearchResult(Models.ExternalProviderType.LyricFind);
}

string result = Parser.Parse(text.Substring(start, endOfLyricInJson - start));

return new SearchResult(result, Models.ExternalProviderType.LyricFind);
}

/// <summary>
/// Check if lyric text contains instrumental flag.
/// </summary>
private bool IsInstumentalLyric(string text)
{
return TryReturnBooleanFieldValue(text, _instrumentalStart)
|| TryReturnBooleanFieldValue(text, _songIsInstrumentalStart);
}

/// <summary>
/// Try to find and return the fielad value as boolean. Pattern: "<paramref name="fieldName"/>":<true|false>.

Check warning on line 165 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'The character(s) '|' cannot be used at this location.'

Check warning on line 165 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'The character(s) '|' cannot be used at this location.'

Check warning on line 165 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'The character(s) '|' cannot be used at this location.'

Check warning on line 165 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'The character(s) '|' cannot be used at this location.'
/// In case if fieldName is not found returns false.
/// </summary>

Check warning on line 167 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'true'.'

Check warning on line 167 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'true'.'

Check warning on line 167 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'true'.'

Check warning on line 167 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'true'.'
private bool TryReturnBooleanFieldValue(string text, string fieldName)

Check warning on line 168 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'

Check warning on line 168 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'

Check warning on line 168 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'

Check warning on line 168 in LyricsScraperNET/Providers/LyricFind/LyricFindProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'
{
var startIndex = text.IndexOf(fieldName);
if (startIndex <= 0)
return false;
var fieldValue = text.Substring(startIndex + fieldName.Length + 1, 5);
return fieldValue.IndexOf("true", StringComparison.OrdinalIgnoreCase) >= 0;
}
}
}
26 changes: 19 additions & 7 deletions LyricsScraperNET/Providers/SongLyrics/SongLyricsProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HtmlAgilityPack;
using LyricsScraperNET.Extensions;
using LyricsScraperNET.Helpers;
using LyricsScraperNET.Models.Responses;
using LyricsScraperNET.Network;
Expand All @@ -21,6 +22,11 @@ public sealed class SongLyricsProvider : ExternalProviderBase

private const string NotExistLyricPattern = "We do not have the lyrics for (.*) yet.";

/// <summary>
/// For instrumental songs, the text "[Instrumental]" is returned in the songLyricsDiv
/// </summary>
private const string InstrumentalLyricText = "Instrumental";

#region Constructors

public SongLyricsProvider()

Check warning on line 32 in LyricsScraperNET/Providers/SongLyrics/SongLyricsProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

Non-nullable field '_logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 32 in LyricsScraperNET/Providers/SongLyrics/SongLyricsProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

Non-nullable field '_logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 32 in LyricsScraperNET/Providers/SongLyrics/SongLyricsProvider.cs

View workflow job for this annotation

GitHub Actions / lyrics_scraper_net-cicd

Non-nullable field '_logger' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
Expand Down Expand Up @@ -71,7 +77,7 @@ protected override SearchResult SearchLyric(Uri uri)
if (WebClient == null)
{
_logger?.LogWarning($"SongLyrics. Please set up WebClient first");
return new SearchResult();
return new SearchResult(Models.ExternalProviderType.SongLyrics);
}
var htmlPageBody = WebClient.Load(uri);
return GetParsedLyricFromHtmlPageBody(uri, htmlPageBody);
Expand All @@ -91,7 +97,7 @@ protected override async Task<SearchResult> SearchLyricAsync(Uri uri)
if (WebClient == null)
{
_logger?.LogWarning($"SongLyrics. Please set up WebClient first");
return new SearchResult();
return new SearchResult(Models.ExternalProviderType.SongLyrics);
}
var htmlPageBody = await WebClient.LoadAsync(uri);
return GetParsedLyricFromHtmlPageBody(uri, htmlPageBody);
Expand All @@ -108,8 +114,8 @@ private SearchResult GetParsedLyricFromHtmlPageBody(Uri uri, string htmlPageBody
{
if (string.IsNullOrEmpty(htmlPageBody))
{
_logger?.LogWarning($"SongLyrics. Text is empty for {uri}");
return new SearchResult();
_logger?.LogWarning($"SongLyrics. Text is empty for Uri: [{uri}]");
return new SearchResult(Models.ExternalProviderType.SongLyrics);
}

var htmlDocument = new HtmlDocument();
Expand All @@ -119,16 +125,22 @@ private SearchResult GetParsedLyricFromHtmlPageBody(Uri uri, string htmlPageBody

if (lyricsContainerNode == null)
{
_logger?.LogWarning($"SongLyrics. Can't find lyrics for {uri}");
return new SearchResult();
_logger?.LogWarning($"SongLyrics. Can't find lyrics for Uri: [{uri}]");
return new SearchResult(Models.ExternalProviderType.SongLyrics);
}

// Check if lyric not exist on site yet
if (Regex.IsMatch(lyricsContainerNode.InnerText, NotExistLyricPattern, RegexOptions.IgnoreCase))
{
_logger?.LogDebug($"SongLyrics. Returns empty result: \"{lyricsContainerNode.InnerText}\"");
return new SearchResult();
return new SearchResult(Models.ExternalProviderType.SongLyrics);
}

// Check if lyric is instrumental
if (string.Equals(lyricsContainerNode.InnerText, InstrumentalLyricText, StringComparison.OrdinalIgnoreCase)
|| string.Equals(lyricsContainerNode.InnerText, $"[{InstrumentalLyricText}]", StringComparison.OrdinalIgnoreCase))
return new SearchResult(Models.ExternalProviderType.SongLyrics).AddInstrumental(true);

return new SearchResult(lyricsContainerNode.InnerText, Models.ExternalProviderType.SongLyrics);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,24 @@
<None Update="Providers\AZLyrics\Resources\Lyrics_Result_01.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\Genius\instrumental_test_data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\Genius\Resources\Lyrics_Result_02.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\LyricFind\instrumental_test_data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\LyricFind\Resources\Lyrics_Result_01.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\LyricFind\lyric_test_data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\Musixmatch\instrumental_test_data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\Musixmatch\Resources\Lyrics_Result_01.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand All @@ -58,6 +67,9 @@
<None Update="Providers\AZLyrics\lyric_test_data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\SongLyrics\instrumental_test_data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Providers\SongLyrics\Resources\Lyrics_Result_01.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down

This file was deleted.

Loading

0 comments on commit d8d9d44

Please sign in to comment.