< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.PathManager
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/PathManager.cs
Line coverage
8%
Covered lines: 4
Uncovered lines: 42
Coverable lines: 46
Total lines: 146
Line coverage: 8.6%
Branch coverage
0%
Covered branches: 0
Total branches: 18
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/1/2026 - 12:13:05 AM Line coverage: 11.5% (3/26) Branch coverage: 0% (0/2) Total lines: 1015/13/2026 - 12:15:27 AM Line coverage: 10.2% (4/39) Branch coverage: 0% (0/14) Total lines: 1296/8/2026 - 12:16:15 AM Line coverage: 10% (4/40) Branch coverage: 0% (0/16) Total lines: 1337/22/2026 - 12:16:22 AM Line coverage: 8.6% (4/46) Branch coverage: 0% (0/18) Total lines: 146 5/1/2026 - 12:13:05 AM Line coverage: 11.5% (3/26) Branch coverage: 0% (0/2) Total lines: 1015/13/2026 - 12:15:27 AM Line coverage: 10.2% (4/39) Branch coverage: 0% (0/14) Total lines: 1296/8/2026 - 12:16:15 AM Line coverage: 10% (4/40) Branch coverage: 0% (0/16) Total lines: 1337/22/2026 - 12:16:22 AM Line coverage: 8.6% (4/46) Branch coverage: 0% (0/18) Total lines: 146

Coverage delta

Coverage delta 2 -2

Metrics

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/PathManager.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4using System.IO;
 5using Jellyfin.Extensions;
 6using MediaBrowser.Common.Configuration;
 7using MediaBrowser.Controller.Configuration;
 8using MediaBrowser.Controller.Entities;
 9using MediaBrowser.Controller.IO;
 10using Microsoft.Extensions.Logging;
 11
 12namespace Emby.Server.Implementations.Library;
 13
 14/// <summary>
 15/// IPathManager implementation.
 16/// </summary>
 17public class PathManager : IPathManager
 18{
 19    private readonly ILogger<PathManager> _logger;
 20    private readonly IServerConfigurationManager _config;
 21    private readonly IApplicationPaths _appPaths;
 22
 23    /// <summary>
 24    /// Initializes a new instance of the <see cref="PathManager"/> class.
 25    /// </summary>
 26    /// <param name="logger">The logger.</param>
 27    /// <param name="config">The server configuration manager.</param>
 28    /// <param name="appPaths">The application paths.</param>
 29    public PathManager(
 30        ILogger<PathManager> logger,
 31        IServerConfigurationManager config,
 32        IApplicationPaths appPaths)
 33    {
 2234        _logger = logger;
 2235        _config = config;
 2236        _appPaths = appPaths;
 2237    }
 38
 039    private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles");
 40
 041    private string AttachmentCachePath => Path.Combine(_appPaths.DataPath, "attachments");
 42
 43    /// <inheritdoc />
 44    public string? GetAttachmentPath(string mediaSourceId, string fileName)
 45    {
 046        var folder = GetAttachmentFolderPath(mediaSourceId);
 047        if (folder is null)
 48        {
 049            return null;
 50        }
 51
 052        var safeName = PathHelper.GetSafeLeafFileName(fileName);
 053        if (safeName is null)
 54        {
 055            _logger.LogWarning("Rejecting attachment filename '{FileName}' for MediaSource {MediaSourceId}: not a valid 
 056            return null;
 57        }
 58
 059        return Path.Combine(folder, safeName);
 60    }
 61
 62    /// <inheritdoc />
 63    public string? GetAttachmentFolderPath(string mediaSourceId)
 64    {
 065        if (!Guid.TryParse(mediaSourceId, out var parsed))
 66        {
 067            _logger.LogDebug("MediaSource Id '{MediaSourceId}' is not a GUID; no on-disk attachment folder.", mediaSourc
 068            return null;
 69        }
 70
 071        var id = parsed.ToString("D", CultureInfo.InvariantCulture).AsSpan();
 072        return Path.Join(AttachmentCachePath, id[..2], id);
 73    }
 74
 75    /// <inheritdoc />
 76    public string? GetSubtitleFolderPath(string mediaSourceId)
 77    {
 078        if (!Guid.TryParse(mediaSourceId, out var parsed))
 79        {
 080            _logger.LogDebug("MediaSource Id '{MediaSourceId}' is not a GUID; no on-disk subtitle folder.", mediaSourceI
 081            return null;
 82        }
 83
 084        var id = parsed.ToString("D", CultureInfo.InvariantCulture).AsSpan();
 085        return Path.Join(SubtitleCachePath, id[..2], id);
 86    }
 87
 88    /// <inheritdoc />
 89    public string? GetSubtitlePath(string mediaSourceId, int streamIndex, string extension)
 90    {
 091        var folder = GetSubtitleFolderPath(mediaSourceId);
 092        return folder is null ? null : Path.Combine(folder, streamIndex.ToString(CultureInfo.InvariantCulture) + extensi
 93    }
 94
 95    /// <inheritdoc />
 96    public string GetTrickplayDirectory(BaseItem item, bool saveWithMedia = false)
 97    {
 098        var id = item.Id.ToString("D", CultureInfo.InvariantCulture).AsSpan();
 99
 0100        return saveWithMedia
 0101            ? Path.Combine(item.ContainingFolderPath, Path.ChangeExtension(Path.GetFileName(item.Path), ".trickplay"))
 0102            : Path.Join(_config.ApplicationPaths.TrickplayPath, id[..2], id);
 103    }
 104
 105    /// <inheritdoc/>
 106    public string GetChapterImageFolderPath(BaseItem item)
 107    {
 0108        return Path.Combine(item.GetInternalMetadataPath(), "chapters");
 109    }
 110
 111    /// <inheritdoc/>
 112    public string GetChapterImagePath(BaseItem item, long chapterPositionTicks)
 113    {
 0114        var filename = item.DateModified.Ticks.ToString(CultureInfo.InvariantCulture) + "_" + chapterPositionTicks.ToStr
 115
 0116        return Path.Combine(GetChapterImageFolderPath(item), filename);
 117    }
 118
 119    /// <inheritdoc/>
 120    public IReadOnlyList<string> GetExtractedDataPaths(BaseItem item)
 121    {
 0122        var mediaSourceId = item.Id.ToString("N", CultureInfo.InvariantCulture);
 0123        List<string> paths = [];
 0124        var attachmentFolder = GetAttachmentFolderPath(mediaSourceId);
 0125        if (attachmentFolder is not null)
 126        {
 0127            paths.Add(attachmentFolder);
 128        }
 129
 0130        var subtitleFolder = GetSubtitleFolderPath(mediaSourceId);
 0131        if (subtitleFolder is not null)
 132        {
 0133            paths.Add(subtitleFolder);
 134        }
 135
 0136        paths.Add(GetTrickplayDirectory(item, false));
 0137        if (!string.IsNullOrEmpty(item.Path))
 138        {
 0139            paths.Add(GetTrickplayDirectory(item, true));
 140        }
 141
 0142        paths.Add(GetChapterImageFolderPath(item));
 143
 0144        return paths;
 145    }
 146}