diff options
Diffstat (limited to 'Jellyfin.Api/Models')
| -rw-r--r-- | Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs | 34 | ||||
| -rw-r--r-- | Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs | 117 | ||||
| -rw-r--r-- | Jellyfin.Api/Models/UserDtos/Lyric.cs | 18 | ||||
| -rw-r--r-- | Jellyfin.Api/Models/UserDtos/Lyrics.cs | 23 | ||||
| -rw-r--r-- | Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs | 81 |
5 files changed, 250 insertions, 23 deletions
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 +{ + /// <summary> + /// Interface ILyricsProvider. + /// </summary> + public interface ILyricsProvider + { + /// <summary> + /// Gets a value indicating the File Extenstions this provider works with. + /// </summary> + public Collection<string>? FileExtensions { get; } + + /// <summary> + /// Gets a value indicating whether Process() generated data. + /// </summary> + /// <returns><c>true</c> if data generated; otherwise, <c>false</c>.</returns> + bool HasData { get; } + + /// <summary> + /// Gets Data object generated by Process() method. + /// </summary> + /// <returns><c>Object</c> with data if no error occured; otherwise, <c>null</c>.</returns> + object? Data { get; } + + /// <summary> + /// Opens lyric file for [the specified item], and processes it for API return. + /// </summary> + /// <param name="item">The item to to process.</param> + 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 +{ + /// <summary> + /// LRC File Lyric Provider. + /// </summary> + public class LrcLyricsProvider : ILyricsProvider + { + /// <summary> + /// Initializes a new instance of the <see cref="LrcLyricsProvider"/> class. + /// </summary> + public LrcLyricsProvider() + { + FileExtensions = new Collection<string> + { + "lrc" + }; + } + + /// <summary> + /// Gets a value indicating the File Extenstions this provider works with. + /// </summary> + public Collection<string>? FileExtensions { get; } + + /// <summary> + /// Gets or Sets a value indicating whether Process() generated data. + /// </summary> + /// <returns><c>true</c> if data generated; otherwise, <c>false</c>.</returns> + public bool HasData { get; set; } + + /// <summary> + /// Gets or Sets Data object generated by Process() method. + /// </summary> + /// <returns><c>Object</c> with data if no error occured; otherwise, <c>null</c>.</returns> + public object? Data { get; set; } + + /// <summary> + /// Opens lyric file for [the specified item], and processes it for API return. + /// </summary> + /// <param name="item">The item to to process.</param> + public void Process(BaseItem item) + { + string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return; + } + + List<Lyric> lyricsList = new List<Lyric>(); + + List<LrcParser.Model.Lyric> sortedLyricData = new List<LrcParser.Model.Lyric>(); + var metaData = new ExpandoObject() as IDictionary<string, object>; + 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 +{ + /// <summary> + /// Lyric dto. + /// </summary> + public class Lyric + { + /// <summary> + /// Gets or sets the start time (ticks). + /// </summary> + public double Start { get; set; } + + /// <summary> + /// Gets or sets the text. + /// </summary> + 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 -{ - /// <summary> - /// Lyric dto. - /// </summary> - public class Lyrics - { - /// <summary> - /// Gets or sets the start. - /// </summary> - public double? Start { get; set; } - - /// <summary> - /// Gets or sets the text. - /// </summary> - public string? Text { get; set; } - - /// <summary> - /// Gets or sets the error. - /// </summary> - 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 +{ + /// <summary> + /// TXT File Lyric Provider. + /// </summary> + public class TxtLyricsProvider : ILyricsProvider + { + /// <summary> + /// Initializes a new instance of the <see cref="TxtLyricsProvider"/> class. + /// </summary> + public TxtLyricsProvider() + { + FileExtensions = new Collection<string> + { + "lrc", "txt" + }; + } + + /// <summary> + /// Gets a value indicating the File Extenstions this provider works with. + /// </summary> + public Collection<string>? FileExtensions { get; } + + /// <summary> + /// Gets or Sets a value indicating whether Process() generated data. + /// </summary> + /// <returns><c>true</c> if data generated; otherwise, <c>false</c>.</returns> + public bool HasData { get; set; } + + /// <summary> + /// Gets or Sets Data object generated by Process() method. + /// </summary> + /// <returns><c>Object</c> with data if no error occured; otherwise, <c>null</c>.</returns> + public object? Data { get; set; } + + /// <summary> + /// Opens lyric file for [the specified item], and processes it for API return. + /// </summary> + /// <param name="item">The item to to process.</param> + public void Process(BaseItem item) + { + string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return; + } + + List<Lyric> lyricsList = new List<Lyric>(); + + 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 }; + } + } +} |
