< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Lyric.LrcLyricParser
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Lyric/LrcLyricParser.cs
Line coverage
88%
Covered lines: 44
Uncovered lines: 6
Coverable lines: 50
Total lines: 122
Line coverage: 88%
Branch coverage
86%
Covered branches: 19
Total branches: 22
Branch coverage: 86.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor()100%11100%
get_Name()100%210%
get_Priority()100%11100%
ParseLyrics(...)86.36%232288.88%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Lyric/LrcLyricParser.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Linq;
 5using System.Text;
 6using System.Text.RegularExpressions;
 7using Jellyfin.Extensions;
 8using LrcParser.Model;
 9using LrcParser.Parser;
 10using MediaBrowser.Controller.Lyrics;
 11using MediaBrowser.Controller.Resolvers;
 12using MediaBrowser.Model.Lyrics;
 13
 14namespace MediaBrowser.Providers.Lyric;
 15
 16/// <summary>
 17/// LRC Lyric Parser.
 18/// </summary>
 19public partial class LrcLyricParser : ILyricParser
 20{
 21    private readonly LyricParser _lrcLyricParser;
 22
 123    private static readonly string[] _supportedMediaTypes = [".lrc", ".elrc"];
 24
 25    /// <summary>
 26    /// Initializes a new instance of the <see cref="LrcLyricParser"/> class.
 27    /// </summary>
 28    public LrcLyricParser()
 29    {
 2230        _lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser();
 2231    }
 32
 33    /// <inheritdoc />
 034    public string Name => "LrcLyricProvider";
 35
 36    /// <summary>
 37    /// Gets the priority.
 38    /// </summary>
 39    /// <value>The priority.</value>
 2140    public ResolverPriority Priority => ResolverPriority.Fourth;
 41
 42    /// <inheritdoc />
 43    public LyricDto? ParseLyrics(LyricFile lyrics)
 44    {
 145        if (!_supportedMediaTypes.Contains(Path.GetExtension(lyrics.Name.AsSpan()), StringComparison.OrdinalIgnoreCase))
 46        {
 047            return null;
 48        }
 49
 50        Song lyricData;
 51
 52        try
 53        {
 154            lyricData = _lrcLyricParser.Decode(lyrics.Content);
 155        }
 056        catch (Exception)
 57        {
 58            // Failed to parse, return null so the next parser will be tried
 059            return null;
 60        }
 61
 162        List<LrcParser.Model.Lyric> sortedLyricData = lyricData.Lyrics.OrderBy(x => x.StartTime).ToList();
 63
 164        if (sortedLyricData.Count == 0)
 65        {
 066            return null;
 67        }
 68
 169        List<LyricLine> lyricList = [];
 6470        for (var lineIndex = 0; lineIndex < sortedLyricData.Count; lineIndex++)
 71        {
 3172            var lyric = sortedLyricData[lineIndex];
 73
 74            // Extract cues from time tags
 3175            var cues = new List<LyricLineCue>();
 3176            if (lyric.TimeTags.Count > 0)
 77            {
 3178                var keys = lyric.TimeTags.Keys.ToList();
 68279                for (var tagIndex = 0; tagIndex < keys.Count - 1; tagIndex++)
 80                {
 31081                    var currentKey = keys[tagIndex];
 31082                    var nextKey = keys[tagIndex + 1];
 83
 31084                    var currentPos = currentKey.State == IndexState.End ? currentKey.Index + 1 : currentKey.Index;
 31085                    var nextPos = nextKey.State == IndexState.End ? nextKey.Index + 1 : nextKey.Index;
 31086                    var currentMs = lyric.TimeTags[currentKey] ?? 0;
 31087                    var nextMs = lyric.TimeTags[keys[tagIndex + 1]] ?? 0;
 31088                    var currentSlice = lyric.Text[currentPos..nextPos];
 31089                    var currentSliceTrimmed = currentSlice.Trim();
 31090                    if (currentSliceTrimmed.Length > 0)
 91                    {
 15592                        cues.Add(new LyricLineCue(
 15593                            position: currentPos,
 15594                            endPosition: nextPos,
 15595                            start: TimeSpan.FromMilliseconds(currentMs).Ticks,
 15596                            end: TimeSpan.FromMilliseconds(nextMs).Ticks));
 97                    }
 98                }
 99
 31100                var lastKey = keys[^1];
 31101                var lastPos = lastKey.State == IndexState.End ? lastKey.Index + 1 : lastKey.Index;
 31102                var lastMs = lyric.TimeTags[lastKey] ?? 0;
 31103                var lastSlice = lyric.Text[lastPos..];
 31104                var lastSliceTrimmed = lastSlice.Trim();
 105
 31106                if (lastSliceTrimmed.Length > 0)
 107                {
 31108                    cues.Add(new LyricLineCue(
 31109                        position: lastPos,
 31110                        endPosition: lyric.Text.Length,
 31111                        start: TimeSpan.FromMilliseconds(lastMs).Ticks,
 31112                        end: lineIndex + 1 < sortedLyricData.Count ? TimeSpan.FromMilliseconds(sortedLyricData[lineIndex
 113                }
 114            }
 115
 31116            long lyricStartTicks = TimeSpan.FromMilliseconds(lyric.StartTime).Ticks;
 31117            lyricList.Add(new LyricLine(lyric.Text, lyricStartTicks, cues));
 118        }
 119
 1120        return new LyricDto { Lyrics = lyricList };
 0121    }
 122}