From 5f5347aee3209383248a6055318ec8883291d406 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:14:23 -0400 Subject: [PATCH 01/52] Add Lyrics API Endpoint --- .gitignore | 2 + .../Controllers/UserLibraryController.cs | 49 ++++++++++ Jellyfin.Api/Helpers/ItemHelper.cs | 87 ++++++++++++++++++ Jellyfin.Api/Jellyfin.Api.csproj | 12 +++ Jellyfin.Api/Libraries/LrcParser.dll | Bin 0 -> 12288 bytes Jellyfin.Api/Models/UserDtos/Lyrics.cs | 23 +++++ 6 files changed, 173 insertions(+) create mode 100644 Jellyfin.Api/Helpers/ItemHelper.cs create mode 100644 Jellyfin.Api/Libraries/LrcParser.dll create mode 100644 Jellyfin.Api/Models/UserDtos/Lyrics.cs diff --git a/.gitignore b/.gitignore index c2ae76c1e..3f80ffcf7 100644 --- a/.gitignore +++ b/.gitignore @@ -281,3 +281,5 @@ apiclient/generated # Omnisharp crash logs mono_crash.*.json + +!LrcParser.dll \ No newline at end of file diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index e45f9b58c..89fb56744 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; +using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; using Jellyfin.Extensions; +using Kfstorm.LrcParser; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -381,5 +385,50 @@ namespace Jellyfin.Api.Controllers return _userDataRepository.GetUserDataDto(item, user); } + + /// + /// Gets an item's lyrics. + /// + /// User id. + /// Item id. + /// Lyrics returned. + /// Something went wrong. No Lyrics will be returned. + /// An containing the intros to play. + [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + { + var user = _userManager.GetUserById(userId); + + if (user == null) + { + List lyricsList = new List + { + new Lyrics { Error = "User Not Found" } + }; + return NotFound(new { Results = lyricsList.ToArray() }); + } + + var item = itemId.Equals(default) + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(itemId); + + if (item == null) + { + List lyricsList = new List + { + new Lyrics { Error = "Requested Item Not Found" } + }; + return NotFound(new { Results = lyricsList.ToArray() }); + } + + List result = ItemHelper.GetLyricData(item); + if (string.IsNullOrEmpty(result.ElementAt(0).Error)) + { + return Ok(new { Results = result }); + } + + return NotFound(new { Results = result.ToArray() }); + } } } diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs new file mode 100644 index 000000000..43ef7aa09 --- /dev/null +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Jellyfin.Api.Models.UserDtos; +using Kfstorm.LrcParser; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Net; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Helpers +{ + /// + /// Item helper. + /// + public static class ItemHelper + { + /// + /// Opens lyrics file, converts to a List of Lyrics, and returns it. + /// + /// Requested Item. + /// Collection of Lyrics. + internal static List GetLyricData(BaseItem item) + { + List lyricsList = new List(); + + string lrcFilePath = @Path.ChangeExtension(item.Path, "lrc"); + + // LRC File not found, fallback to TXT file + if (!System.IO.File.Exists(lrcFilePath)) + { + string txtFilePath = @Path.ChangeExtension(item.Path, "txt"); + if (!System.IO.File.Exists(txtFilePath)) + { + lyricsList.Add(new Lyrics { Error = "Lyric File Not Found" }); + return lyricsList; + } + + var lyricTextData = System.IO.File.ReadAllText(txtFilePath); + string[] lyricTextLines = lyricTextData.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + + foreach (var lyricLine in lyricTextLines) + { + lyricsList.Add(new Lyrics { Text = lyricLine }); + } + + return lyricsList; + } + + // Process LRC File + ILrcFile lyricData; + string lrcFileContent = System.IO.File.ReadAllText(lrcFilePath); + try + { + lrcFileContent = lrcFileContent.Replace('<', '['); + lrcFileContent = lrcFileContent.Replace('>', ']'); + lyricData = Kfstorm.LrcParser.LrcFile.FromText(lrcFileContent); + } + catch + { + lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); + return lyricsList; + } + + if (lyricData == null) + { + lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); + return lyricsList; + } + + foreach (var lyricLine in lyricData.Lyrics) + { + double ticks = lyricLine.Timestamp.TotalSeconds * 10000000; + lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = lyricLine.Content }); + } + + return lyricsList; + } + } +} diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 894d87138..1b78bb107 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -46,4 +46,16 @@ + + + Libraries\LrcParser.dll + + + + + + Always + + + diff --git a/Jellyfin.Api/Libraries/LrcParser.dll b/Jellyfin.Api/Libraries/LrcParser.dll new file mode 100644 index 0000000000000000000000000000000000000000..46e4fb703335931a91bdb2bcce2a24c62b6b8dda GIT binary patch literal 12288 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 Y+Hfv^i;kV{{|xhgd;RYC?aRRb02;KsP5=M^ literal 0 HcmV?d00001 diff --git a/Jellyfin.Api/Models/UserDtos/Lyrics.cs b/Jellyfin.Api/Models/UserDtos/Lyrics.cs new file mode 100644 index 000000000..df1b5d08a --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/Lyrics.cs @@ -0,0 +1,23 @@ +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Lyric dto. + /// + public class Lyrics + { + /// + /// Gets or sets the start. + /// + public double? Start { get; set; } + + /// + /// Gets or sets the test. + /// + public string? Text { get; set; } + + /// + /// Gets or sets the error. + /// + public string? Error { get; set; } + } +} From 8b78802c0b42d9d55f5bd8c3f34a542d78903f75 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 21:08:38 -0400 Subject: [PATCH 02/52] Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 89fb56744..42367940d 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -402,11 +402,7 @@ namespace Jellyfin.Api.Controllers if (user == null) { - List lyricsList = new List - { - new Lyrics { Error = "User Not Found" } - }; - return NotFound(new { Results = lyricsList.ToArray() }); + return NotFound(); } var item = itemId.Equals(default) From 92715a74262fbf52f7071b45f5b61b7f057bb28e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 21:09:39 -0400 Subject: [PATCH 03/52] Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 42367940d..a1b911a45 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -424,7 +424,7 @@ namespace Jellyfin.Api.Controllers return Ok(new { Results = result }); } - return NotFound(new { Results = result.ToArray() }); + return NotFound(); } } } From 0aa2780ea7b23aed31765550f707f7ea9fc0daf1 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 21:15:57 -0400 Subject: [PATCH 04/52] Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index a1b911a45..7830df9bc 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -411,11 +411,7 @@ namespace Jellyfin.Api.Controllers if (item == null) { - List lyricsList = new List - { - new Lyrics { Error = "Requested Item Not Found" } - }; - return NotFound(new { Results = lyricsList.ToArray() }); + return NotFound(); } List result = ItemHelper.GetLyricData(item); From d24444b6d3a2e973d50edc2a8a1d01a433db645e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 10 Sep 2022 10:51:05 -0400 Subject: [PATCH 05/52] Update Jellyfin.Api/Models/UserDtos/Lyrics.cs Co-authored-by: Neil Burrows --- Jellyfin.Api/Models/UserDtos/Lyrics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Models/UserDtos/Lyrics.cs b/Jellyfin.Api/Models/UserDtos/Lyrics.cs index df1b5d08a..cd548eb03 100644 --- a/Jellyfin.Api/Models/UserDtos/Lyrics.cs +++ b/Jellyfin.Api/Models/UserDtos/Lyrics.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Api.Models.UserDtos public double? Start { get; set; } /// - /// Gets or sets the test. + /// Gets or sets the text. /// public string? Text { get; set; } From 2e260e5319b0b58290a1e30a28886c69d5a65325 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 10 Sep 2022 14:29:30 -0400 Subject: [PATCH 06/52] Updates based on review --- .../Controllers/UserLibraryController.cs | 9 ++- Jellyfin.Api/Helpers/ItemHelper.cs | 68 +++++++++++------- Jellyfin.Api/Jellyfin.Api.csproj | 13 +--- Jellyfin.Api/Libraries/LrcParser.dll | Bin 12288 -> 0 bytes 4 files changed, 46 insertions(+), 44 deletions(-) delete mode 100644 Jellyfin.Api/Libraries/LrcParser.dll diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 89fb56744..afd4013ed 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -12,7 +12,6 @@ using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; using Jellyfin.Extensions; -using Kfstorm.LrcParser; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -422,13 +421,13 @@ namespace Jellyfin.Api.Controllers return NotFound(new { Results = lyricsList.ToArray() }); } - List result = ItemHelper.GetLyricData(item); - if (string.IsNullOrEmpty(result.ElementAt(0).Error)) + var result = ItemHelper.GetLyricData(item); + if (result is not null) { - return Ok(new { Results = result }); + return Ok(result); } - return NotFound(new { Results = result.ToArray() }); + return NotFound(); } } } diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs index 43ef7aa09..c1b5ea6cc 100644 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -1,19 +1,13 @@ using System; using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; using System.IO; -using System.Net.Http; -using System.Threading.Tasks; +using System.Linq; using Jellyfin.Api.Models.UserDtos; -using Kfstorm.LrcParser; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Dlna; +using LrcParser.Model; +using LrcParser.Parser; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api.Helpers { @@ -27,7 +21,7 @@ namespace Jellyfin.Api.Helpers /// /// Requested Item. /// Collection of Lyrics. - internal static List GetLyricData(BaseItem item) + internal static object? GetLyricData(BaseItem item) { List lyricsList = new List(); @@ -39,8 +33,7 @@ namespace Jellyfin.Api.Helpers string txtFilePath = @Path.ChangeExtension(item.Path, "txt"); if (!System.IO.File.Exists(txtFilePath)) { - lyricsList.Add(new Lyrics { Error = "Lyric File Not Found" }); - return lyricsList; + return null; } var lyricTextData = System.IO.File.ReadAllText(txtFilePath); @@ -51,37 +44,58 @@ namespace Jellyfin.Api.Helpers lyricsList.Add(new Lyrics { Text = lyricLine }); } - return lyricsList; + return new { lyrics = lyricsList }; } // Process LRC File - ILrcFile lyricData; + Song lyricData; + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; string lrcFileContent = System.IO.File.ReadAllText(lrcFilePath); + try { - lrcFileContent = lrcFileContent.Replace('<', '['); - lrcFileContent = lrcFileContent.Replace('>', ']'); - lyricData = Kfstorm.LrcParser.LrcFile.FromText(lrcFileContent); + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + lyricData = lrcLyricParser.Decode(lrcFileContent); + var _metaData = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string dataRow in _metaData) + { + var data = dataRow.Split(":"); + + string newPropertyName = data[0].Replace("[", string.Empty, StringComparison.Ordinal); + string newPropertyValue = data[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(newPropertyName, newPropertyValue); + } + + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); } catch { - lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); - return lyricsList; + return null; } if (lyricData == null) { - lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); - return lyricsList; + return null; } - foreach (var lyricLine in lyricData.Lyrics) + for (int i = 0; i < sortedLyricData.Count; i++) { - double ticks = lyricLine.Timestamp.TotalSeconds * 10000000; - lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = lyricLine.Content }); + if (sortedLyricData[i].TimeTags.Count > 0) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } } - return lyricsList; + return new { MetaData = metaData, lyrics = lyricsList }; } } } diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 1b78bb107..972387e02 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,6 +17,7 @@ + @@ -46,16 +47,4 @@ - - - Libraries\LrcParser.dll - - - - - - Always - - - diff --git a/Jellyfin.Api/Libraries/LrcParser.dll b/Jellyfin.Api/Libraries/LrcParser.dll deleted file mode 100644 index 46e4fb703335931a91bdb2bcce2a24c62b6b8dda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 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 Y+Hfv^i;kV{{|xhgd;RYC?aRRb02;KsP5=M^ From 9d5cf67dfe2d5871d42a55a5e114c5ead1036ff0 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 10 Sep 2022 14:58:03 -0400 Subject: [PATCH 07/52] Create ILyricsProvider --- Emby.Server.Implementations/Dto/DtoService.cs | 5 + Jellyfin.Api/Helpers/ItemHelper.cs | 135 ++++++++++-------- .../Models/UserDtos/ILyricsProvider.cs | 34 +++++ .../Models/UserDtos/LrcLyricsProvider.cs | 117 +++++++++++++++ Jellyfin.Api/Models/UserDtos/Lyric.cs | 18 +++ Jellyfin.Api/Models/UserDtos/Lyrics.cs | 23 --- .../Models/UserDtos/TxtLyricsProvider.cs | 81 +++++++++++ MediaBrowser.Model/Dto/BaseItemDto.cs | 2 + 8 files changed, 334 insertions(+), 81 deletions(-) create mode 100644 Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs create mode 100644 Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs create mode 100644 Jellyfin.Api/Models/UserDtos/Lyric.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/Lyrics.cs create mode 100644 Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 09ba36851..96717cff5 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using Jellyfin.Api.Helpers; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; @@ -139,6 +140,10 @@ namespace Emby.Server.Implementations.Dto { LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult(); } + else if (item is Audio) + { + dto.HasLocalLyricsFile = ItemHelper.HasLyricFile(item.Path); + } if (item is IItemByName itemByName && options.ContainsField(ItemFields.ItemCounts)) diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs index c1b5ea6cc..622eb0b9f 100644 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -23,79 +23,98 @@ namespace Jellyfin.Api.Helpers /// Collection of Lyrics. internal static object? GetLyricData(BaseItem item) { - List lyricsList = new List(); + List providerList = new List(); - string lrcFilePath = @Path.ChangeExtension(item.Path, "lrc"); + // Find all classes that implement ILyricsProvider Interface + var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() + .GetTypes() + .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - // LRC File not found, fallback to TXT file - if (!System.IO.File.Exists(lrcFilePath)) - { - string txtFilePath = @Path.ChangeExtension(item.Path, "txt"); - if (!System.IO.File.Exists(txtFilePath)) - { - return null; - } - - var lyricTextData = System.IO.File.ReadAllText(txtFilePath); - string[] lyricTextLines = lyricTextData.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); - - foreach (var lyricLine in lyricTextLines) - { - lyricsList.Add(new Lyrics { Text = lyricLine }); - } - - return new { lyrics = lyricsList }; - } - - // Process LRC File - Song lyricData; - List sortedLyricData = new List(); - var metaData = new ExpandoObject() as IDictionary; - string lrcFileContent = System.IO.File.ReadAllText(lrcFilePath); - - try - { - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - lyricData = lrcLyricParser.Decode(lrcFileContent); - var _metaData = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) - .Select(x => x.Text) - .ToList(); - - foreach (string dataRow in _metaData) - { - var data = dataRow.Split(":"); - - string newPropertyName = data[0].Replace("[", string.Empty, StringComparison.Ordinal); - string newPropertyValue = data[1].Replace("]", string.Empty, StringComparison.Ordinal); - - metaData.Add(newPropertyName, newPropertyValue); - } - - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); - } - catch + if (!foundLyricProviders.Any()) { return null; } - if (lyricData == null) + foreach (var provider in foundLyricProviders) + { + providerList.Add((ILyricsProvider)Activator.CreateInstance(provider)); + } + + foreach (ILyricsProvider provider in providerList) + { + provider.Process(item); + if (provider.HasData) + { + return provider.Data; + } + } + + return null; + } + + /// + /// Checks if requested item has a matching lyric file. + /// + /// Path of requested item. + /// True if item has a matching lyrics file. + public static string? GetLyricFilePath(string itemPath) + { + List supportedLyricFileExtensions = new List(); + + // Find all classes that implement ILyricsProvider Interface + var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() + .GetTypes() + .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); + + if (!foundLyricProviders.Any()) { return null; } - for (int i = 0; i < sortedLyricData.Count; i++) + // Iterate over all found lyric providers + foreach (var provider in foundLyricProviders) { - if (sortedLyricData[i].TimeTags.Count > 0) + var foundProvider = (ILyricsProvider)Activator.CreateInstance(provider); + if (foundProvider?.FileExtensions is null) { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + continue; + } + + if (foundProvider.FileExtensions.Any()) + { + // Gather distinct list of handled file extentions + foreach (string lyricFileExtension in foundProvider.FileExtensions) + { + if (!supportedLyricFileExtensions.Contains(lyricFileExtension)) + { + supportedLyricFileExtensions.Add(lyricFileExtension); + } + } } } - return new { MetaData = metaData, lyrics = lyricsList }; + foreach (string lyricFileExtension in supportedLyricFileExtensions) + { + string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); + if (System.IO.File.Exists(lyricFilePath)) + { + return lyricFilePath; + } + } + + return null; + } + + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// Path of requested item. + /// True if item has a matching lyrics file; otherwise false. + public static bool HasLyricFile(string itemPath) + { + string? lyricFilePath = GetLyricFilePath(itemPath); + return !string.IsNullOrEmpty(lyricFilePath); } } } diff --git a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs new file mode 100644 index 000000000..37f1f5bbe --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs @@ -0,0 +1,34 @@ +using System.Collections.ObjectModel; +using MediaBrowser.Controller.Entities; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricsProvider + { + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public Collection? FileExtensions { get; } + + /// + /// Gets a value indicating whether Process() generated data. + /// + /// true if data generated; otherwise, false. + bool HasData { get; } + + /// + /// Gets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + object? Data { get; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + void Process(BaseItem item); + } +} diff --git a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs new file mode 100644 index 000000000..029acd6ca --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public LrcLyricsProvider() + { + FileExtensions = new Collection + { + "lrc" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public Collection? FileExtensions { get; } + + /// + /// Gets or Sets a value indicating whether Process() generated data. + /// + /// true if data generated; otherwise, false. + public bool HasData { get; set; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + public void Process(BaseItem item) + { + string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return; + } + + List lyricsList = new List(); + + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; + string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + + try + { + // Parse and sort lyric rows + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + Song lyricData = lrcLyricParser.Decode(lrcFileContent); + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + var metaDataField = metaDataRow.Split(":"); + + string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); + string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(metaDataFieldName, metaDataFieldValue); + } + } + catch + { + return; + } + + if (!sortedLyricData.Any()) + { + return; + } + + for (int i = 0; i < sortedLyricData.Count; i++) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + this.HasData = true; + if (metaData.Any()) + { + this.Data = new { MetaData = metaData, lyrics = lyricsList }; + } + else + { + this.Data = new { lyrics = lyricsList }; + } + } + } +} diff --git a/Jellyfin.Api/Models/UserDtos/Lyric.cs b/Jellyfin.Api/Models/UserDtos/Lyric.cs new file mode 100644 index 000000000..2794cd78a --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/Lyric.cs @@ -0,0 +1,18 @@ +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Lyric dto. + /// + public class Lyric + { + /// + /// Gets or sets the start time (ticks). + /// + public double Start { get; set; } + + /// + /// Gets or sets the text. + /// + public string Text { get; set; } = string.Empty; + } +} diff --git a/Jellyfin.Api/Models/UserDtos/Lyrics.cs b/Jellyfin.Api/Models/UserDtos/Lyrics.cs deleted file mode 100644 index cd548eb03..000000000 --- a/Jellyfin.Api/Models/UserDtos/Lyrics.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// Lyric dto. - /// - public class Lyrics - { - /// - /// Gets or sets the start. - /// - public double? Start { get; set; } - - /// - /// Gets or sets the text. - /// - public string? Text { get; set; } - - /// - /// Gets or sets the error. - /// - public string? Error { get; set; } - } -} diff --git a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs new file mode 100644 index 000000000..03cce1ffb --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public TxtLyricsProvider() + { + FileExtensions = new Collection + { + "lrc", "txt" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public Collection? FileExtensions { get; } + + /// + /// Gets or Sets a value indicating whether Process() generated data. + /// + /// true if data generated; otherwise, false. + public bool HasData { get; set; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + public void Process(BaseItem item) + { + string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return; + } + + List lyricsList = new List(); + + string lyricData = System.IO.File.ReadAllText(lyricFilePath); + + // Splitting on Environment.NewLine caused some new lines to be missed in Windows. + char[] newLinedelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + + if (!lyricTextLines.Any()) + { + return; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new Lyric { Text = lyricLine }); + } + + this.HasData = true; + this.Data = new { lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index fdb84fa32..b40a0210a 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -76,6 +76,8 @@ namespace MediaBrowser.Model.Dto public bool? CanDownload { get; set; } + public bool? HasLocalLyricsFile { get; set; } + public bool? HasSubtitles { get; set; } public string PreferredMetadataLanguage { get; set; } From 97f8a63b8934ead26cfc7d41550188d86cada93a Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 11 Sep 2022 15:56:34 -0400 Subject: [PATCH 08/52] Allow LRC start value to be null --- Jellyfin.Api/Models/UserDtos/Lyric.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Models/UserDtos/Lyric.cs b/Jellyfin.Api/Models/UserDtos/Lyric.cs index 2794cd78a..f83fc9839 100644 --- a/Jellyfin.Api/Models/UserDtos/Lyric.cs +++ b/Jellyfin.Api/Models/UserDtos/Lyric.cs @@ -8,7 +8,7 @@ namespace Jellyfin.Api.Models.UserDtos /// /// Gets or sets the start time (ticks). /// - public double Start { get; set; } + public double? Start { get; set; } /// /// Gets or sets the text. From 3928d02e175f732d5e13fa0c66dbccb704c4324d Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 11 Sep 2022 16:13:17 -0400 Subject: [PATCH 09/52] Resolve Possible null reference --- Jellyfin.Api/Helpers/ItemHelper.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs index 622eb0b9f..cd29361bd 100644 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -37,7 +37,11 @@ namespace Jellyfin.Api.Helpers foreach (var provider in foundLyricProviders) { - providerList.Add((ILyricsProvider)Activator.CreateInstance(provider)); + ILyricsProvider? newProvider = Activator.CreateInstance(provider) as ILyricsProvider; + if (newProvider is not null) + { + providerList.Add(newProvider); + } } foreach (ILyricsProvider provider in providerList) @@ -74,7 +78,7 @@ namespace Jellyfin.Api.Helpers // Iterate over all found lyric providers foreach (var provider in foundLyricProviders) { - var foundProvider = (ILyricsProvider)Activator.CreateInstance(provider); + ILyricsProvider? foundProvider = Activator.CreateInstance(provider) as ILyricsProvider; if (foundProvider?.FileExtensions is null) { continue; From cecca5f715f70b263f81b5c44dfcee5c38d53bf8 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 11 Sep 2022 19:13:02 -0400 Subject: [PATCH 10/52] Remove unneeded 2nd loops --- .gitignore | 2 -- Jellyfin.Api/Helpers/ItemHelper.cs | 30 ++++++++---------------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 3f80ffcf7..c2ae76c1e 100644 --- a/.gitignore +++ b/.gitignore @@ -281,5 +281,3 @@ apiclient/generated # Omnisharp crash logs mono_crash.*.json - -!LrcParser.dll \ No newline at end of file diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs index cd29361bd..0afe5a155 100644 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -40,16 +40,11 @@ namespace Jellyfin.Api.Helpers ILyricsProvider? newProvider = Activator.CreateInstance(provider) as ILyricsProvider; if (newProvider is not null) { - providerList.Add(newProvider); - } - } - - foreach (ILyricsProvider provider in providerList) - { - provider.Process(item); - if (provider.HasData) - { - return provider.Data; + newProvider.Process(item); + if (newProvider.HasData) + { + return newProvider.Data; + } } } @@ -86,26 +81,17 @@ namespace Jellyfin.Api.Helpers if (foundProvider.FileExtensions.Any()) { - // Gather distinct list of handled file extentions foreach (string lyricFileExtension in foundProvider.FileExtensions) { - if (!supportedLyricFileExtensions.Contains(lyricFileExtension)) + string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); + if (System.IO.File.Exists(lyricFilePath)) { - supportedLyricFileExtensions.Add(lyricFileExtension); + return lyricFilePath; } } } } - foreach (string lyricFileExtension in supportedLyricFileExtensions) - { - string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); - if (System.IO.File.Exists(lyricFilePath)) - { - return lyricFilePath; - } - } - return null; } From 31ec521f5ebfdffc1c38b4c67e7632933041a988 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 11 Sep 2022 19:15:33 -0400 Subject: [PATCH 11/52] Remove now unused variables --- Jellyfin.Api/Helpers/ItemHelper.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs index 0afe5a155..49bb8af8e 100644 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -23,8 +23,6 @@ namespace Jellyfin.Api.Helpers /// Collection of Lyrics. internal static object? GetLyricData(BaseItem item) { - List providerList = new List(); - // Find all classes that implement ILyricsProvider Interface var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() .GetTypes() @@ -58,8 +56,6 @@ namespace Jellyfin.Api.Helpers /// True if item has a matching lyrics file. public static string? GetLyricFilePath(string itemPath) { - List supportedLyricFileExtensions = new List(); - // Find all classes that implement ILyricsProvider Interface var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() .GetTypes() From c0dae0fef5255f27071b8dd84e8468a3e1ad29bf Mon Sep 17 00:00:00 2001 From: Jamie Introcaso Date: Wed, 14 Sep 2022 20:39:26 -0400 Subject: [PATCH 12/52] Adds lyric providers to DI pipeline This is adding those lyric providers to the DI pipeline along with a super simple implementation of how to use them in the controller method. Probably should be refactored into a lyric service of some sort that would have the providers injected into it. --- .../ApplicationHost.cs | 3 +++ .../Controllers/UserLibraryController.cs | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91a16c199..3e9c540e7 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -46,6 +46,7 @@ using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; +using Jellyfin.Api.Models.UserDtos; using Jellyfin.MediaEncoding.Hls.Playlist; using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; @@ -580,6 +581,8 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index ed8a98d23..123c5e079 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; -using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; -using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -40,6 +37,7 @@ namespace Jellyfin.Api.Controllers private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; private readonly IFileSystem _fileSystem; + private readonly IEnumerable _lyricProviders; /// /// Initializes a new instance of the class. @@ -50,13 +48,15 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Collection of all registered interfaces. public UserLibraryController( IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, IUserViewManager userViewManager, - IFileSystem fileSystem) + IFileSystem fileSystem, + IEnumerable lyricProviders) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -64,6 +64,7 @@ namespace Jellyfin.Api.Controllers _dtoService = dtoService; _userViewManager = userViewManager; _fileSystem = fileSystem; + _lyricProviders = lyricProviders; } /// @@ -413,10 +414,14 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = ItemHelper.GetLyricData(item); - if (result is not null) + // Super nieve implementation. I would suggest building a lyric service of some sort and doing this there. + foreach (var provider in _lyricProviders) { - return Ok(result); + provider.Process(item); + if (provider.HasData) + { + return Ok(provider.Data); + } } return NotFound(); From d9be3874ba3842d5888c5cbbe583614ed990849e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:45:26 -0400 Subject: [PATCH 13/52] Auto stash before merge of "lyric-lrc-file-support" and "origin/lyric-lrc-file-support" --- .../ApplicationHost.cs | 2 + Emby.Server.Implementations/Dto/DtoService.cs | 9 +- .../Controllers/UserLibraryController.cs | 5 +- Jellyfin.Api/Helpers/ItemHelper.cs | 106 ------------------ Jellyfin.Api/Jellyfin.Api.csproj | 1 - .../Models/UserDtos/ILyricsProvider.cs | 34 ------ .../Lyrics/ILyricsProvider.cs | 24 ++++ .../Lyrics}/Lyric.cs | 2 +- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 87 ++++++++++++++ .../Lyrics/LyricResponse.cs | 15 +++ MediaBrowser.Model/Dto/BaseItemDto.cs | 2 +- .../Lyric}/LrcLyricsProvider.cs | 39 +++---- .../Lyric}/TxtLyricsProvider.cs | 35 +++--- .../MediaBrowser.Providers.csproj | 2 + 14 files changed, 173 insertions(+), 190 deletions(-) delete mode 100644 Jellyfin.Api/Helpers/ItemHelper.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricsProvider.cs rename {Jellyfin.Api/Models/UserDtos => MediaBrowser.Controller/Lyrics}/Lyric.cs (90%) create mode 100644 MediaBrowser.Controller/Lyrics/LyricInfo.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricResponse.cs rename {Jellyfin.Api/Models/UserDtos => MediaBrowser.Providers/Lyric}/LrcLyricsProvider.cs (76%) rename {Jellyfin.Api/Models/UserDtos => MediaBrowser.Providers/Lyric}/TxtLyricsProvider.cs (65%) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3e9c540e7..5487e5e02 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -68,6 +68,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Notifications; @@ -95,6 +96,7 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.Chapters; +using MediaBrowser.Providers.Lyric; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.Tmdb; using MediaBrowser.Providers.Subtitles; diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 96717cff5..bed82a4bb 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -19,6 +19,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; @@ -51,6 +52,8 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy _livetvManagerFactory; + private readonly IEnumerable _lyricProviders; + public DtoService( ILogger logger, ILibraryManager libraryManager, @@ -60,7 +63,8 @@ namespace Emby.Server.Implementations.Dto IProviderManager providerManager, IApplicationHost appHost, IMediaSourceManager mediaSourceManager, - Lazy livetvManagerFactory) + Lazy livetvManagerFactory, + IEnumerable lyricProviders) { _logger = logger; _libraryManager = libraryManager; @@ -71,6 +75,7 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; + _lyricProviders = lyricProviders; } private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; @@ -142,7 +147,7 @@ namespace Emby.Server.Implementations.Dto } else if (item is Audio) { - dto.HasLocalLyricsFile = ItemHelper.HasLyricFile(item.Path); + dto.HasLyrics = MediaBrowser.Controller.Lyrics.LyricInfo.HasLyricFile(_lyricProviders, item.Path); } if (item is IItemByName itemByName diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 123c5e079..3da78c116 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -13,6 +13,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -414,8 +415,8 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - // Super nieve implementation. I would suggest building a lyric service of some sort and doing this there. - foreach (var provider in _lyricProviders) + var result = MediaBrowser.Controller.Lyrics.LyricInfo.GetLyricData(_lyricProviders, item); + if (result is not null) { provider.Process(item); if (provider.HasData) diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs deleted file mode 100644 index 49bb8af8e..000000000 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Globalization; -using System.IO; -using System.Linq; -using Jellyfin.Api.Models.UserDtos; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Helpers -{ - /// - /// Item helper. - /// - public static class ItemHelper - { - /// - /// Opens lyrics file, converts to a List of Lyrics, and returns it. - /// - /// Requested Item. - /// Collection of Lyrics. - internal static object? GetLyricData(BaseItem item) - { - // Find all classes that implement ILyricsProvider Interface - var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() - .GetTypes() - .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - - if (!foundLyricProviders.Any()) - { - return null; - } - - foreach (var provider in foundLyricProviders) - { - ILyricsProvider? newProvider = Activator.CreateInstance(provider) as ILyricsProvider; - if (newProvider is not null) - { - newProvider.Process(item); - if (newProvider.HasData) - { - return newProvider.Data; - } - } - } - - return null; - } - - /// - /// Checks if requested item has a matching lyric file. - /// - /// Path of requested item. - /// True if item has a matching lyrics file. - public static string? GetLyricFilePath(string itemPath) - { - // Find all classes that implement ILyricsProvider Interface - var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() - .GetTypes() - .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - - if (!foundLyricProviders.Any()) - { - return null; - } - - // Iterate over all found lyric providers - foreach (var provider in foundLyricProviders) - { - ILyricsProvider? foundProvider = Activator.CreateInstance(provider) as ILyricsProvider; - if (foundProvider?.FileExtensions is null) - { - continue; - } - - if (foundProvider.FileExtensions.Any()) - { - foreach (string lyricFileExtension in foundProvider.FileExtensions) - { - string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); - if (System.IO.File.Exists(lyricFilePath)) - { - return lyricFilePath; - } - } - } - } - - return null; - } - - - /// - /// Checks if requested item has a matching local lyric file. - /// - /// Path of requested item. - /// True if item has a matching lyrics file; otherwise false. - public static bool HasLyricFile(string itemPath) - { - string? lyricFilePath = GetLyricFilePath(itemPath); - return !string.IsNullOrEmpty(lyricFilePath); - } - } -} diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 972387e02..894d87138 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,6 @@ - diff --git a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs deleted file mode 100644 index 37f1f5bbe..000000000 --- a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.ObjectModel; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// Interface ILyricsProvider. - /// - public interface ILyricsProvider - { - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public Collection? FileExtensions { get; } - - /// - /// Gets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - bool HasData { get; } - - /// - /// Gets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - object? Data { get; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - void Process(BaseItem item); - } -} diff --git a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs new file mode 100644 index 000000000..bac32a398 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricsProvider + { + /// + /// Gets the supported media types for this provider. + /// + /// The supported media types. + IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets the lyrics. + /// + /// The item to to process. + /// Task{LyricResponse}. + LyricResponse? GetLyrics(BaseItem item); + } +} diff --git a/Jellyfin.Api/Models/UserDtos/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs similarity index 90% rename from Jellyfin.Api/Models/UserDtos/Lyric.cs rename to MediaBrowser.Controller/Lyrics/Lyric.cs index f83fc9839..d44546dd3 100644 --- a/Jellyfin.Api/Models/UserDtos/Lyric.cs +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -1,4 +1,4 @@ -namespace Jellyfin.Api.Models.UserDtos +namespace MediaBrowser.Controller.Lyrics { /// /// Lyric dto. diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs new file mode 100644 index 000000000..83a10701a --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Net; +using Microsoft.AspNetCore.Mvc; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Item helper. + /// + public class LyricInfo + { + /// + /// Opens lyrics file, converts to a List of Lyrics, and returns it. + /// + /// Collection of all registered interfaces. + /// Requested Item. + /// Collection of Lyrics. + public static LyricResponse? GetLyricData(IEnumerable lyricProviders, BaseItem item) + { + + foreach (var provider in lyricProviders) + { + var result = provider.GetLyrics(item); + if (result is not null) + { + return result; + } + } + + return new LyricResponse + { + Lyrics = new List + { + new Lyric { Start = 0, Text = "Test" } + } + }; + } + + /// + /// Checks if requested item has a matching lyric file. + /// + /// The current lyricProvider interface. + /// Path of requested item. + /// True if item has a matching lyrics file. + public static string? GetLyricFilePath(ILyricsProvider lyricProvider, string itemPath) + { + if (lyricProvider.SupportedMediaTypes.Any()) + { + foreach (string lyricFileExtension in lyricProvider.SupportedMediaTypes) + { + string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); + if (System.IO.File.Exists(lyricFilePath)) + { + return lyricFilePath; + } + } + } + + return null; + } + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// Collection of all registered interfaces. + /// Path of requested item. + /// True if item has a matching lyrics file; otherwise false. + public static bool HasLyricFile(IEnumerable lyricProviders, string itemPath) + { + foreach (var provider in lyricProviders) + { + if (GetLyricFilePath(provider, itemPath) is not null) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs new file mode 100644 index 000000000..e312638ec --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -0,0 +1,15 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Lyrics +{ + public class LyricResponse + { + public IDictionary MetaData { get; set; } + + public IEnumerable Lyrics { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index b40a0210a..2a86fded2 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.Model.Dto public bool? CanDownload { get; set; } - public bool? HasLocalLyricsFile { get; set; } + public bool? HasLyrics { get; set; } public bool? HasSubtitles { get; set; } diff --git a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs similarity index 76% rename from Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs rename to MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs index 029acd6ca..e30d56308 100644 --- a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs @@ -4,11 +4,14 @@ using System.Collections.ObjectModel; using System.Dynamic; using System.Globalization; using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; -namespace Jellyfin.Api.Models.UserDtos +namespace MediaBrowser.Providers.Lyric { /// /// LRC File Lyric Provider. @@ -20,7 +23,7 @@ namespace Jellyfin.Api.Models.UserDtos /// public LrcLyricsProvider() { - FileExtensions = new Collection + SupportedMediaTypes = new Collection { "lrc" }; @@ -29,13 +32,7 @@ namespace Jellyfin.Api.Models.UserDtos /// /// Gets a value indicating the File Extenstions this provider works with. /// - public Collection? FileExtensions { get; } - - /// - /// Gets or Sets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - public bool HasData { get; set; } + public IEnumerable SupportedMediaTypes { get; } /// /// Gets or Sets Data object generated by Process() method. @@ -47,16 +44,17 @@ namespace Jellyfin.Api.Models.UserDtos /// Opens lyric file for [the specified item], and processes it for API return. /// /// The item to to process. - public void Process(BaseItem item) + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) { - string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); if (string.IsNullOrEmpty(lyricFilePath)) { - return; + return null; } - List lyricsList = new List(); + List lyricsList = new List(); List sortedLyricData = new List(); var metaData = new ExpandoObject() as IDictionary; @@ -88,30 +86,27 @@ namespace Jellyfin.Api.Models.UserDtos } catch { - return; + return null; } if (!sortedLyricData.Any()) { - return; + return null; } for (int i = 0; i < sortedLyricData.Count; i++) { var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); } - this.HasData = true; if (metaData.Any()) { - this.Data = new { MetaData = metaData, lyrics = lyricsList }; - } - else - { - this.Data = new { lyrics = lyricsList }; + return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; } + + return new LyricResponse { Lyrics = lyricsList }; } } } diff --git a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs similarity index 65% rename from Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs rename to MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs index 03cce1ffb..2a5da4e4d 100644 --- a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Dynamic; -using System.Globalization; using System.Linq; -using LrcParser.Model; -using LrcParser.Parser; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; -namespace Jellyfin.Api.Models.UserDtos +namespace MediaBrowser.Providers.Lyric { /// /// TXT File Lyric Provider. @@ -20,7 +19,7 @@ namespace Jellyfin.Api.Models.UserDtos /// public TxtLyricsProvider() { - FileExtensions = new Collection + SupportedMediaTypes = new Collection { "lrc", "txt" }; @@ -29,13 +28,7 @@ namespace Jellyfin.Api.Models.UserDtos /// /// Gets a value indicating the File Extenstions this provider works with. /// - public Collection? FileExtensions { get; } - - /// - /// Gets or Sets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - public bool HasData { get; set; } + public IEnumerable SupportedMediaTypes { get; } /// /// Gets or Sets Data object generated by Process() method. @@ -47,16 +40,17 @@ namespace Jellyfin.Api.Models.UserDtos /// Opens lyric file for [the specified item], and processes it for API return. /// /// The item to to process. - public void Process(BaseItem item) + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) { - string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); if (string.IsNullOrEmpty(lyricFilePath)) { - return; + return null; } - List lyricsList = new List(); + List lyricsList = new List(); string lyricData = System.IO.File.ReadAllText(lyricFilePath); @@ -66,16 +60,15 @@ namespace Jellyfin.Api.Models.UserDtos if (!lyricTextLines.Any()) { - return; + return null; } foreach (string lyricLine in lyricTextLines) { - lyricsList.Add(new Lyric { Text = lyricLine }); + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); } - this.HasData = true; - this.Data = new { lyrics = lyricsList }; + return new LyricResponse { Lyrics = lyricsList }; } } } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9864db9ac..8514489f8 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -6,6 +6,7 @@ + @@ -16,6 +17,7 @@ + From f4fd908f8d7ffcdea6acaf75928f6c2960ed6338 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 15 Sep 2022 20:49:25 -0400 Subject: [PATCH 14/52] Create ILyricManager --- .../ApplicationHost.cs | 4 +- Emby.Server.Implementations/Dto/DtoService.cs | 8 +- .../Controllers/UserLibraryController.cs | 16 ++- .../Lyrics/ILyricManager.cs | 37 +++++++ .../{ILyricsProvider.cs => ILyricProvider.cs} | 7 +- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 50 +--------- ...cLyricsProvider.cs => LrcLyricProvider.cs} | 13 ++- MediaBrowser.Providers/Lyric/LyricManager.cs | 97 +++++++++++++++++++ ...tLyricsProvider.cs => TxtLyricProvider.cs} | 14 ++- 9 files changed, 175 insertions(+), 71 deletions(-) create mode 100644 MediaBrowser.Controller/Lyrics/ILyricManager.cs rename MediaBrowser.Controller/Lyrics/{ILyricsProvider.cs => ILyricProvider.cs} (79%) rename MediaBrowser.Providers/Lyric/{LrcLyricsProvider.cs => LrcLyricProvider.cs} (93%) create mode 100644 MediaBrowser.Providers/Lyric/LyricManager.cs rename MediaBrowser.Providers/Lyric/{TxtLyricsProvider.cs => TxtLyricProvider.cs} (88%) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5487e5e02..409fc04b1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -583,8 +583,6 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); - serviceCollection.AddTransient(); - serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -603,6 +601,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -790,6 +789,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); + Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index bed82a4bb..6ab574c5c 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy _livetvManagerFactory; - private readonly IEnumerable _lyricProviders; + private readonly ILyricManager _lyricManager; public DtoService( ILogger logger, @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Dto IApplicationHost appHost, IMediaSourceManager mediaSourceManager, Lazy livetvManagerFactory, - IEnumerable lyricProviders) + ILyricManager lyricManager) { _logger = logger; _libraryManager = libraryManager; @@ -75,7 +75,7 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; - _lyricProviders = lyricProviders; + _lyricManager = lyricManager; } private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; @@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.Dto } else if (item is Audio) { - dto.HasLyrics = MediaBrowser.Controller.Lyrics.LyricInfo.HasLyricFile(_lyricProviders, item.Path); + dto.HasLyrics = _lyricManager.HasLyricFile(item); } if (item is IItemByName itemByName diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 3da78c116..1421ab444 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -38,7 +38,7 @@ namespace Jellyfin.Api.Controllers private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; private readonly IFileSystem _fileSystem; - private readonly IEnumerable _lyricProviders; + private readonly ILyricManager _lyricManager; /// /// Initializes a new instance of the class. @@ -49,7 +49,7 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Collection of all registered interfaces. + /// Instance of the interface. public UserLibraryController( IUserManager userManager, IUserDataManager userDataRepository, @@ -57,7 +57,7 @@ namespace Jellyfin.Api.Controllers IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem, - IEnumerable lyricProviders) + ILyricManager lyricManager) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers _dtoService = dtoService; _userViewManager = userViewManager; _fileSystem = fileSystem; - _lyricProviders = lyricProviders; + _lyricManager = lyricManager; } /// @@ -415,14 +415,10 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = MediaBrowser.Controller.Lyrics.LyricInfo.GetLyricData(_lyricProviders, item); + var result = _lyricManager.GetLyric(item); if (result is not null) { - provider.Process(item); - if (provider.HasData) - { - return Ok(provider.Data); - } + return Ok(result); } return NotFound(); diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs new file mode 100644 index 000000000..4fd11b9e0 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -0,0 +1,37 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Controller.Lyrics +{ + public interface ILyricManager + { + /// + /// Adds the parts. + /// + /// The lyric providers. + void AddParts(IEnumerable lyricProviders); + + /// + /// Gets the lyrics. + /// + /// The media item. + /// Lyrics for passed item. + LyricResponse GetLyric(BaseItem item); + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// The media item. + /// True if item has a matching lyrics file; otherwise false. + bool HasLyricFile(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs similarity index 79% rename from MediaBrowser.Controller/Lyrics/ILyricsProvider.cs rename to MediaBrowser.Controller/Lyrics/ILyricProvider.cs index bac32a398..691fed1fd 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -6,8 +6,13 @@ namespace MediaBrowser.Controller.Lyrics /// /// Interface ILyricsProvider. /// - public interface ILyricsProvider + public interface ILyricProvider { + /// + /// Gets a value indicating the provider name. + /// + string Name { get; } + /// /// Gets the supported media types for this provider. /// diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 83a10701a..d44e14237 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -13,42 +13,15 @@ namespace MediaBrowser.Controller.Lyrics /// /// Item helper. /// - public class LyricInfo + public static class LyricInfo { - /// - /// Opens lyrics file, converts to a List of Lyrics, and returns it. - /// - /// Collection of all registered interfaces. - /// Requested Item. - /// Collection of Lyrics. - public static LyricResponse? GetLyricData(IEnumerable lyricProviders, BaseItem item) - { - - foreach (var provider in lyricProviders) - { - var result = provider.GetLyrics(item); - if (result is not null) - { - return result; - } - } - - return new LyricResponse - { - Lyrics = new List - { - new Lyric { Start = 0, Text = "Test" } - } - }; - } - /// /// Checks if requested item has a matching lyric file. /// /// The current lyricProvider interface. /// Path of requested item. /// True if item has a matching lyrics file. - public static string? GetLyricFilePath(ILyricsProvider lyricProvider, string itemPath) + public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) { if (lyricProvider.SupportedMediaTypes.Any()) { @@ -64,24 +37,5 @@ namespace MediaBrowser.Controller.Lyrics return null; } - - /// - /// Checks if requested item has a matching local lyric file. - /// - /// Collection of all registered interfaces. - /// Path of requested item. - /// True if item has a matching lyrics file; otherwise false. - public static bool HasLyricFile(IEnumerable lyricProviders, string itemPath) - { - foreach (var provider in lyricProviders) - { - if (GetLyricFilePath(provider, itemPath) is not null) - { - return true; - } - } - - return false; - } } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs similarity index 93% rename from MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs rename to MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index e30d56308..18a85c93a 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -16,19 +16,26 @@ namespace MediaBrowser.Providers.Lyric /// /// LRC File Lyric Provider. /// - public class LrcLyricsProvider : ILyricsProvider + public class LrcLyricProvider : ILyricProvider { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public LrcLyricsProvider() + public LrcLyricProvider() { + Name = "LrcLyricProvider"; + SupportedMediaTypes = new Collection { "lrc" }; } + /// + /// Gets a value indicating the provider name. + /// + public string Name { get; } + /// /// Gets a value indicating the File Extenstions this provider works with. /// diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs new file mode 100644 index 000000000..48572c63e --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -0,0 +1,97 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Extensions; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Lyric +{ + public class LyricManager : ILyricManager + { + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly ILibraryMonitor _monitor; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly ILocalizationManager _localization; + + private ILyricProvider[] _lyricProviders; + + public LyricManager( + ILogger logger, + IFileSystem fileSystem, + ILibraryMonitor monitor, + IMediaSourceManager mediaSourceManager, + ILocalizationManager localizationManager) + { + _logger = logger; + _fileSystem = fileSystem; + _monitor = monitor; + _mediaSourceManager = mediaSourceManager; + _localization = localizationManager; + } + + /// + public void AddParts(IEnumerable lyricProviders) + { + _lyricProviders = lyricProviders + .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) + .ToArray(); + } + + /// + public LyricResponse GetLyric(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) + { + var results = provider.GetLyrics(item); + if (results is not null) + { + return results; + } + } + + return null; + } + + /// + public bool HasLyricFile(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) + { + if (item is null) + { + continue; + } + + if (LyricInfo.GetLyricFilePath(provider, item.Path) is not null) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs similarity index 88% rename from MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs rename to MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 2a5da4e4d..939d8708b 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; +using System.Xml.Linq; using Jellyfin.Api.Helpers; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; @@ -12,19 +13,26 @@ namespace MediaBrowser.Providers.Lyric /// /// TXT File Lyric Provider. /// - public class TxtLyricsProvider : ILyricsProvider + public class TxtLyricProvider : ILyricProvider { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public TxtLyricsProvider() + public TxtLyricProvider() { + Name = "TxtLyricProvider"; + SupportedMediaTypes = new Collection { "lrc", "txt" }; } + /// + /// Gets a value indicating the provider name. + /// + public string Name { get; } + /// /// Gets a value indicating the File Extenstions this provider works with. /// From f740d1b9f00d91bfad970f56abed67d8c8c16c9c Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 16 Sep 2022 20:52:40 -0400 Subject: [PATCH 15/52] Remove use of AddParts. Cleanup use of Lyric vs Lyrics. --- .../ApplicationHost.cs | 2 +- .../Controllers/UserLibraryController.cs | 6 ++--- Jellyfin.Server/CoreAppHost.cs | 6 +++++ .../Lyrics/ILyricManager.cs | 18 ++----------- .../Lyrics/ILyricProvider.cs | 4 +-- MediaBrowser.Controller/Lyrics/Lyric.cs | 4 +-- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 8 +++--- .../Lyrics/LyricResponse.cs | 9 +++++++ .../Lyric/LrcLyricProvider.cs | 24 +++++++---------- MediaBrowser.Providers/Lyric/LyricManager.cs | 16 ++++-------- .../Lyric/TxtLyricProvider.cs | 26 +++++++------------ 11 files changed, 53 insertions(+), 70 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 409fc04b1..5edc25952 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -789,7 +789,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); - Resolve().AddParts(GetExports()); + //Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 1421ab444..2cb2e9328 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -394,10 +394,10 @@ namespace Jellyfin.Api.Controllers /// Item id. /// Lyrics returned. /// Something went wrong. No Lyrics will be returned. - /// An containing the intros to play. + /// An containing the item's lyrics. [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -415,7 +415,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = _lyricManager.GetLyric(item); + var result = _lyricManager.GetLyrics(item); if (result is not null) { return Ok(result); diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 67e50b92d..984711dc2 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -19,6 +19,7 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Activity; @@ -95,6 +96,11 @@ namespace Jellyfin.Server serviceCollection.AddScoped(); + foreach (var type in GetExportTypes()) + { + serviceCollection.AddSingleton(typeof(ILyricProvider), type); + } + base.RegisterServices(serviceCollection); } diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs index 4fd11b9e0..c0f78d177 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -1,37 +1,23 @@ -#nullable disable - #pragma warning disable CS1591 -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Lyrics { public interface ILyricManager { - /// - /// Adds the parts. - /// - /// The lyric providers. - void AddParts(IEnumerable lyricProviders); - /// /// Gets the lyrics. /// /// The media item. /// Lyrics for passed item. - LyricResponse GetLyric(BaseItem item); + LyricResponse GetLyrics(BaseItem item); /// /// Checks if requested item has a matching local lyric file. /// /// The media item. - /// True if item has a matching lyrics file; otherwise false. + /// True if item has a matching lyric file; otherwise false. bool HasLyricFile(BaseItem item); } } diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index 691fed1fd..5e677ab26 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -22,8 +22,8 @@ namespace MediaBrowser.Controller.Lyrics /// /// Gets the lyrics. /// - /// The item to to process. - /// Task{LyricResponse}. + /// The media item. + /// If found, returns lyrics for passed item; otherwise, null. LyricResponse? GetLyrics(BaseItem item); } } diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs index d44546dd3..56a0a8a72 100644 --- a/MediaBrowser.Controller/Lyrics/Lyric.cs +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -1,12 +1,12 @@ namespace MediaBrowser.Controller.Lyrics { /// - /// Lyric dto. + /// Lyric model. /// public class Lyric { /// - /// Gets or sets the start time (ticks). + /// Gets or sets the start time in ticks. /// public double? Start { get; set; } diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index d44e14237..018f296b1 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -11,16 +11,16 @@ using Microsoft.AspNetCore.Mvc; namespace MediaBrowser.Controller.Lyrics { /// - /// Item helper. + /// Lyric helper methods. /// public static class LyricInfo { /// - /// Checks if requested item has a matching lyric file. + /// Gets matching lyric file for a requested item. /// - /// The current lyricProvider interface. + /// The lyricProvider interface to use. /// Path of requested item. - /// True if item has a matching lyrics file. + /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) { if (lyricProvider.SupportedMediaTypes.Any()) diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index e312638ec..498eb873c 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -6,10 +6,19 @@ using System.Collections.Generic; namespace MediaBrowser.Controller.Lyrics { + /// + /// LyricResponse model. + /// public class LyricResponse { + /// + /// Gets or sets MetaData. + /// public IDictionary MetaData { get; set; } + /// + /// Gets or sets Lyrics. + /// public IEnumerable Lyrics { get; set; } } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 18a85c93a..10db10ac6 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -14,7 +14,7 @@ using MediaBrowser.Controller.Lyrics; namespace MediaBrowser.Providers.Lyric { /// - /// LRC File Lyric Provider. + /// LRC Lyric Provider. /// public class LrcLyricProvider : ILyricProvider { @@ -37,21 +37,15 @@ namespace MediaBrowser.Providers.Lyric public string Name { get; } /// - /// Gets a value indicating the File Extenstions this provider works with. + /// Gets a value indicating the File Extenstions this provider supports. /// public IEnumerable SupportedMediaTypes { get; } /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. + /// Opens lyric file for the requested item, and processes it for API return. /// /// The item to to process. - /// A representing the asynchronous operation. + /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); @@ -61,9 +55,9 @@ namespace MediaBrowser.Providers.Lyric return null; } - List lyricsList = new List(); - + List lyricList = new List(); List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); @@ -105,15 +99,15 @@ namespace MediaBrowser.Providers.Lyric { var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + lyricList.Add(new Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); } if (metaData.Any()) { - return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + return new LyricResponse { MetaData = metaData, Lyrics = lyricList }; } - return new LyricResponse { Lyrics = lyricsList }; + return new LyricResponse { Lyrics = lyricList }; } } } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 48572c63e..698da4686 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -36,32 +36,26 @@ namespace MediaBrowser.Providers.Lyric private readonly IMediaSourceManager _mediaSourceManager; private readonly ILocalizationManager _localization; - private ILyricProvider[] _lyricProviders; + private IEnumerable _lyricProviders; public LyricManager( ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, IMediaSourceManager mediaSourceManager, - ILocalizationManager localizationManager) + ILocalizationManager localizationManager, + IEnumerable lyricProviders) { _logger = logger; _fileSystem = fileSystem; _monitor = monitor; _mediaSourceManager = mediaSourceManager; _localization = localizationManager; + _lyricProviders = lyricProviders; } /// - public void AddParts(IEnumerable lyricProviders) - { - _lyricProviders = lyricProviders - .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) - .ToArray(); - } - - /// - public LyricResponse GetLyric(BaseItem item) + public LyricResponse GetLyrics(BaseItem item) { foreach (ILyricProvider provider in _lyricProviders) { diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 939d8708b..aa222ed97 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -11,7 +11,7 @@ using MediaBrowser.Controller.Lyrics; namespace MediaBrowser.Providers.Lyric { /// - /// TXT File Lyric Provider. + /// TXT Lyric Provider. /// public class TxtLyricProvider : ILyricProvider { @@ -34,21 +34,15 @@ namespace MediaBrowser.Providers.Lyric public string Name { get; } /// - /// Gets a value indicating the File Extenstions this provider works with. + /// Gets a value indicating the File Extenstions this provider supports. /// public IEnumerable SupportedMediaTypes { get; } /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. + /// Opens lyric file for the requested item, and processes it for API return. /// /// The item to to process. - /// A representing the asynchronous operation. + /// If provider can determine lyrics, returns a ; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); @@ -58,25 +52,25 @@ namespace MediaBrowser.Providers.Lyric return null; } - List lyricsList = new List(); + List lyricList = new List(); string lyricData = System.IO.File.ReadAllText(lyricFilePath); // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLinedelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + char[] newLineDelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLineDelims, StringSplitOptions.RemoveEmptyEntries); if (!lyricTextLines.Any()) { return null; } - foreach (string lyricLine in lyricTextLines) + foreach (string lyricTextLine in lyricTextLines) { - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + lyricList.Add(new Controller.Lyrics.Lyric { Text = lyricTextLine }); } - return new LyricResponse { Lyrics = lyricsList }; + return new LyricResponse { Lyrics = lyricList }; } } } From 8912f618f59c1e798e406b8ed7fed4504e2c2de3 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 16 Sep 2022 21:11:28 -0400 Subject: [PATCH 16/52] Change API GetLyrics return type --- Jellyfin.Api/Controllers/UserLibraryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 2cb2e9328..df91a8efc 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -397,7 +397,7 @@ namespace Jellyfin.Api.Controllers /// An containing the item's lyrics. [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public ActionResult GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); From 9350fa40bda4464a1a5f01fee7f2f72415b7f5d4 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 08:46:09 -0400 Subject: [PATCH 17/52] Convert _lyricProviders to an array. --- MediaBrowser.Providers/Lyric/LyricManager.cs | 42 ++------------------ 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 698da4686..f5560b054 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -2,56 +2,20 @@ #pragma warning disable CS1591 -using System; using System.Collections.Generic; -using System.Globalization; -using System.IO; using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Extensions; -using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Lyrics; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Subtitles; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Providers; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Lyric { public class LyricManager : ILyricManager { - private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; - private readonly ILibraryMonitor _monitor; - private readonly IMediaSourceManager _mediaSourceManager; - private readonly ILocalizationManager _localization; + private readonly ILyricProvider[] _lyricProviders; - private IEnumerable _lyricProviders; - - public LyricManager( - ILogger logger, - IFileSystem fileSystem, - ILibraryMonitor monitor, - IMediaSourceManager mediaSourceManager, - ILocalizationManager localizationManager, - IEnumerable lyricProviders) + public LyricManager(IEnumerable lyricProviders) { - _logger = logger; - _fileSystem = fileSystem; - _monitor = monitor; - _mediaSourceManager = mediaSourceManager; - _localization = localizationManager; - _lyricProviders = lyricProviders; + _lyricProviders = lyricProviders.ToArray(); } /// From 823e2ec029d8708b71452afc7442524823d82acb Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 09:22:07 -0400 Subject: [PATCH 18/52] Removing unused lines --- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 7 ------- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 2 -- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 2 -- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 3 --- 4 files changed, 14 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 018f296b1..ae831b4d2 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Lyrics; -using MediaBrowser.Controller.Net; -using Microsoft.AspNetCore.Mvc; namespace MediaBrowser.Controller.Lyrics { diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index 498eb873c..59ee5c7f2 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System.Collections.Generic; namespace MediaBrowser.Controller.Lyrics diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 10db10ac6..ea42d7525 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -4,8 +4,6 @@ using System.Collections.ObjectModel; using System.Dynamic; using System.Globalization; using System.Linq; -using System.Threading.Tasks; -using Jellyfin.Api.Helpers; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index aa222ed97..8a51d7277 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -2,9 +2,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Threading.Tasks; -using System.Xml.Linq; -using Jellyfin.Api.Helpers; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; From 7d886116fd3b617cae6884a33b8b545358fa6289 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 10:42:48 -0400 Subject: [PATCH 19/52] Var type refinements --- MediaBrowser.Controller/Lyrics/Lyric.cs | 2 +- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 4 ++-- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 13 +++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs index 56a0a8a72..35cdabbb9 100644 --- a/MediaBrowser.Controller/Lyrics/Lyric.cs +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -8,7 +8,7 @@ namespace MediaBrowser.Controller.Lyrics /// /// Gets or sets the start time in ticks. /// - public double? Start { get; set; } + public long? Start { get; set; } /// /// Gets or sets the text. diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index 59ee5c7f2..796ca3bc3 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -10,9 +10,9 @@ namespace MediaBrowser.Controller.Lyrics public class LyricResponse { /// - /// Gets or sets MetaData. + /// Gets or sets Metadata. /// - public IDictionary MetaData { get; set; } + public IDictionary Metadata { get; set; } /// /// Gets or sets Lyrics. diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index ea42d7525..59a172cee 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -8,6 +8,7 @@ using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; +using Swashbuckle.AspNetCore.SwaggerGen; namespace MediaBrowser.Providers.Lyric { @@ -56,7 +57,7 @@ namespace MediaBrowser.Providers.Lyric List lyricList = new List(); List sortedLyricData = new List(); - var metaData = new ExpandoObject() as IDictionary; + IDictionary metaData = new Dictionary(); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); try @@ -77,8 +78,8 @@ namespace MediaBrowser.Providers.Lyric { var metaDataField = metaDataRow.Split(":"); - string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); - string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); + string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal).Trim(); + string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal).Trim(); metaData.Add(metaDataFieldName, metaDataFieldValue); } @@ -96,13 +97,13 @@ namespace MediaBrowser.Providers.Lyric for (int i = 0; i < sortedLyricData.Count; i++) { var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricList.Add(new Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + long ticks = Convert.ToInt64(timeData, new NumberFormatInfo()) * 10000; + lyricList.Add(new Controller.Lyrics.Lyric { Start = ticks, Text = sortedLyricData[i].Text }); } if (metaData.Any()) { - return new LyricResponse { MetaData = metaData, Lyrics = lyricList }; + return new LyricResponse { Metadata = metaData, Lyrics = lyricList }; } return new LyricResponse { Lyrics = lyricList }; From 29932466a9e75f9cf54332be9bb9d39dce238d07 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 10:55:04 -0400 Subject: [PATCH 20/52] Remove commented out code --- Emby.Server.Implementations/ApplicationHost.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5edc25952..3c3c90e61 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -789,7 +789,6 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); - //Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); From c65819221d9a84ec0ae69a243fdcb17bce7aa65f Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 17:37:38 -0400 Subject: [PATCH 21/52] Code cleanups. Remove pragma commands --- .../Lyrics/ILyricManager.cs | 38 ++-- .../Lyrics/ILyricProvider.cs | 39 ++-- MediaBrowser.Controller/Lyrics/Lyric.cs | 34 ++-- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 39 ++-- .../Lyrics/LyricResponse.cs | 27 ++- .../Lyric/LrcLyricProvider.cs | 172 +++++++++--------- MediaBrowser.Providers/Lyric/LyricManager.cs | 72 ++++---- .../Lyric/TxtLyricProvider.cs | 96 ++++------ .../MediaBrowser.Providers.csproj | 1 - 9 files changed, 247 insertions(+), 271 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs index c0f78d177..dad0250f6 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -1,23 +1,23 @@ -#pragma warning disable CS1591 - using MediaBrowser.Controller.Entities; -namespace MediaBrowser.Controller.Lyrics -{ - public interface ILyricManager - { - /// - /// Gets the lyrics. - /// - /// The media item. - /// Lyrics for passed item. - LyricResponse GetLyrics(BaseItem item); +namespace MediaBrowser.Controller.Lyrics; - /// - /// Checks if requested item has a matching local lyric file. - /// - /// The media item. - /// True if item has a matching lyric file; otherwise false. - bool HasLyricFile(BaseItem item); - } +/// +/// Interface ILyricManager. +/// +public interface ILyricManager +{ + /// + /// Gets the lyrics. + /// + /// The media item. + /// Lyrics for passed item. + LyricResponse GetLyrics(BaseItem item); + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// The media item. + /// True if item has a matching lyric file; otherwise false. + bool HasLyricFile(BaseItem item); } diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index 5e677ab26..1b52de255 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -1,29 +1,28 @@ using System.Collections.Generic; using MediaBrowser.Controller.Entities; -namespace MediaBrowser.Controller.Lyrics +namespace MediaBrowser.Controller.Lyrics; + +/// +/// Interface ILyricsProvider. +/// +public interface ILyricProvider { /// - /// Interface ILyricsProvider. + /// Gets a value indicating the provider name. /// - public interface ILyricProvider - { - /// - /// Gets a value indicating the provider name. - /// - string Name { get; } + string Name { get; } - /// - /// Gets the supported media types for this provider. - /// - /// The supported media types. - IEnumerable SupportedMediaTypes { get; } + /// + /// Gets the supported media types for this provider. + /// + /// The supported media types. + IEnumerable SupportedMediaTypes { get; } - /// - /// Gets the lyrics. - /// - /// The media item. - /// If found, returns lyrics for passed item; otherwise, null. - LyricResponse? GetLyrics(BaseItem item); - } + /// + /// Gets the lyrics. + /// + /// The media item. + /// If found, returns lyrics for passed item; otherwise, null. + LyricResponse? GetLyrics(BaseItem item); } diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs index 35cdabbb9..f39fbb022 100644 --- a/MediaBrowser.Controller/Lyrics/Lyric.cs +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -1,18 +1,28 @@ -namespace MediaBrowser.Controller.Lyrics +namespace MediaBrowser.Controller.Lyrics; + +/// +/// Lyric model. +/// +public class Lyric { /// - /// Lyric model. + /// Initializes a new instance of the class. /// - public class Lyric + /// The lyric start time in ticks. + /// The lyric text. + public Lyric(string text, long? start = null) { - /// - /// Gets or sets the start time in ticks. - /// - public long? Start { get; set; } - - /// - /// Gets or sets the text. - /// - public string Text { get; set; } = string.Empty; + Start = start; + Text = text; } + + /// + /// Gets the start time in ticks. + /// + public long? Start { get; } + + /// + /// Gets the text. + /// + public string Text { get; } } diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index ae831b4d2..61e205b6c 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -1,34 +1,29 @@ using System.IO; -using System.Linq; -namespace MediaBrowser.Controller.Lyrics +namespace MediaBrowser.Controller.Lyrics; + +/// +/// Lyric helper methods. +/// +public static class LyricInfo { /// - /// Lyric helper methods. + /// Gets matching lyric file for a requested item. /// - public static class LyricInfo + /// The lyricProvider interface to use. + /// Path of requested item. + /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. + public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) { - /// - /// Gets matching lyric file for a requested item. - /// - /// The lyricProvider interface to use. - /// Path of requested item. - /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. - public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) + foreach (string lyricFileExtension in lyricProvider.SupportedMediaTypes) { - if (lyricProvider.SupportedMediaTypes.Any()) + var lyricFilePath = Path.ChangeExtension(itemPath, lyricFileExtension); + if (File.Exists(lyricFilePath)) { - foreach (string lyricFileExtension in lyricProvider.SupportedMediaTypes) - { - string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); - if (System.IO.File.Exists(lyricFilePath)) - { - return lyricFilePath; - } - } + return lyricFilePath; } - - return null; } + + return null; } } diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index 796ca3bc3..e18cb1101 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -1,22 +1,19 @@ -#nullable disable - using System.Collections.Generic; -namespace MediaBrowser.Controller.Lyrics +namespace MediaBrowser.Controller.Lyrics; + +/// +/// LyricResponse model. +/// +public class LyricResponse { /// - /// LyricResponse model. + /// Gets or sets Metadata. /// - public class LyricResponse - { - /// - /// Gets or sets Metadata. - /// - public IDictionary Metadata { get; set; } + public IDictionary? Metadata { get; set; } - /// - /// Gets or sets Lyrics. - /// - public IEnumerable Lyrics { get; set; } - } + /// + /// Gets or sets Lyrics. + /// + public IEnumerable? Lyrics { get; set; } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 59a172cee..9bacfc296 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -1,112 +1,102 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Dynamic; using System.Globalization; using System.Linq; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; -using Swashbuckle.AspNetCore.SwaggerGen; -namespace MediaBrowser.Providers.Lyric +namespace MediaBrowser.Providers.Lyric; + +/// +/// LRC Lyric Provider. +/// +public class LrcLyricProvider : ILyricProvider { - /// - /// LRC Lyric Provider. - /// - public class LrcLyricProvider : ILyricProvider - { - /// - /// Initializes a new instance of the class. - /// - public LrcLyricProvider() - { - Name = "LrcLyricProvider"; + /// + public string Name { get; } = "LrcLyricProvider"; - SupportedMediaTypes = new Collection + /// + public IEnumerable SupportedMediaTypes + { + get => new Collection { "lrc" }; - } + } - /// - /// Gets a value indicating the provider name. - /// - public string Name { get; } + /// + /// Opens lyric file for the requested item, and processes it for API return. + /// + /// The item to to process. + /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - /// - /// Gets a value indicating the File Extenstions this provider supports. - /// - public IEnumerable SupportedMediaTypes { get; } - - /// - /// Opens lyric file for the requested item, and processes it for API return. - /// - /// The item to to process. - /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. - public LyricResponse? GetLyrics(BaseItem item) + if (string.IsNullOrEmpty(lyricFilePath)) { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - List lyricList = new List(); - List sortedLyricData = new List(); - - IDictionary metaData = new Dictionary(); - string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); - - try - { - // Parse and sort lyric rows - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - Song lyricData = lrcLyricParser.Decode(lrcFileContent); - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); - - // Parse metadata rows - var metaDataRows = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) - .Select(x => x.Text) - .ToList(); - - foreach (string metaDataRow in metaDataRows) - { - var metaDataField = metaDataRow.Split(":"); - - string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal).Trim(); - string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal).Trim(); - - metaData.Add(metaDataFieldName, metaDataFieldValue); - } - } - catch - { - return null; - } - - if (!sortedLyricData.Any()) - { - return null; - } - - for (int i = 0; i < sortedLyricData.Count; i++) - { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - long ticks = Convert.ToInt64(timeData, new NumberFormatInfo()) * 10000; - lyricList.Add(new Controller.Lyrics.Lyric { Start = ticks, Text = sortedLyricData[i].Text }); - } - - if (metaData.Any()) - { - return new LyricResponse { Metadata = metaData, Lyrics = lyricList }; - } - - return new LyricResponse { Lyrics = lyricList }; + return null; } + + List lyricList = new List(); + List sortedLyricData = new List(); + + IDictionary metaData = new Dictionary(); + string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + + try + { + // Parse and sort lyric rows + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + Song lyricData = lrcLyricParser.Decode(lrcFileContent); + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.First().Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith('[') && x.Text.EndsWith(']')) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + var metaDataField = metaDataRow.Split(':'); + if (metaDataField.Length != 2) + { + continue; + } + + string metaDataFieldName = metaDataField[0][1..].Trim(); + string metaDataFieldValue = metaDataField[1][..^1].Trim(); + + metaData.Add(metaDataFieldName, metaDataFieldValue); + } + } + catch + { + return null; + } + + if (sortedLyricData.Count == 0) + { + return null; + } + + for (int i = 0; i < sortedLyricData.Count; i++) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + long ticks = TimeSpan.FromMilliseconds((double)timeData).Ticks; + lyricList.Add(new Controller.Lyrics.Lyric(sortedLyricData[i].Text, ticks)); + } + + if (metaData.Any()) + { + return new LyricResponse { Metadata = metaData, Lyrics = lyricList }; + } + + return new LyricResponse { Lyrics = lyricList }; } } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index f5560b054..06f913d07 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -1,55 +1,57 @@ -#nullable disable - -#pragma warning disable CS1591 - using System.Collections.Generic; using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; -namespace MediaBrowser.Providers.Lyric +namespace MediaBrowser.Providers.Lyric; + +/// +/// Lyric Manager. +/// +public class LyricManager : ILyricManager { - public class LyricManager : ILyricManager + private readonly ILyricProvider[] _lyricProviders; + + /// + /// Initializes a new instance of the class. + /// + /// All found lyricProviders. + public LyricManager(IEnumerable lyricProviders) { - private readonly ILyricProvider[] _lyricProviders; + _lyricProviders = lyricProviders.ToArray(); + } - public LyricManager(IEnumerable lyricProviders) + /// + public LyricResponse GetLyrics(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) { - _lyricProviders = lyricProviders.ToArray(); + var results = provider.GetLyrics(item); + if (results is not null) + { + return results; + } } - /// - public LyricResponse GetLyrics(BaseItem item) + return null; + } + + /// + public bool HasLyricFile(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) { - foreach (ILyricProvider provider in _lyricProviders) + if (item is null) { - var results = provider.GetLyrics(item); - if (results is not null) - { - return results; - } + continue; } - return null; - } - - /// - public bool HasLyricFile(BaseItem item) - { - foreach (ILyricProvider provider in _lyricProviders) + if (LyricInfo.GetLyricFilePath(provider, item.Path) is not null) { - if (item is null) - { - continue; - } - - if (LyricInfo.GetLyricFilePath(provider, item.Path) is not null) - { - return true; - } + return true; } - - return false; } + + return false; } } diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 8a51d7277..d417c8598 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -5,69 +5,53 @@ using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; -namespace MediaBrowser.Providers.Lyric -{ - /// - /// TXT Lyric Provider. - /// - public class TxtLyricProvider : ILyricProvider - { - /// - /// Initializes a new instance of the class. - /// - public TxtLyricProvider() - { - Name = "TxtLyricProvider"; +namespace MediaBrowser.Providers.Lyric; - SupportedMediaTypes = new Collection +/// +/// TXT Lyric Provider. +/// +public class TxtLyricProvider : ILyricProvider +{ + /// + public string Name { get; } = "TxtLyricProvider"; + + /// + public IEnumerable SupportedMediaTypes + { + get => new Collection { "lrc", "txt" }; - } + } - /// - /// Gets a value indicating the provider name. - /// - public string Name { get; } + /// + /// Opens lyric file for the requested item, and processes it for API return. + /// + /// The item to to process. + /// If provider can determine lyrics, returns a ; otherwise, null. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - /// - /// Gets a value indicating the File Extenstions this provider supports. - /// - public IEnumerable SupportedMediaTypes { get; } - - /// - /// Opens lyric file for the requested item, and processes it for API return. - /// - /// The item to to process. - /// If provider can determine lyrics, returns a ; otherwise, null. - public LyricResponse? GetLyrics(BaseItem item) + if (string.IsNullOrEmpty(lyricFilePath)) { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - List lyricList = new List(); - - string lyricData = System.IO.File.ReadAllText(lyricFilePath); - - // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLineDelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLineDelims, StringSplitOptions.RemoveEmptyEntries); - - if (!lyricTextLines.Any()) - { - return null; - } - - foreach (string lyricTextLine in lyricTextLines) - { - lyricList.Add(new Controller.Lyrics.Lyric { Text = lyricTextLine }); - } - - return new LyricResponse { Lyrics = lyricList }; + return null; } + + string[] lyricTextLines = System.IO.File.ReadAllLines(lyricFilePath); + + List lyricList = new List(); + + if (lyricTextLines.Length == 0) + { + return null; + } + + foreach (string lyricTextLine in lyricTextLines) + { + lyricList.Add(new Controller.Lyrics.Lyric(lyricTextLine)); + } + + return new LyricResponse { Lyrics = lyricList }; } } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 8514489f8..e12776ba0 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -6,7 +6,6 @@ - From 64b013b121f472d2658e5f2b3a672c4c4ced342d Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 17:48:27 -0400 Subject: [PATCH 22/52] Resolve Azure build issues --- MediaBrowser.Controller/Lyrics/ILyricManager.cs | 2 +- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 7 ++++++- MediaBrowser.Providers/Lyric/LyricManager.cs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs index dad0250f6..5920bcc62 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -12,7 +12,7 @@ public interface ILyricManager /// /// The media item. /// Lyrics for passed item. - LyricResponse GetLyrics(BaseItem item); + LyricResponse? GetLyrics(BaseItem item); /// /// Checks if requested item has a matching local lyric file. diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 9bacfc296..db87d9236 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -87,7 +87,12 @@ public class LrcLyricProvider : ILyricProvider for (int i = 0; i < sortedLyricData.Count; i++) { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + var timeData = sortedLyricData[i].TimeTags.First().Value; + if (timeData is null) + { + continue; + } + long ticks = TimeSpan.FromMilliseconds((double)timeData).Ticks; lyricList.Add(new Controller.Lyrics.Lyric(sortedLyricData[i].Text, ticks)); } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 06f913d07..0de008db7 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -22,7 +22,7 @@ public class LyricManager : ILyricManager } /// - public LyricResponse GetLyrics(BaseItem item) + public LyricResponse? GetLyrics(BaseItem item) { foreach (ILyricProvider provider in _lyricProviders) { From 0b86630be7737e28555f0132322ecd02915284c5 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 19:47:29 -0400 Subject: [PATCH 23/52] Use model properties for LRC metadata --- .../Lyrics/LyricResponse.cs | 2 +- MediaBrowser.Controller/Lyrics/Metadata.cs | 54 +++++++++++++++++++ .../MediaBrowser.Controller.csproj | 1 + .../Lyric/LrcLyricProvider.cs | 11 ++-- 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 MediaBrowser.Controller/Lyrics/Metadata.cs diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index e18cb1101..adc13050e 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -10,7 +10,7 @@ public class LyricResponse /// /// Gets or sets Metadata. /// - public IDictionary? Metadata { get; set; } + public Metadata? Metadata { get; set; } /// /// Gets or sets Lyrics. diff --git a/MediaBrowser.Controller/Lyrics/Metadata.cs b/MediaBrowser.Controller/Lyrics/Metadata.cs new file mode 100644 index 000000000..114b56777 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/Metadata.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Lyrics; + +/// +/// Metadata model. +/// +public class Metadata +{ + /// + /// Gets or sets Artist - [ar:The song artist]. + /// + public string? Ar { get; set; } + + /// + /// Gets or sets Album - [al:The album this song is on]. + /// + public string? Al { get; set; } + + /// + /// Gets or sets Title - [ti:The title of the song]. + /// + public string? Ti { get; set; } + + /// + /// Gets or sets Author - [au:Creator of the lyric data]. + /// + public string? Au { get; set; } + + /// + /// Gets or sets Length - [length:How long the song is]. + /// + public string? Length { get; set; } + + /// + /// Gets or sets By - [by:Creator of the LRC file]. + /// + public string? By { get; set; } + + /// + /// Gets or sets Offset - [offsec:+/- Timestamp adjustment in milliseconds]. + /// + public string? Offset { get; set; } + + /// + /// Gets or sets Creator - [re:The Software used to create the LRC file]. + /// + public string? Re { get; set; } + + /// + /// Gets or sets Version - [ve:The version of the Creator used]. + /// + public string? Ve { get; set; } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index d4e025a43..c08e276dc 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -18,6 +18,7 @@ + diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index db87d9236..b527c5303 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; +using AutoMapper; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; @@ -44,7 +45,8 @@ public class LrcLyricProvider : ILyricProvider List lyricList = new List(); List sortedLyricData = new List(); - IDictionary metaData = new Dictionary(); + // Must be for automapper support + IDictionary metaData = new Dictionary(); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); try @@ -69,7 +71,7 @@ public class LrcLyricProvider : ILyricProvider continue; } - string metaDataFieldName = metaDataField[0][1..].Trim(); + string metaDataFieldName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(metaDataField[0][1..].Trim().ToLowerInvariant()); string metaDataFieldValue = metaDataField[1][..^1].Trim(); metaData.Add(metaDataFieldName, metaDataFieldValue); @@ -85,6 +87,9 @@ public class LrcLyricProvider : ILyricProvider return null; } + var config = new MapperConfiguration(cfg => { }); + var mapper = config.CreateMapper(); + for (int i = 0; i < sortedLyricData.Count; i++) { var timeData = sortedLyricData[i].TimeTags.First().Value; @@ -99,7 +104,7 @@ public class LrcLyricProvider : ILyricProvider if (metaData.Any()) { - return new LyricResponse { Metadata = metaData, Lyrics = lyricList }; + return new LyricResponse { Metadata = mapper.Map(metaData), Lyrics = lyricList }; } return new LyricResponse { Lyrics = lyricList }; From a52d108af6b8519715bc7005e7db3f1a116760bc Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 11:47:57 -0400 Subject: [PATCH 24/52] Remove automapper tool --- .../Lyrics/LyricMetadata.cs | 52 +++++++++++++ .../Lyrics/LyricResponse.cs | 2 +- MediaBrowser.Controller/Lyrics/Metadata.cs | 54 ------------- .../MediaBrowser.Controller.csproj | 1 - .../Lyric/LrcLyricProvider.cs | 78 ++++++++++++++++--- 5 files changed, 120 insertions(+), 67 deletions(-) create mode 100644 MediaBrowser.Controller/Lyrics/LyricMetadata.cs delete mode 100644 MediaBrowser.Controller/Lyrics/Metadata.cs diff --git a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs new file mode 100644 index 000000000..36a833f85 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs @@ -0,0 +1,52 @@ +namespace MediaBrowser.Controller.Lyrics; + +/// +/// LyricMetadata model. +/// +public class LyricMetadata +{ + /// + /// Gets or sets Artist - The song artist. + /// + public string? Artist { get; set; } + + /// + /// Gets or sets Album - The album this song is on. + /// + public string? Album { get; set; } + + /// + /// Gets or sets Title - The title of the song. + /// + public string? Title { get; set; } + + /// + /// Gets or sets Author - Creator of the lyric data. + /// + public string? Author { get; set; } + + /// + /// Gets or sets Length - How long the song is. + /// + public string? Length { get; set; } + + /// + /// Gets or sets By - Creator of the LRC file. + /// + public string? By { get; set; } + + /// + /// Gets or sets Offset - Offset:+/- Timestamp adjustment in milliseconds. + /// + public string? Offset { get; set; } + + /// + /// Gets or sets Creator - The Software used to create the LRC file. + /// + public string? Creator { get; set; } + + /// + /// Gets or sets Version - The version of the Creator used. + /// + public string? Version { get; set; } +} diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index adc13050e..989248388 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -10,7 +10,7 @@ public class LyricResponse /// /// Gets or sets Metadata. /// - public Metadata? Metadata { get; set; } + public LyricMetadata? Metadata { get; set; } /// /// Gets or sets Lyrics. diff --git a/MediaBrowser.Controller/Lyrics/Metadata.cs b/MediaBrowser.Controller/Lyrics/Metadata.cs deleted file mode 100644 index 114b56777..000000000 --- a/MediaBrowser.Controller/Lyrics/Metadata.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Lyrics; - -/// -/// Metadata model. -/// -public class Metadata -{ - /// - /// Gets or sets Artist - [ar:The song artist]. - /// - public string? Ar { get; set; } - - /// - /// Gets or sets Album - [al:The album this song is on]. - /// - public string? Al { get; set; } - - /// - /// Gets or sets Title - [ti:The title of the song]. - /// - public string? Ti { get; set; } - - /// - /// Gets or sets Author - [au:Creator of the lyric data]. - /// - public string? Au { get; set; } - - /// - /// Gets or sets Length - [length:How long the song is]. - /// - public string? Length { get; set; } - - /// - /// Gets or sets By - [by:Creator of the LRC file]. - /// - public string? By { get; set; } - - /// - /// Gets or sets Offset - [offsec:+/- Timestamp adjustment in milliseconds]. - /// - public string? Offset { get; set; } - - /// - /// Gets or sets Creator - [re:The Software used to create the LRC file]. - /// - public string? Re { get; set; } - - /// - /// Gets or sets Version - [ve:The version of the Creator used]. - /// - public string? Ve { get; set; } -} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index c08e276dc..d4e025a43 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -18,7 +18,6 @@ - diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index b527c5303..4cc06dac8 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Globalization; using System.Linq; -using AutoMapper; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; +using Newtonsoft.Json.Linq; namespace MediaBrowser.Providers.Lyric; @@ -45,8 +44,7 @@ public class LrcLyricProvider : ILyricProvider List lyricList = new List(); List sortedLyricData = new List(); - // Must be for automapper support - IDictionary metaData = new Dictionary(); + IDictionary fileMetaData = new Dictionary(); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); try @@ -71,10 +69,10 @@ public class LrcLyricProvider : ILyricProvider continue; } - string metaDataFieldName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(metaDataField[0][1..].Trim().ToLowerInvariant()); + string metaDataFieldName = metaDataField[0][1..].Trim().ToLowerInvariant(); string metaDataFieldValue = metaDataField[1][..^1].Trim(); - metaData.Add(metaDataFieldName, metaDataFieldValue); + fileMetaData.Add(metaDataFieldName, metaDataFieldValue); } } catch @@ -87,9 +85,6 @@ public class LrcLyricProvider : ILyricProvider return null; } - var config = new MapperConfiguration(cfg => { }); - var mapper = config.CreateMapper(); - for (int i = 0; i < sortedLyricData.Count; i++) { var timeData = sortedLyricData[i].TimeTags.First().Value; @@ -102,11 +97,72 @@ public class LrcLyricProvider : ILyricProvider lyricList.Add(new Controller.Lyrics.Lyric(sortedLyricData[i].Text, ticks)); } - if (metaData.Any()) + if (fileMetaData.Any()) { - return new LyricResponse { Metadata = mapper.Map(metaData), Lyrics = lyricList }; + // Map metaData values from LRC file to LyricMetadata properties + LyricMetadata lyricMetadata = MapMetadataValues(fileMetaData); + + return new LyricResponse { Metadata = lyricMetadata, Lyrics = lyricList }; } return new LyricResponse { Lyrics = lyricList }; } + + /// + /// Converts metadata from an LRC file to LyricMetadata properties. + /// + /// The metadata from the LRC file. + /// A lyricMetadata object with mapped property data. + private LyricMetadata MapMetadataValues(IDictionary metaData) + { + LyricMetadata lyricMetadata = new LyricMetadata(); + + if (metaData.TryGetValue("ar", out var artist) && artist is not null) + { + lyricMetadata.Artist = artist; + } + + if (metaData.TryGetValue("al", out var album) && album is not null) + { + lyricMetadata.Album = album; + } + + if (metaData.TryGetValue("ti", out var title) && title is not null) + { + lyricMetadata.Title = title; + } + + if (metaData.TryGetValue("au", out var author) && author is not null) + { + lyricMetadata.Author = author; + } + + if (metaData.TryGetValue("length", out var length) && length is not null) + { + lyricMetadata.Length = length; + } + + if (metaData.TryGetValue("by", out var by) && by is not null) + { + lyricMetadata.By = by; + } + + if (metaData.TryGetValue("offset", out var offset) && offset is not null) + { + lyricMetadata.Offset = offset; + } + + if (metaData.TryGetValue("re", out var creator) && creator is not null) + { + lyricMetadata.Creator = creator; + } + + if (metaData.TryGetValue("ve", out var version) && version is not null) + { + lyricMetadata.Version = version; + } + + return lyricMetadata; + + } } From db662eeb333ea4e5c7764c4738e8e88a5efc0e8c Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:38:01 -0400 Subject: [PATCH 25/52] Update MediaBrowser.Providers/Lyric/TxtLyricProvider.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index d417c8598..e48bc6963 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -16,13 +16,7 @@ public class TxtLyricProvider : ILyricProvider public string Name { get; } = "TxtLyricProvider"; /// - public IEnumerable SupportedMediaTypes - { - get => new Collection - { - "lrc", "txt" - }; - } + public IEnumerable SupportedMediaTypes { get; } = new[] { "lrc", "txt" }; /// /// Opens lyric file for the requested item, and processes it for API return. From 1aa8b22b89ec320ff0b4215cc35e1bca60e782ba Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:38:10 -0400 Subject: [PATCH 26/52] Update MediaBrowser.Providers/Lyric/TxtLyricProvider.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index e48bc6963..a2161c427 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Providers.Lyric; public class TxtLyricProvider : ILyricProvider { /// - public string Name { get; } = "TxtLyricProvider"; + public string Name => "TxtLyricProvider"; /// public IEnumerable SupportedMediaTypes { get; } = new[] { "lrc", "txt" }; From 636835f73a544a25c0ce2f7d35682df723cf3955 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:38:24 -0400 Subject: [PATCH 27/52] Update MediaBrowser.Providers/Lyric/LrcLyricProvider.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 4cc06dac8..f86255266 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Providers.Lyric; public class LrcLyricProvider : ILyricProvider { /// - public string Name { get; } = "LrcLyricProvider"; + public string Name => "LrcLyricProvider"; /// public IEnumerable SupportedMediaTypes From baf07ddbfe971a74af3802973832d621abf55681 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:39:07 -0400 Subject: [PATCH 28/52] Update MediaBrowser.Providers/Lyric/LrcLyricProvider.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index f86255266..e47870044 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -97,7 +97,7 @@ public class LrcLyricProvider : ILyricProvider lyricList.Add(new Controller.Lyrics.Lyric(sortedLyricData[i].Text, ticks)); } - if (fileMetaData.Any()) + if (fileMetaData.Count != 0) { // Map metaData values from LRC file to LyricMetadata properties LyricMetadata lyricMetadata = MapMetadataValues(fileMetaData); From c85cbcced691faab5ce0c8864e23bfa2f5f2d696 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:39:19 -0400 Subject: [PATCH 29/52] Update MediaBrowser.Providers/Lyric/LrcLyricProvider.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index e47870044..311a8e21d 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -93,7 +93,7 @@ public class LrcLyricProvider : ILyricProvider continue; } - long ticks = TimeSpan.FromMilliseconds((double)timeData).Ticks; + long ticks = TimeSpan.FromMilliseconds(timeData.Value).Ticks; lyricList.Add(new Controller.Lyrics.Lyric(sortedLyricData[i].Text, ticks)); } From 10b07ed9a582a06d947f6fb7ec2ab9fd94215587 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 12:39:38 -0400 Subject: [PATCH 30/52] Update MediaBrowser.Providers/Lyric/LrcLyricProvider.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 311a8e21d..90396e553 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -19,13 +19,7 @@ public class LrcLyricProvider : ILyricProvider public string Name => "LrcLyricProvider"; /// - public IEnumerable SupportedMediaTypes - { - get => new Collection - { - "lrc" - }; - } + public IEnumerable SupportedMediaTypes { get; } = new[] { "lrc" }; /// /// Opens lyric file for the requested item, and processes it for API return. From f737581d49dd9f6ab0c68269f8be073df27fb0ba Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 13:13:01 -0400 Subject: [PATCH 31/52] Use providers in order of priority --- .../Lyrics/ILyricProvider.cs | 7 +++ .../Lyric/LrcLyricProvider.cs | 48 ++++++++++++------- MediaBrowser.Providers/Lyric/LyricManager.cs | 2 +- .../Lyric/TxtLyricProvider.cs | 7 +++ 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index 1b52de255..651fe507f 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Resolvers; namespace MediaBrowser.Controller.Lyrics; @@ -13,6 +14,12 @@ public interface ILyricProvider /// string Name { get; } + /// + /// Gets the priority. + /// + /// The priority. + ResolverPriority Priority { get; } + /// /// Gets the supported media types for this provider. /// diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 90396e553..4690c3e20 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; -using Newtonsoft.Json.Linq; +using MediaBrowser.Controller.Resolvers; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Lyric; @@ -15,9 +15,26 @@ namespace MediaBrowser.Providers.Lyric; /// public class LrcLyricProvider : ILyricProvider { + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + public LrcLyricProvider(ILogger logger) + { + _logger = logger; + } + /// public string Name => "LrcLyricProvider"; + /// + /// Gets the priority. + /// + /// The priority. + public ResolverPriority Priority => ResolverPriority.First; + /// public IEnumerable SupportedMediaTypes { get; } = new[] { "lrc" }; @@ -38,7 +55,7 @@ public class LrcLyricProvider : ILyricProvider List lyricList = new List(); List sortedLyricData = new List(); - IDictionary fileMetaData = new Dictionary(); + IDictionary fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); try @@ -63,15 +80,15 @@ public class LrcLyricProvider : ILyricProvider continue; } - string metaDataFieldName = metaDataField[0][1..].Trim().ToLowerInvariant(); + string metaDataFieldName = metaDataField[0][1..].Trim(); string metaDataFieldValue = metaDataField[1][..^1].Trim(); fileMetaData.Add(metaDataFieldName, metaDataFieldValue); } } - catch + catch (Exception ex) { - return null; + _logger.LogError(ex, "Error parsing lyric data from {Provider}", Name); } if (sortedLyricData.Count == 0) @@ -111,52 +128,51 @@ public class LrcLyricProvider : ILyricProvider { LyricMetadata lyricMetadata = new LyricMetadata(); - if (metaData.TryGetValue("ar", out var artist) && artist is not null) + if (metaData.TryGetValue("ar", out var artist) && !string.IsNullOrEmpty(artist)) { lyricMetadata.Artist = artist; } - if (metaData.TryGetValue("al", out var album) && album is not null) + if (metaData.TryGetValue("al", out var album) && !string.IsNullOrEmpty(album)) { lyricMetadata.Album = album; } - if (metaData.TryGetValue("ti", out var title) && title is not null) + if (metaData.TryGetValue("ti", out var title) && !string.IsNullOrEmpty(title)) { lyricMetadata.Title = title; } - if (metaData.TryGetValue("au", out var author) && author is not null) + if (metaData.TryGetValue("au", out var author) && !string.IsNullOrEmpty(author)) { lyricMetadata.Author = author; } - if (metaData.TryGetValue("length", out var length) && length is not null) + if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length)) { lyricMetadata.Length = length; } - if (metaData.TryGetValue("by", out var by) && by is not null) + if (metaData.TryGetValue("by", out var by) && !string.IsNullOrEmpty(by)) { lyricMetadata.By = by; } - if (metaData.TryGetValue("offset", out var offset) && offset is not null) + if (metaData.TryGetValue("offset", out var offset) && !string.IsNullOrEmpty(offset)) { lyricMetadata.Offset = offset; } - if (metaData.TryGetValue("re", out var creator) && creator is not null) + if (metaData.TryGetValue("re", out var creator) && !string.IsNullOrEmpty(creator)) { lyricMetadata.Creator = creator; } - if (metaData.TryGetValue("ve", out var version) && version is not null) + if (metaData.TryGetValue("ve", out var version) && !string.IsNullOrEmpty(version)) { lyricMetadata.Version = version; } return lyricMetadata; - } } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 0de008db7..7487c6861 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -18,7 +18,7 @@ public class LyricManager : ILyricManager /// All found lyricProviders. public LyricManager(IEnumerable lyricProviders) { - _lyricProviders = lyricProviders.ToArray(); + _lyricProviders = lyricProviders.OrderBy(i => i.Priority).ToArray(); } /// diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index a2161c427..c69008686 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Resolvers; namespace MediaBrowser.Providers.Lyric; @@ -15,6 +16,12 @@ public class TxtLyricProvider : ILyricProvider /// public string Name => "TxtLyricProvider"; + /// + /// Gets the priority. + /// + /// The priority. + public ResolverPriority Priority => ResolverPriority.Second; + /// public IEnumerable SupportedMediaTypes { get; } = new[] { "lrc", "txt" }; From dce81d88182c90fdce90f2690b3adb22485fecd4 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 14:53:25 -0400 Subject: [PATCH 32/52] Update MediaBrowser.Controller/Lyrics/LyricResponse.cs Co-authored-by: Niels van Velzen --- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index 989248388..f3d8b07bc 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -10,7 +10,7 @@ public class LyricResponse /// /// Gets or sets Metadata. /// - public LyricMetadata? Metadata { get; set; } + public LyricMetadata Metadata { get; set; } /// /// Gets or sets Lyrics. From dddebec794c72242952cb45e84fed452d828a641 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 14:53:36 -0400 Subject: [PATCH 33/52] Update MediaBrowser.Controller/Lyrics/LyricResponse.cs Co-authored-by: Niels van Velzen --- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index f3d8b07bc..405e8cac1 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -15,5 +15,5 @@ public class LyricResponse /// /// Gets or sets Lyrics. /// - public IEnumerable? Lyrics { get; set; } + public IEnumerable Lyrics { get; set; } } From 7e923e268865d8c0933a22247424d205429a474b Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 16:05:50 -0400 Subject: [PATCH 34/52] Use numeric values for metadata values --- .../Lyrics/ILyricProvider.cs | 2 +- .../Lyrics/{Lyric.cs => LyricLine.cs} | 6 +-- .../Lyrics/LyricMetadata.cs | 6 ++- .../Lyrics/LyricResponse.cs | 2 +- .../Lyric/LrcLyricProvider.cs | 53 +++++++++++++++---- .../Lyric/TxtLyricProvider.cs | 9 ++-- 6 files changed, 56 insertions(+), 22 deletions(-) rename MediaBrowser.Controller/Lyrics/{Lyric.cs => LyricLine.cs} (77%) diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index 651fe507f..c5b625226 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -24,7 +24,7 @@ public interface ILyricProvider /// Gets the supported media types for this provider. /// /// The supported media types. - IEnumerable SupportedMediaTypes { get; } + IReadOnlyCollection SupportedMediaTypes { get; } /// /// Gets the lyrics. diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/LyricLine.cs similarity index 77% rename from MediaBrowser.Controller/Lyrics/Lyric.cs rename to MediaBrowser.Controller/Lyrics/LyricLine.cs index f39fbb022..43997f656 100644 --- a/MediaBrowser.Controller/Lyrics/Lyric.cs +++ b/MediaBrowser.Controller/Lyrics/LyricLine.cs @@ -3,14 +3,14 @@ namespace MediaBrowser.Controller.Lyrics; /// /// Lyric model. /// -public class Lyric +public class LyricLine { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The lyric start time in ticks. /// The lyric text. - public Lyric(string text, long? start = null) + public LyricLine(string text, long? start = null) { Start = start; Text = text; diff --git a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs index 36a833f85..0ba777975 100644 --- a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs +++ b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs @@ -1,3 +1,5 @@ +using System; + namespace MediaBrowser.Controller.Lyrics; /// @@ -28,7 +30,7 @@ public class LyricMetadata /// /// Gets or sets Length - How long the song is. /// - public string? Length { get; set; } + public long? Length { get; set; } /// /// Gets or sets By - Creator of the LRC file. @@ -38,7 +40,7 @@ public class LyricMetadata /// /// Gets or sets Offset - Offset:+/- Timestamp adjustment in milliseconds. /// - public string? Offset { get; set; } + public long? Offset { get; set; } /// /// Gets or sets Creator - The Software used to create the LRC file. diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index 405e8cac1..b3c65ac8c 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -15,5 +15,5 @@ public class LyricResponse /// /// Gets or sets Lyrics. /// - public IEnumerable Lyrics { get; set; } + public IEnumerable Lyrics { get; set; } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 4690c3e20..50fa519b3 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Resolvers; +using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Lyric; @@ -36,7 +38,7 @@ public class LrcLyricProvider : ILyricProvider public ResolverPriority Priority => ResolverPriority.First; /// - public IEnumerable SupportedMediaTypes { get; } = new[] { "lrc" }; + public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc" }; /// /// Opens lyric file for the requested item, and processes it for API return. @@ -52,7 +54,7 @@ public class LrcLyricProvider : ILyricProvider return null; } - List lyricList = new List(); + List lyricList = new List(); List sortedLyricData = new List(); IDictionary fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -74,14 +76,28 @@ public class LrcLyricProvider : ILyricProvider foreach (string metaDataRow in metaDataRows) { - var metaDataField = metaDataRow.Split(':'); - if (metaDataField.Length != 2) + int colonCount = metaDataRow.Count(f => (f == ':')); + if (colonCount == 0) { continue; } - string metaDataFieldName = metaDataField[0][1..].Trim(); - string metaDataFieldValue = metaDataField[1][..^1].Trim(); + string[] metaDataField; + string metaDataFieldName; + string metaDataFieldValue; + + if (colonCount == 1) + { + metaDataField = metaDataRow.Split(':'); + metaDataFieldName = metaDataField[0][1..].Trim(); + metaDataFieldValue = metaDataField[1][..^1].Trim(); + } + else + { + int colonIndex = metaDataRow.IndexOf(':', StringComparison.OrdinalIgnoreCase); + metaDataFieldName = metaDataRow[..colonIndex][1..].Trim(); + metaDataFieldValue = metaDataRow[(colonIndex + 1)..][..^1].Trim(); + } fileMetaData.Add(metaDataFieldName, metaDataFieldValue); } @@ -105,7 +121,7 @@ public class LrcLyricProvider : ILyricProvider } long ticks = TimeSpan.FromMilliseconds(timeData.Value).Ticks; - lyricList.Add(new Controller.Lyrics.Lyric(sortedLyricData[i].Text, ticks)); + lyricList.Add(new LyricLine(sortedLyricData[i].Text, ticks)); } if (fileMetaData.Count != 0) @@ -150,7 +166,23 @@ public class LrcLyricProvider : ILyricProvider if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length)) { - lyricMetadata.Length = length; + // Ensure minutes include leading zero + var lengthData = length.Split(':'); + if (lengthData[0].Length == 1) + { + length = "0" + length; + } + + // If only Minutes and Seconds were provided, prepend zeros for hours + if (lengthData.Length == 2) + { + length = "00:" + length; + } + + if (DateTime.TryParseExact(length, "HH:mm:ss", null, DateTimeStyles.None, out var value)) + { + lyricMetadata.Length = value.TimeOfDay.Ticks; + } } if (metaData.TryGetValue("by", out var by) && !string.IsNullOrEmpty(by)) @@ -160,7 +192,10 @@ public class LrcLyricProvider : ILyricProvider if (metaData.TryGetValue("offset", out var offset) && !string.IsNullOrEmpty(offset)) { - lyricMetadata.Offset = offset; + if (int.TryParse(offset, out var value)) + { + lyricMetadata.Offset = TimeSpan.FromMilliseconds(value).Ticks; + } } if (metaData.TryGetValue("re", out var creator) && !string.IsNullOrEmpty(creator)) diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index c69008686..cd0e1599e 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -1,7 +1,4 @@ -using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Resolvers; @@ -23,7 +20,7 @@ public class TxtLyricProvider : ILyricProvider public ResolverPriority Priority => ResolverPriority.Second; /// - public IEnumerable SupportedMediaTypes { get; } = new[] { "lrc", "txt" }; + public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "txt" }; /// /// Opens lyric file for the requested item, and processes it for API return. @@ -41,7 +38,7 @@ public class TxtLyricProvider : ILyricProvider string[] lyricTextLines = System.IO.File.ReadAllLines(lyricFilePath); - List lyricList = new List(); + List lyricList = new List(); if (lyricTextLines.Length == 0) { @@ -50,7 +47,7 @@ public class TxtLyricProvider : ILyricProvider foreach (string lyricTextLine in lyricTextLines) { - lyricList.Add(new Controller.Lyrics.Lyric(lyricTextLine)); + lyricList.Add(new LyricLine(lyricTextLine)); } return new LyricResponse { Lyrics = lyricList }; From 552b6aceae94bd8079a027407d15acf6d46f9b6e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 16:17:26 -0400 Subject: [PATCH 35/52] Add default values to LyricResponse --- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index b3c65ac8c..ded3ca10e 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -10,10 +10,10 @@ public class LyricResponse /// /// Gets or sets Metadata. /// - public LyricMetadata Metadata { get; set; } + public LyricMetadata Metadata { get; set; } = new LyricMetadata(); /// /// Gets or sets Lyrics. /// - public IEnumerable Lyrics { get; set; } + public IReadOnlyCollection Lyrics { get; set; } = new List(); } From 28d017865b7f51babc7c13dbfaf71a660d834d46 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sun, 18 Sep 2022 21:17:53 -0400 Subject: [PATCH 36/52] Code Cleanup --- MediaBrowser.Controller/Lyrics/LyricLine.cs | 2 +- .../Lyrics/LyricResponse.cs | 4 +- .../Lyric/LrcLyricProvider.cs | 39 +++++-------------- .../Lyric/TxtLyricProvider.cs | 4 +- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricLine.cs b/MediaBrowser.Controller/Lyrics/LyricLine.cs index 43997f656..eb5ff9972 100644 --- a/MediaBrowser.Controller/Lyrics/LyricLine.cs +++ b/MediaBrowser.Controller/Lyrics/LyricLine.cs @@ -8,8 +8,8 @@ public class LyricLine /// /// Initializes a new instance of the class. /// - /// The lyric start time in ticks. /// The lyric text. + /// The lyric start time in ticks. public LyricLine(string text, long? start = null) { Start = start; diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index ded3ca10e..64c3b3c28 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -10,10 +10,10 @@ public class LyricResponse /// /// Gets or sets Metadata. /// - public LyricMetadata Metadata { get; set; } = new LyricMetadata(); + public LyricMetadata Metadata { get; set; } = new(); /// /// Gets or sets Lyrics. /// - public IReadOnlyCollection Lyrics { get; set; } = new List(); + public IReadOnlyList Lyrics { get; set; } = new List(); } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 50fa519b3..9d622a1cd 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -38,7 +38,7 @@ public class LrcLyricProvider : ILyricProvider public ResolverPriority Priority => ResolverPriority.First; /// - public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc" }; + public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "elrc" }; /// /// Opens lyric file for the requested item, and processes it for API return. @@ -54,8 +54,8 @@ public class LrcLyricProvider : ILyricProvider return null; } - List lyricList = new List(); - List sortedLyricData = new List(); + List lyricList = new(); + List sortedLyricData = new(); IDictionary fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); @@ -85,19 +85,11 @@ public class LrcLyricProvider : ILyricProvider string[] metaDataField; string metaDataFieldName; string metaDataFieldValue; + string[] test; - if (colonCount == 1) - { - metaDataField = metaDataRow.Split(':'); - metaDataFieldName = metaDataField[0][1..].Trim(); - metaDataFieldValue = metaDataField[1][..^1].Trim(); - } - else - { - int colonIndex = metaDataRow.IndexOf(':', StringComparison.OrdinalIgnoreCase); - metaDataFieldName = metaDataRow[..colonIndex][1..].Trim(); - metaDataFieldValue = metaDataRow[(colonIndex + 1)..][..^1].Trim(); - } + metaDataField = metaDataRow.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + metaDataFieldName = metaDataField[0][1..].Trim(); + metaDataFieldValue = metaDataField[1][..^1].Trim(); fileMetaData.Add(metaDataFieldName, metaDataFieldValue); } @@ -142,7 +134,7 @@ public class LrcLyricProvider : ILyricProvider /// A lyricMetadata object with mapped property data. private LyricMetadata MapMetadataValues(IDictionary metaData) { - LyricMetadata lyricMetadata = new LyricMetadata(); + LyricMetadata lyricMetadata = new(); if (metaData.TryGetValue("ar", out var artist) && !string.IsNullOrEmpty(artist)) { @@ -166,20 +158,7 @@ public class LrcLyricProvider : ILyricProvider if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length)) { - // Ensure minutes include leading zero - var lengthData = length.Split(':'); - if (lengthData[0].Length == 1) - { - length = "0" + length; - } - - // If only Minutes and Seconds were provided, prepend zeros for hours - if (lengthData.Length == 2) - { - length = "00:" + length; - } - - if (DateTime.TryParseExact(length, "HH:mm:ss", null, DateTimeStyles.None, out var value)) + if (DateTime.TryParseExact(length, new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }, null, DateTimeStyles.None, out var value)) { lyricMetadata.Length = value.TimeOfDay.Ticks; } diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index cd0e1599e..542df1387 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -20,7 +20,7 @@ public class TxtLyricProvider : ILyricProvider public ResolverPriority Priority => ResolverPriority.Second; /// - public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "txt" }; + public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "elrc", "txt" }; /// /// Opens lyric file for the requested item, and processes it for API return. @@ -38,7 +38,7 @@ public class TxtLyricProvider : ILyricProvider string[] lyricTextLines = System.IO.File.ReadAllLines(lyricFilePath); - List lyricList = new List(); + List lyricList = new(); if (lyricTextLines.Length == 0) { From d7fedf35128a4400dfd4c4e72bbfee3f362d565d Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:26:38 -0400 Subject: [PATCH 37/52] Variable declaration cleanup --- .../Lyric/LrcLyricProvider.cs | 65 +++++++++---------- .../Lyric/TxtLyricProvider.cs | 4 +- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 9d622a1cd..ea10749b9 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -7,7 +7,6 @@ using LrcParser.Parser; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Lyric; @@ -54,49 +53,45 @@ public class LrcLyricProvider : ILyricProvider return null; } - List lyricList = new(); - List sortedLyricData = new(); - IDictionary fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + Song lyricData; + try { // Parse and sort lyric rows LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - Song lyricData = lrcLyricParser.Decode(lrcFileContent); - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.First().Value).ToList(); - - // Parse metadata rows - var metaDataRows = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith('[') && x.Text.EndsWith(']')) - .Select(x => x.Text) - .ToList(); - - foreach (string metaDataRow in metaDataRows) - { - int colonCount = metaDataRow.Count(f => (f == ':')); - if (colonCount == 0) - { - continue; - } - - string[] metaDataField; - string metaDataFieldName; - string metaDataFieldValue; - string[] test; - - metaDataField = metaDataRow.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - metaDataFieldName = metaDataField[0][1..].Trim(); - metaDataFieldValue = metaDataField[1][..^1].Trim(); - - fileMetaData.Add(metaDataFieldName, metaDataFieldValue); - } + lyricData = lrcLyricParser.Decode(lrcFileContent); } catch (Exception ex) { _logger.LogError(ex, "Error parsing lyric data from {Provider}", Name); + return null; + } + + List sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.First().Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith('[') && x.Text.EndsWith(']')) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + int colonCount = metaDataRow.Count(f => (f == ':')); + if (colonCount == 0) + { + continue; + } + + string[] metaDataField = metaDataRow.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + string metaDataFieldName = metaDataField[0][1..].Trim(); + string metaDataFieldValue = metaDataField[1][..^1].Trim(); + + fileMetaData.Add(metaDataFieldName, metaDataFieldValue); } if (sortedLyricData.Count == 0) @@ -104,6 +99,8 @@ public class LrcLyricProvider : ILyricProvider return null; } + List lyricList = new(); + for (int i = 0; i < sortedLyricData.Count; i++) { var timeData = sortedLyricData[i].TimeTags.First().Value; @@ -132,7 +129,7 @@ public class LrcLyricProvider : ILyricProvider /// /// The metadata from the LRC file. /// A lyricMetadata object with mapped property data. - private LyricMetadata MapMetadataValues(IDictionary metaData) + private static LyricMetadata MapMetadataValues(IDictionary metaData) { LyricMetadata lyricMetadata = new(); diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 542df1387..6a189e132 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -38,13 +38,13 @@ public class TxtLyricProvider : ILyricProvider string[] lyricTextLines = System.IO.File.ReadAllLines(lyricFilePath); - List lyricList = new(); - if (lyricTextLines.Length == 0) { return null; } + List lyricList = new(lyricTextLines.Length); + foreach (string lyricTextLine in lyricTextLines) { lyricList.Add(new LyricLine(lyricTextLine)); From 5d2364f0646ba4f4b6866115ad82fd8fac0733de Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:59:16 -0400 Subject: [PATCH 38/52] Move AcceptedTimeFormats to class level variable --- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 3 ++- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index 64c3b3c28..56a569645 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace MediaBrowser.Controller.Lyrics; @@ -15,5 +16,5 @@ public class LyricResponse /// /// Gets or sets Lyrics. /// - public IReadOnlyList Lyrics { get; set; } = new List(); + public IReadOnlyList Lyrics { get; set; } = Array.Empty(); } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index ea10749b9..b67b8c5d3 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -39,6 +39,8 @@ public class LrcLyricProvider : ILyricProvider /// public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "elrc" }; + private static string[] AcceptedTimeFormats => new[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; + /// /// Opens lyric file for the requested item, and processes it for API return. /// @@ -88,8 +90,8 @@ public class LrcLyricProvider : ILyricProvider } string[] metaDataField = metaDataRow.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - string metaDataFieldName = metaDataField[0][1..].Trim(); - string metaDataFieldValue = metaDataField[1][..^1].Trim(); + string metaDataFieldName = metaDataField[0][1..]; + string metaDataFieldValue = metaDataField[1][..^1]; fileMetaData.Add(metaDataFieldName, metaDataFieldValue); } @@ -155,7 +157,7 @@ public class LrcLyricProvider : ILyricProvider if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length)) { - if (DateTime.TryParseExact(length, new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }, null, DateTimeStyles.None, out var value)) + if (DateTime.TryParseExact(length, AcceptedTimeFormats, null, DateTimeStyles.None, out var value)) { lyricMetadata.Length = value.TimeOfDay.Ticks; } From 838334b322e7d8781c1514bcbf33df85e94b797c Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 19 Sep 2022 17:02:42 -0400 Subject: [PATCH 39/52] Add missing summary to AcceptedTimeFormats --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index b67b8c5d3..0422549ff 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -39,6 +39,10 @@ public class LrcLyricProvider : ILyricProvider /// public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "elrc" }; + /// + /// Gets the Accepted Time Formats for the metadata numeric values. + /// + /// The AcceptedTimeFormats. private static string[] AcceptedTimeFormats => new[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; /// From df85b2fe651ce060cf3b7c4a20c5254c9c80f3aa Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 19 Sep 2022 17:46:29 -0400 Subject: [PATCH 40/52] Update MediaBrowser.Providers/Lyric/LrcLyricProvider.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 0422549ff..debf982cb 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -39,11 +39,7 @@ public class LrcLyricProvider : ILyricProvider /// public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "elrc" }; - /// - /// Gets the Accepted Time Formats for the metadata numeric values. - /// - /// The AcceptedTimeFormats. - private static string[] AcceptedTimeFormats => new[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; + private static readonly IReadOnlyList _acceptedTimeFormats = new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; /// /// Opens lyric file for the requested item, and processes it for API return. From 3ba8218e45899c7d3622092ed92dc60b4c28803e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 19 Sep 2022 17:57:03 -0400 Subject: [PATCH 41/52] Use field instead of property --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index debf982cb..3657cf894 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -18,6 +18,8 @@ public class LrcLyricProvider : ILyricProvider { private readonly ILogger _logger; + private static readonly IReadOnlyList _acceptedTimeFormats = new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; + /// /// Initializes a new instance of the class. /// @@ -39,8 +41,6 @@ public class LrcLyricProvider : ILyricProvider /// public IReadOnlyCollection SupportedMediaTypes { get; } = new[] { "lrc", "elrc" }; - private static readonly IReadOnlyList _acceptedTimeFormats = new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; - /// /// Opens lyric file for the requested item, and processes it for API return. /// @@ -83,8 +83,7 @@ public class LrcLyricProvider : ILyricProvider foreach (string metaDataRow in metaDataRows) { - int colonCount = metaDataRow.Count(f => (f == ':')); - if (colonCount == 0) + if (!metaDataRow.Contains(':', StringComparison.OrdinalIgnoreCase)) { continue; } @@ -157,7 +156,7 @@ public class LrcLyricProvider : ILyricProvider if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length)) { - if (DateTime.TryParseExact(length, AcceptedTimeFormats, null, DateTimeStyles.None, out var value)) + if (DateTime.TryParseExact(length, _acceptedTimeFormats.ToArray(), null, DateTimeStyles.None, out var value)) { lyricMetadata.Length = value.TimeOfDay.Ticks; } From b442c79e620d2bdbb9007038371b75fe41b7cd8a Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 19 Sep 2022 20:24:05 -0400 Subject: [PATCH 42/52] Make LyricParser a field --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 3657cf894..3177abb96 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -18,6 +18,8 @@ public class LrcLyricProvider : ILyricProvider { private readonly ILogger _logger; + private readonly LyricParser _lrcLyricParser; + private static readonly IReadOnlyList _acceptedTimeFormats = new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; /// @@ -27,6 +29,7 @@ public class LrcLyricProvider : ILyricProvider public LrcLyricProvider(ILogger logger) { _logger = logger; + _lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); } /// @@ -62,13 +65,11 @@ public class LrcLyricProvider : ILyricProvider try { - // Parse and sort lyric rows - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - lyricData = lrcLyricParser.Decode(lrcFileContent); + lyricData = _lrcLyricParser.Decode(lrcFileContent); } catch (Exception ex) { - _logger.LogError(ex, "Error parsing lyric data from {Provider}", Name); + _logger.LogError(ex, "Error parsing lyric file {LyricFilePath} from {Provider}", lyricFilePath, Name); return null; } @@ -92,7 +93,12 @@ public class LrcLyricProvider : ILyricProvider string metaDataFieldName = metaDataField[0][1..]; string metaDataFieldValue = metaDataField[1][..^1]; - fileMetaData.Add(metaDataFieldName, metaDataFieldValue); + if (string.IsNullOrEmpty(metaDataFieldName) || string.IsNullOrEmpty(metaDataFieldValue)) + { + continue; + } + + fileMetaData[metaDataFieldName] = metaDataFieldValue; } if (sortedLyricData.Count == 0) From 6f0d33b1caafdf5c8c0413bf3fdcddc822a08f51 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Tue, 20 Sep 2022 08:36:43 -0400 Subject: [PATCH 43/52] Use Directory GetFiles to find lyric files --- .../ApplicationHost.cs | 1 - MediaBrowser.Controller/Lyrics/LyricInfo.cs | 20 +++++++++++++++---- .../Lyric/LrcLyricProvider.cs | 12 +++++++---- MediaBrowser.Providers/Lyric/LyricManager.cs | 2 +- .../Lyric/TxtLyricProvider.cs | 2 +- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3c3c90e61..67c63bb59 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -46,7 +46,6 @@ using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; -using Jellyfin.Api.Models.UserDtos; using Jellyfin.MediaEncoding.Hls.Playlist; using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 61e205b6c..a063a4cc5 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -1,4 +1,7 @@ +using System.Collections.Generic; using System.IO; +using System.Linq; +using Jellyfin.Extensions; namespace MediaBrowser.Controller.Lyrics; @@ -13,12 +16,21 @@ public static class LyricInfo /// The lyricProvider interface to use. /// Path of requested item. /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. - public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) + public static string? GetLyricFilePath(this ILyricProvider lyricProvider, string itemPath) { - foreach (string lyricFileExtension in lyricProvider.SupportedMediaTypes) + if (lyricProvider is null) { - var lyricFilePath = Path.ChangeExtension(itemPath, lyricFileExtension); - if (File.Exists(lyricFilePath)) + return null; + } + + if (!Directory.Exists(Path.GetDirectoryName(itemPath))) + { + return null; + } + + foreach (var lyricFilePath in Directory.GetFiles(Path.GetDirectoryName(itemPath), $"{Path.GetFileNameWithoutExtension(itemPath)}.*")) + { + if (lyricProvider.SupportedMediaTypes.Contains(Path.GetExtension(lyricFilePath)[1..])) { return lyricFilePath; } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 3177abb96..8ed0552cc 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -20,7 +20,7 @@ public class LrcLyricProvider : ILyricProvider private readonly LyricParser _lrcLyricParser; - private static readonly IReadOnlyList _acceptedTimeFormats = new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; + private static readonly string[] _acceptedTimeFormats = { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; /// /// Initializes a new instance of the class. @@ -51,14 +51,14 @@ public class LrcLyricProvider : ILyricProvider /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + string? lyricFilePath = this.GetLyricFilePath(item.Path); if (string.IsNullOrEmpty(lyricFilePath)) { return null; } - IDictionary fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); + var fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); Song lyricData; @@ -90,6 +90,10 @@ public class LrcLyricProvider : ILyricProvider } string[] metaDataField = metaDataRow.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + // Remove square bracket before field name, and after field value + // Example 1: [au: 1hitsong] + // Example 2: [ar: Calabrese] string metaDataFieldName = metaDataField[0][1..]; string metaDataFieldValue = metaDataField[1][..^1]; @@ -162,7 +166,7 @@ public class LrcLyricProvider : ILyricProvider if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length)) { - if (DateTime.TryParseExact(length, _acceptedTimeFormats.ToArray(), null, DateTimeStyles.None, out var value)) + if (DateTime.TryParseExact(length, _acceptedTimeFormats, null, DateTimeStyles.None, out var value)) { lyricMetadata.Length = value.TimeOfDay.Ticks; } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 7487c6861..336b324a7 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -46,7 +46,7 @@ public class LyricManager : ILyricManager continue; } - if (LyricInfo.GetLyricFilePath(provider, item.Path) is not null) + if (provider.GetLyricFilePath(item.Path) is not null) { return true; } diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 6a189e132..df6d52630 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -29,7 +29,7 @@ public class TxtLyricProvider : ILyricProvider /// If provider can determine lyrics, returns a ; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + string? lyricFilePath = this.GetLyricFilePath(item.Path); if (string.IsNullOrEmpty(lyricFilePath)) { From 0d5bd85d6d292b0876f60bf0654e243f595ec9f9 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Tue, 20 Sep 2022 08:48:08 -0400 Subject: [PATCH 44/52] Resolve Azure build error --- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index a063a4cc5..57964eee5 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -18,18 +18,29 @@ public static class LyricInfo /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. public static string? GetLyricFilePath(this ILyricProvider lyricProvider, string itemPath) { + // Ensure we have a provider if (lyricProvider is null) { return null; } - if (!Directory.Exists(Path.GetDirectoryName(itemPath))) + // Ensure the path to the item is not null + string? itemDirectoryPath = Path.GetDirectoryName(itemPath); + if (itemDirectoryPath is null) { return null; } - foreach (var lyricFilePath in Directory.GetFiles(Path.GetDirectoryName(itemPath), $"{Path.GetFileNameWithoutExtension(itemPath)}.*")) + // Ensure the directory path exists + if (!Directory.Exists(itemDirectoryPath)) { + return null; + } + + foreach (var lyricFilePath in Directory.GetFiles(itemDirectoryPath, $"{Path.GetFileNameWithoutExtension(itemPath)}.*")) + { + if (lyricFilePath is null) { continue; } + if (lyricProvider.SupportedMediaTypes.Contains(Path.GetExtension(lyricFilePath)[1..])) { return lyricFilePath; From b1771f07e92dd47f8e49c7f96f61b731d4a6065c Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Tue, 20 Sep 2022 13:34:46 -0400 Subject: [PATCH 45/52] Use Span in SupportedMediaTypes comparison --- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 57964eee5..6ebb83d12 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -1,6 +1,5 @@ -using System.Collections.Generic; +using System; using System.IO; -using System.Linq; using Jellyfin.Extensions; namespace MediaBrowser.Controller.Lyrics; @@ -39,9 +38,7 @@ public static class LyricInfo foreach (var lyricFilePath in Directory.GetFiles(itemDirectoryPath, $"{Path.GetFileNameWithoutExtension(itemPath)}.*")) { - if (lyricFilePath is null) { continue; } - - if (lyricProvider.SupportedMediaTypes.Contains(Path.GetExtension(lyricFilePath)[1..])) + if (EnumerableExtensions.Contains(lyricProvider.SupportedMediaTypes, Path.GetExtension(lyricFilePath.AsSpan())[1..], StringComparison.OrdinalIgnoreCase)) { return lyricFilePath; } From 35399ce8fef0559f65bee4f519c582d192a04e52 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Wed, 21 Sep 2022 17:49:28 -0400 Subject: [PATCH 46/52] Update summaries, Use spans --- MediaBrowser.Controller/Lyrics/LyricLine.cs | 12 ++++++------ .../Lyrics/LyricMetadata.cs | 18 +++++++++--------- .../Lyrics/LyricResponse.cs | 4 ++-- .../Lyric/LrcLyricProvider.cs | 16 ++++++++-------- .../Lyric/TxtLyricProvider.cs | 10 ++++++---- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricLine.cs b/MediaBrowser.Controller/Lyrics/LyricLine.cs index eb5ff9972..c406f92fc 100644 --- a/MediaBrowser.Controller/Lyrics/LyricLine.cs +++ b/MediaBrowser.Controller/Lyrics/LyricLine.cs @@ -12,17 +12,17 @@ public class LyricLine /// The lyric start time in ticks. public LyricLine(string text, long? start = null) { - Start = start; Text = text; + Start = start; } + /// + /// Gets the text of this lyric line. + /// + public string Text { get; } + /// /// Gets the start time in ticks. /// public long? Start { get; } - - /// - /// Gets the text. - /// - public string Text { get; } } diff --git a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs index 0ba777975..6091ede52 100644 --- a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs +++ b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs @@ -8,47 +8,47 @@ namespace MediaBrowser.Controller.Lyrics; public class LyricMetadata { /// - /// Gets or sets Artist - The song artist. + /// Gets or sets the song artist. /// public string? Artist { get; set; } /// - /// Gets or sets Album - The album this song is on. + /// Gets or sets the album this song is on. /// public string? Album { get; set; } /// - /// Gets or sets Title - The title of the song. + /// Gets or sets the title of the song. /// public string? Title { get; set; } /// - /// Gets or sets Author - Creator of the lyric data. + /// Gets or sets the author of the lyric data. /// public string? Author { get; set; } /// - /// Gets or sets Length - How long the song is. + /// Gets or sets the length of the song in ticks. /// public long? Length { get; set; } /// - /// Gets or sets By - Creator of the LRC file. + /// Gets or sets who the LRC file was created by. /// public string? By { get; set; } /// - /// Gets or sets Offset - Offset:+/- Timestamp adjustment in milliseconds. + /// Gets or sets the lyric offset compared to audio in ticks. /// public long? Offset { get; set; } /// - /// Gets or sets Creator - The Software used to create the LRC file. + /// Gets or sets the software used to create the LRC file. /// public string? Creator { get; set; } /// - /// Gets or sets Version - The version of the Creator used. + /// Gets or sets the version of the creator used. /// public string? Version { get; set; } } diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index 56a569645..0d52b5ec5 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -9,12 +9,12 @@ namespace MediaBrowser.Controller.Lyrics; public class LyricResponse { /// - /// Gets or sets Metadata. + /// Gets or sets Metadata for the lyrics. /// public LyricMetadata Metadata { get; set; } = new(); /// - /// Gets or sets Lyrics. + /// Gets or sets a collection of individual lyric lines. /// public IReadOnlyList Lyrics { get; set; } = Array.Empty(); } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 8ed0552cc..1dbe5958e 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using LrcParser.Model; using LrcParser.Parser; @@ -59,7 +60,7 @@ public class LrcLyricProvider : ILyricProvider } var fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); - string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + string lrcFileContent = File.ReadAllText(lyricFilePath); Song lyricData; @@ -84,25 +85,24 @@ public class LrcLyricProvider : ILyricProvider foreach (string metaDataRow in metaDataRows) { - if (!metaDataRow.Contains(':', StringComparison.OrdinalIgnoreCase)) + var index = metaDataRow.IndexOf(':', StringComparison.OrdinalIgnoreCase); + if (index == -1) { continue; } - string[] metaDataField = metaDataRow.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - // Remove square bracket before field name, and after field value // Example 1: [au: 1hitsong] // Example 2: [ar: Calabrese] - string metaDataFieldName = metaDataField[0][1..]; - string metaDataFieldValue = metaDataField[1][..^1]; + var metaDataFieldNameSpan = metaDataRow.AsSpan(1, index - 1).Trim(); + var metaDataFieldValueSpan = metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim(); - if (string.IsNullOrEmpty(metaDataFieldName) || string.IsNullOrEmpty(metaDataFieldValue)) + if (metaDataFieldValueSpan.IsEmpty || metaDataFieldValueSpan.IsEmpty) { continue; } - fileMetaData[metaDataFieldName] = metaDataFieldValue; + fileMetaData[metaDataFieldNameSpan.ToString()] = metaDataFieldValueSpan.ToString(); } if (sortedLyricData.Count == 0) diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index df6d52630..bce881054 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.IO; +using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Resolvers; @@ -36,18 +38,18 @@ public class TxtLyricProvider : ILyricProvider return null; } - string[] lyricTextLines = System.IO.File.ReadAllLines(lyricFilePath); + string[] lyricTextLines = File.ReadAllLines(lyricFilePath); if (lyricTextLines.Length == 0) { return null; } - List lyricList = new(lyricTextLines.Length); + LyricLine[] lyricList = new LyricLine[lyricTextLines.Length]; - foreach (string lyricTextLine in lyricTextLines) + for (int lyricLine = 0; lyricLine < lyricTextLines.Length; lyricLine++) { - lyricList.Add(new LyricLine(lyricTextLine)); + lyricList[lyricLine] = new LyricLine(lyricTextLines[lyricLine]); } return new LyricResponse { Lyrics = lyricList }; From a50bdb47709be0412b3abb2729f8c657b5d0c779 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 22 Sep 2022 08:13:53 -0400 Subject: [PATCH 47/52] Use async functions --- .../Controllers/UserLibraryController.cs | 4 ++-- .../Lyrics/ILyricManager.cs | 5 ++-- .../Lyrics/ILyricProvider.cs | 5 ++-- .../Lyric/LrcLyricProvider.cs | 23 ++++++++++++++----- MediaBrowser.Providers/Lyric/LyricManager.cs | 5 ++-- .../Lyric/TxtLyricProvider.cs | 5 ++-- 6 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index df91a8efc..682a6e832 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -397,7 +397,7 @@ namespace Jellyfin.Api.Controllers /// An containing the item's lyrics. [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public async Task> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -415,7 +415,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = _lyricManager.GetLyrics(item); + var result = await _lyricManager.GetLyrics(item).ConfigureAwait(false); if (result is not null) { return Ok(result); diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs index 5920bcc62..bb93e1e4c 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Lyrics; @@ -11,8 +12,8 @@ public interface ILyricManager /// Gets the lyrics. /// /// The media item. - /// Lyrics for passed item. - LyricResponse? GetLyrics(BaseItem item); + /// A task representing found lyrics the passed item. + Task GetLyrics(BaseItem item); /// /// Checks if requested item has a matching local lyric file. diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index c5b625226..2a04c6152 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Resolvers; @@ -30,6 +31,6 @@ public interface ILyricProvider /// Gets the lyrics. /// /// The media item. - /// If found, returns lyrics for passed item; otherwise, null. - LyricResponse? GetLyrics(BaseItem item); + /// A task representing found lyrics. + Task GetLyrics(BaseItem item); } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 1dbe5958e..d06db3afc 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Threading.Tasks; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; @@ -50,7 +51,7 @@ public class LrcLyricProvider : ILyricProvider /// /// The item to to process. /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. - public LyricResponse? GetLyrics(BaseItem item) + public async Task GetLyrics(BaseItem item) { string? lyricFilePath = this.GetLyricFilePath(item.Path); @@ -60,7 +61,7 @@ public class LrcLyricProvider : ILyricProvider } var fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); - string lrcFileContent = File.ReadAllText(lyricFilePath); + string lrcFileContent = await Task.FromResult(File.ReadAllText(lyricFilePath)).ConfigureAwait(false); Song lyricData; @@ -94,15 +95,15 @@ public class LrcLyricProvider : ILyricProvider // Remove square bracket before field name, and after field value // Example 1: [au: 1hitsong] // Example 2: [ar: Calabrese] - var metaDataFieldNameSpan = metaDataRow.AsSpan(1, index - 1).Trim(); - var metaDataFieldValueSpan = metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim(); + var metaDataFieldName = GetMetadataFieldName(metaDataRow, index); + var metaDataFieldValue = GetMetadataValue(metaDataRow, index); - if (metaDataFieldValueSpan.IsEmpty || metaDataFieldValueSpan.IsEmpty) + if (string.IsNullOrEmpty(metaDataFieldName) || string.IsNullOrEmpty(metaDataFieldValue)) { continue; } - fileMetaData[metaDataFieldNameSpan.ToString()] = metaDataFieldValueSpan.ToString(); + fileMetaData[metaDataFieldName.ToString()] = metaDataFieldValue.ToString(); } if (sortedLyricData.Count == 0) @@ -197,4 +198,14 @@ public class LrcLyricProvider : ILyricProvider return lyricMetadata; } + + private static string GetMetadataFieldName(string metaDataRow, int index) + { + return metaDataRow.AsSpan(1, index - 1).Trim().ToString(); + } + + private static string GetMetadataValue(string metaDataRow, int index) + { + return metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim().ToString(); + } } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 336b324a7..f9547e0f0 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; @@ -22,11 +23,11 @@ public class LyricManager : ILyricManager } /// - public LyricResponse? GetLyrics(BaseItem item) + public async Task GetLyrics(BaseItem item) { foreach (ILyricProvider provider in _lyricProviders) { - var results = provider.GetLyrics(item); + var results = await provider.GetLyrics(item).ConfigureAwait(false); if (results is not null) { return results; diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index bce881054..9df4ec83e 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Resolvers; @@ -29,7 +30,7 @@ public class TxtLyricProvider : ILyricProvider /// /// The item to to process. /// If provider can determine lyrics, returns a ; otherwise, null. - public LyricResponse? GetLyrics(BaseItem item) + public async Task GetLyrics(BaseItem item) { string? lyricFilePath = this.GetLyricFilePath(item.Path); @@ -38,7 +39,7 @@ public class TxtLyricProvider : ILyricProvider return null; } - string[] lyricTextLines = File.ReadAllLines(lyricFilePath); + string[] lyricTextLines = await Task.FromResult(File.ReadAllLines(lyricFilePath)).ConfigureAwait(false); if (lyricTextLines.Length == 0) { From 97409adb45e84916fbd0b4b140ab794ac4279d56 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 22 Sep 2022 09:00:07 -0400 Subject: [PATCH 48/52] Adjust file read async functions --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 2 +- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index d06db3afc..05d8f2674 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -61,7 +61,7 @@ public class LrcLyricProvider : ILyricProvider } var fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); - string lrcFileContent = await Task.FromResult(File.ReadAllText(lyricFilePath)).ConfigureAwait(false); + string lrcFileContent = await File.ReadAllTextAsync(lyricFilePath).ConfigureAwait(false); Song lyricData; diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 9df4ec83e..9d4f2f00e 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -39,7 +39,7 @@ public class TxtLyricProvider : ILyricProvider return null; } - string[] lyricTextLines = await Task.FromResult(File.ReadAllLines(lyricFilePath)).ConfigureAwait(false); + string[] lyricTextLines = await File.ReadAllLinesAsync(lyricFilePath).ConfigureAwait(false); if (lyricTextLines.Length == 0) { From 563d5fb5d9c349555227a9e3e7d265a55d589c31 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 22 Sep 2022 17:38:46 -0400 Subject: [PATCH 49/52] Return string.Empty is span IsEmpty --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 6 ++++-- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 05d8f2674..5844db078 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -201,11 +201,13 @@ public class LrcLyricProvider : ILyricProvider private static string GetMetadataFieldName(string metaDataRow, int index) { - return metaDataRow.AsSpan(1, index - 1).Trim().ToString(); + var metadataFieldName = metaDataRow.AsSpan(1, index - 1).Trim(); + return metadataFieldName.IsEmpty ? string.Empty : metadataFieldName.ToString(); } private static string GetMetadataValue(string metaDataRow, int index) { - return metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim().ToString(); + var metadataValue = metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim(); + return metadataValue.IsEmpty ? string.Empty : metadataValue.ToString(); } } diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 9d4f2f00e..85f792d09 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -48,9 +48,9 @@ public class TxtLyricProvider : ILyricProvider LyricLine[] lyricList = new LyricLine[lyricTextLines.Length]; - for (int lyricLine = 0; lyricLine < lyricTextLines.Length; lyricLine++) + for (int lyricLineIndex = 0; lyricLineIndex < lyricTextLines.Length; lyricLineIndex++) { - lyricList[lyricLine] = new LyricLine(lyricTextLines[lyricLine]); + lyricList[lyricLineIndex] = new LyricLine(lyricTextLines[lyricLineIndex]); } return new LyricResponse { Lyrics = lyricList }; From 1ae5c83aa7a7c84ab487e8abc1da6f8aabcdd39c Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 23 Sep 2022 07:59:46 -0400 Subject: [PATCH 50/52] Update MediaBrowser.Providers/Lyric/LrcLyricProvider.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 5844db078..4d4e35a4e 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -103,7 +103,7 @@ public class LrcLyricProvider : ILyricProvider continue; } - fileMetaData[metaDataFieldName.ToString()] = metaDataFieldValue.ToString(); + fileMetaData[metaDataFieldName] = metaDataFieldValue; } if (sortedLyricData.Count == 0) From 5efa138c221fddf86696ed6b1c58409ad8527bd3 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 26 Sep 2022 16:24:21 -0400 Subject: [PATCH 51/52] Update MediaBrowser.Controller/Lyrics/LyricInfo.cs Co-authored-by: Bond-009 --- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 6ebb83d12..6ec6df582 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -38,7 +38,7 @@ public static class LyricInfo foreach (var lyricFilePath in Directory.GetFiles(itemDirectoryPath, $"{Path.GetFileNameWithoutExtension(itemPath)}.*")) { - if (EnumerableExtensions.Contains(lyricProvider.SupportedMediaTypes, Path.GetExtension(lyricFilePath.AsSpan())[1..], StringComparison.OrdinalIgnoreCase)) + if (lyricProvider.SupportedMediaTypes.Contains(Path.GetExtension(lyricFilePath.AsSpan())[1..], StringComparison.OrdinalIgnoreCase)) { return lyricFilePath; } From c36785724444f651c1c24a72fed750e42a4d7b68 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Mon, 26 Sep 2022 16:27:55 -0400 Subject: [PATCH 52/52] Format returns --- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 11 +++++++++-- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 5 ++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 4d4e35a4e..7b108921b 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -130,10 +130,17 @@ public class LrcLyricProvider : ILyricProvider // Map metaData values from LRC file to LyricMetadata properties LyricMetadata lyricMetadata = MapMetadataValues(fileMetaData); - return new LyricResponse { Metadata = lyricMetadata, Lyrics = lyricList }; + return new LyricResponse + { + Metadata = lyricMetadata, + Lyrics = lyricList + }; } - return new LyricResponse { Lyrics = lyricList }; + return new LyricResponse + { + Lyrics = lyricList + }; } /// diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 85f792d09..96a9e9dcf 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -53,6 +53,9 @@ public class TxtLyricProvider : ILyricProvider lyricList[lyricLineIndex] = new LyricLine(lyricTextLines[lyricLineIndex]); } - return new LyricResponse { Lyrics = lyricList }; + return new LyricResponse + { + Lyrics = lyricList + }; } }