< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Playlists.PlaylistItemsProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs
Line coverage
5%
Covered lines: 5
Uncovered lines: 87
Coverable lines: 92
Total lines: 227
Line coverage: 5.4%
Branch coverage
0%
Covered branches: 0
Total branches: 34
Branch coverage: 0%
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
.ctor(...)100%11100%
get_Name()100%210%
get_Order()100%210%
GetMetadata(...)100%210%
Fetch(...)0%7280%
GetItems(...)0%110100%
GetPlsItems(...)100%210%
GetM3uItems(...)100%210%
GetZplItems(...)100%210%
GetWplItems(...)100%210%
GetLinkedChild(...)0%620%
TryGetPlaylistItemPath(...)0%4260%
HasChanged(...)0%7280%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Collections.Generic;
 5using System.IO;
 6using System.Linq;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using Jellyfin.Data.Enums;
 10using Jellyfin.Extensions;
 11using MediaBrowser.Controller.Entities;
 12using MediaBrowser.Controller.Library;
 13using MediaBrowser.Controller.Playlists;
 14using MediaBrowser.Controller.Providers;
 15using MediaBrowser.Model.IO;
 16using Microsoft.Extensions.Logging;
 17using PlaylistsNET.Content;
 18
 19namespace MediaBrowser.Providers.Playlists;
 20
 21/// <summary>
 22/// Local playlist provider.
 23/// </summary>
 24public class PlaylistItemsProvider : ILocalMetadataProvider<Playlist>,
 25    IHasOrder,
 26    IForcedProvider,
 27    IHasItemChangeMonitor
 28{
 29    private readonly IFileSystem _fileSystem;
 30    private readonly ILibraryManager _libraryManager;
 31    private readonly ILogger<PlaylistItemsProvider> _logger;
 2232    private readonly CollectionType[] _ignoredCollections = [CollectionType.livetv, CollectionType.boxsets, CollectionTy
 33
 34    /// <summary>
 35    /// Initializes a new instance of the <see cref="PlaylistItemsProvider"/> class.
 36    /// </summary>
 37    /// <param name="logger">Instance of the <see cref="ILogger{PlaylistItemsProvider}"/> interface.</param>
 38    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 39    /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
 40    public PlaylistItemsProvider(ILogger<PlaylistItemsProvider> logger, ILibraryManager libraryManager, IFileSystem file
 41    {
 2242        _logger = logger;
 2243        _libraryManager = libraryManager;
 2244        _fileSystem = fileSystem;
 2245    }
 46
 47    /// <inheritdoc />
 048    public string Name => "Playlist Item Provider";
 49
 50    /// <inheritdoc />
 051    public int Order => 100;
 52
 53    /// <inheritdoc />
 54    public Task<MetadataResult<Playlist>> GetMetadata(
 55        ItemInfo info,
 56        IDirectoryService directoryService,
 57        CancellationToken cancellationToken)
 58    {
 059        var result = new MetadataResult<Playlist>()
 060        {
 061            Item = new Playlist
 062            {
 063                Path = info.Path
 064            }
 065        };
 066        Fetch(result);
 67
 068        return Task.FromResult(result);
 69    }
 70
 71    private void Fetch(MetadataResult<Playlist> result)
 72    {
 073        var item = result.Item;
 074        var path = item.Path;
 075        if (!Playlist.IsPlaylistFile(path))
 76        {
 077            return;
 78        }
 79
 080        var extension = Path.GetExtension(path);
 081        if (!Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparison.OrdinalIgnoreCase))
 82        {
 083            return;
 84        }
 85
 086        var items = GetItems(path, extension).ToArray();
 087        if (items.Length > 0)
 88        {
 089            result.HasMetadata = true;
 090            item.LinkedChildren = items;
 91        }
 92
 093        return;
 94    }
 95
 96    private IEnumerable<LinkedChild> GetItems(string path, string extension)
 97    {
 098        var libraryRoots = _libraryManager.GetUserRootFolder().Children
 099                            .OfType<CollectionFolder>()
 0100                            .Where(f => f.CollectionType.HasValue && !_ignoredCollections.Contains(f.CollectionType.Valu
 0101                            .SelectMany(f => f.PhysicalLocations)
 0102                            .Distinct(StringComparer.OrdinalIgnoreCase)
 0103                            .ToList();
 104
 0105        using (var stream = File.OpenRead(path))
 106        {
 0107            if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase))
 108            {
 0109                return GetWplItems(stream, path, libraryRoots);
 110            }
 111
 0112            if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
 113            {
 0114                return GetZplItems(stream, path, libraryRoots);
 115            }
 116
 0117            if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
 118            {
 0119                return GetM3uItems(stream, path, libraryRoots);
 120            }
 121
 0122            if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
 123            {
 0124                return GetM3uItems(stream, path, libraryRoots);
 125            }
 126
 0127            if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
 128            {
 0129                return GetPlsItems(stream, path, libraryRoots);
 130            }
 0131        }
 132
 0133        return Enumerable.Empty<LinkedChild>();
 0134    }
 135
 136    private IEnumerable<LinkedChild> GetPlsItems(Stream stream, string playlistPath, List<string> libraryRoots)
 137    {
 0138        var content = new PlsContent();
 0139        var playlist = content.GetFromStream(stream);
 140
 0141        return playlist.PlaylistEntries
 0142                .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
 0143                .Where(i => i is not null);
 144    }
 145
 146    private IEnumerable<LinkedChild> GetM3uItems(Stream stream, string playlistPath, List<string> libraryRoots)
 147    {
 0148        var content = new M3uContent();
 0149        var playlist = content.GetFromStream(stream);
 150
 0151        return playlist.PlaylistEntries
 0152                .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
 0153                .Where(i => i is not null);
 154    }
 155
 156    private IEnumerable<LinkedChild> GetZplItems(Stream stream, string playlistPath, List<string> libraryRoots)
 157    {
 0158        var content = new ZplContent();
 0159        var playlist = content.GetFromStream(stream);
 160
 0161        return playlist.PlaylistEntries
 0162                .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
 0163                .Where(i => i is not null);
 164    }
 165
 166    private IEnumerable<LinkedChild> GetWplItems(Stream stream, string playlistPath, List<string> libraryRoots)
 167    {
 0168        var content = new WplContent();
 0169        var playlist = content.GetFromStream(stream);
 170
 0171        return playlist.PlaylistEntries
 0172                .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots))
 0173                .Where(i => i is not null);
 174    }
 175
 176    private LinkedChild GetLinkedChild(string itemPath, string playlistPath, List<string> libraryRoots)
 177    {
 0178        if (TryGetPlaylistItemPath(itemPath, playlistPath, libraryRoots, out var parsedPath))
 179        {
 0180            return new LinkedChild
 0181            {
 0182                Path = parsedPath,
 0183                Type = LinkedChildType.Manual
 0184            };
 185        }
 186
 0187        return null;
 188    }
 189
 190    private bool TryGetPlaylistItemPath(string itemPath, string playlistPath, List<string> libraryPaths, out string path
 191    {
 0192        path = null;
 0193        string pathToCheck = _fileSystem.MakeAbsolutePath(Path.GetDirectoryName(playlistPath), itemPath);
 0194        if (!File.Exists(pathToCheck))
 195        {
 0196            return false;
 197        }
 198
 0199        foreach (var libraryPath in libraryPaths)
 200        {
 0201            if (pathToCheck.StartsWith(libraryPath, StringComparison.OrdinalIgnoreCase))
 202            {
 0203                path = pathToCheck;
 0204                return true;
 205            }
 206        }
 207
 0208        return false;
 0209    }
 210
 211    /// <inheritdoc />
 212    public bool HasChanged(BaseItem item, IDirectoryService directoryService)
 213    {
 0214        var path = item.Path;
 0215        if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol)
 216        {
 0217            var file = directoryService.GetFile(path);
 0218            if (file is not null && file.LastWriteTimeUtc != item.DateModified)
 219            {
 0220                _logger.LogDebug("Refreshing {Path} due to date modified timestamp change.", path);
 0221                return true;
 222            }
 223        }
 224
 0225        return false;
 226    }
 227}