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: Add Lyrics API Endpoint --- Jellyfin.Api/Controllers/UserLibraryController.cs | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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() }); + } } } -- cgit v1.2.3 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: Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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) -- cgit v1.2.3 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: Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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(); } } } -- cgit v1.2.3 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: Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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); -- cgit v1.2.3 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: Updates based on review --- Jellyfin.Api/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 (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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 46e4fb703..000000000 Binary files a/Jellyfin.Api/Libraries/LrcParser.dll and /dev/null differ -- cgit v1.2.3 From c0dae0fef5255f27071b8dd84e8468a3e1ad29bf Mon Sep 17 00:00:00 2001 From: Jamie Introcaso Date: Wed, 14 Sep 2022 20:39:26 -0400 Subject: 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. --- Emby.Server.Implementations/ApplicationHost.cs | 3 +++ Jellyfin.Api/Controllers/UserLibraryController.cs | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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(); -- cgit v1.2.3 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: Auto stash before merge of "lyric-lrc-file-support" and "origin/lyric-lrc-file-support" --- Emby.Server.Implementations/ApplicationHost.cs | 2 + Emby.Server.Implementations/Dto/DtoService.cs | 9 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 5 +- Jellyfin.Api/Helpers/ItemHelper.cs | 106 ------------------- Jellyfin.Api/Jellyfin.Api.csproj | 1 - Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs | 34 ------ Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs | 117 --------------------- Jellyfin.Api/Models/UserDtos/Lyric.cs | 18 ---- Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs | 81 -------------- MediaBrowser.Controller/Lyrics/ILyricsProvider.cs | 24 +++++ MediaBrowser.Controller/Lyrics/Lyric.cs | 18 ++++ MediaBrowser.Controller/Lyrics/LyricInfo.cs | 87 +++++++++++++++ MediaBrowser.Controller/Lyrics/LyricResponse.cs | 15 +++ MediaBrowser.Model/Dto/BaseItemDto.cs | 2 +- MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs | 112 ++++++++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs | 74 +++++++++++++ .../MediaBrowser.Providers.csproj | 2 + 17 files changed, 345 insertions(+), 362 deletions(-) delete mode 100644 Jellyfin.Api/Helpers/ItemHelper.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/Lyric.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricsProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/Lyric.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricInfo.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricResponse.cs create mode 100644 MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs deleted file mode 100644 index 029acd6ca..000000000 --- a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs +++ /dev/null @@ -1,117 +0,0 @@ -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 deleted file mode 100644 index f83fc9839..000000000 --- a/Jellyfin.Api/Models/UserDtos/Lyric.cs +++ /dev/null @@ -1,18 +0,0 @@ -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/TxtLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs deleted file mode 100644 index 03cce1ffb..000000000 --- a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs +++ /dev/null @@ -1,81 +0,0 @@ -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.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/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs new file mode 100644 index 000000000..d44546dd3 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -0,0 +1,18 @@ +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// 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/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/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs new file mode 100644 index 000000000..e30d56308 --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +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 MediaBrowser.Providers.Lyric +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public LrcLyricsProvider() + { + SupportedMediaTypes = new Collection + { + "lrc" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + 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. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + 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 null; + } + + if (!sortedLyricData.Any()) + { + 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 MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + if (metaData.Any()) + { + return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs new file mode 100644 index 000000000..2a5da4e4d --- /dev/null +++ b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public TxtLyricsProvider() + { + SupportedMediaTypes = new Collection + { + "lrc", "txt" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + 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. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + 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 null; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + } + + 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 @@ + -- cgit v1.2.3 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: Create ILyricManager --- Emby.Server.Implementations/ApplicationHost.cs | 4 +- Emby.Server.Implementations/Dto/DtoService.cs | 8 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 16 ++- MediaBrowser.Controller/Lyrics/ILyricManager.cs | 37 +++++++ MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 29 ++++++ MediaBrowser.Controller/Lyrics/ILyricsProvider.cs | 24 ----- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 50 +-------- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 119 ++++++++++++++++++++++ MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs | 112 -------------------- MediaBrowser.Providers/Lyric/LyricManager.cs | 97 ++++++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 82 +++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs | 74 -------------- 12 files changed, 378 insertions(+), 274 deletions(-) create mode 100644 MediaBrowser.Controller/Lyrics/ILyricManager.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricProvider.cs delete mode 100644 MediaBrowser.Controller/Lyrics/ILyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/LrcLyricProvider.cs delete mode 100644 MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/LyricManager.cs create mode 100644 MediaBrowser.Providers/Lyric/TxtLyricProvider.cs delete mode 100644 MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs new file mode 100644 index 000000000..691fed1fd --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricProvider + { + /// + /// Gets a value indicating the provider name. + /// + string Name { get; } + + /// + /// 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/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs deleted file mode 100644 index bac32a398..000000000 --- a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs +++ /dev/null @@ -1,24 +0,0 @@ -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/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/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs new file mode 100644 index 000000000..18a85c93a --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +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 MediaBrowser.Providers.Lyric +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricProvider : ILyricProvider + { + /// + /// Initializes a new instance of the class. + /// + 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. + /// + 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. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + 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 null; + } + + if (!sortedLyricData.Any()) + { + 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 MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + if (metaData.Any()) + { + return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs deleted file mode 100644 index e30d56308..000000000 --- a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; -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 MediaBrowser.Providers.Lyric -{ - /// - /// LRC File Lyric Provider. - /// - public class LrcLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public LrcLyricsProvider() - { - SupportedMediaTypes = new Collection - { - "lrc" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - 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. - /// - /// The item to to process. - /// A representing the asynchronous operation. - public LyricResponse? GetLyrics(BaseItem item) - { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - 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 null; - } - - if (!sortedLyricData.Any()) - { - 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 MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); - } - - if (metaData.Any()) - { - return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; - } - - return new LyricResponse { Lyrics = lyricsList }; - } - } -} 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/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs new file mode 100644 index 000000000..939d8708b --- /dev/null +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -0,0 +1,82 @@ +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; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricProvider : ILyricProvider + { + /// + /// Initializes a new instance of the class. + /// + 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. + /// + 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. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + 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 null; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs deleted file mode 100644 index 2a5da4e4d..000000000 --- a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using Jellyfin.Api.Helpers; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Lyrics; - -namespace MediaBrowser.Providers.Lyric -{ - /// - /// TXT File Lyric Provider. - /// - public class TxtLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public TxtLyricsProvider() - { - SupportedMediaTypes = new Collection - { - "lrc", "txt" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - 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. - /// - /// The item to to process. - /// A representing the asynchronous operation. - public LyricResponse? GetLyrics(BaseItem item) - { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - 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 null; - } - - foreach (string lyricLine in lyricTextLines) - { - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); - } - - return new LyricResponse { Lyrics = lyricsList }; - } - } -} -- cgit v1.2.3 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: Remove use of AddParts. Cleanup use of Lyric vs Lyrics. --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +++--- Jellyfin.Server/CoreAppHost.cs | 6 ++++++ MediaBrowser.Controller/Lyrics/ILyricManager.cs | 18 ++-------------- MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 4 ++-- MediaBrowser.Controller/Lyrics/Lyric.cs | 4 ++-- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 8 +++---- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 9 ++++++++ MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 24 ++++++++------------- MediaBrowser.Providers/Lyric/LyricManager.cs | 16 +++++--------- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 26 +++++++++-------------- 11 files changed, 53 insertions(+), 70 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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 }; } } } -- cgit v1.2.3 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: Change API GetLyrics return type --- Jellyfin.Api/Controllers/UserLibraryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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); -- cgit v1.2.3 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: Use async functions --- Jellyfin.Api/Controllers/UserLibraryController.cs | 4 ++-- MediaBrowser.Controller/Lyrics/ILyricManager.cs | 5 +++-- MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 5 +++-- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 23 +++++++++++++++++------ MediaBrowser.Providers/Lyric/LyricManager.cs | 5 +++-- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 5 +++-- 6 files changed, 31 insertions(+), 16 deletions(-) (limited to 'Jellyfin.Api/Controllers/UserLibraryController.cs') 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) { -- cgit v1.2.3