< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.MediaInfo.ProbeProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
Line coverage
29%
Covered lines: 29
Uncovered lines: 70
Coverable lines: 99
Total lines: 319
Line coverage: 29.2%
Branch coverage
0%
Covered branches: 0
Total branches: 62
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/23/2026 - 12:11:06 AM Line coverage: 31.4% (28/89) Branch coverage: 0% (0/50) Total lines: 2994/7/2026 - 12:14:03 AM Line coverage: 28.5% (28/98) Branch coverage: 0% (0/62) Total lines: 3185/4/2026 - 12:15:16 AM Line coverage: 29.2% (29/99) Branch coverage: 0% (0/62) Total lines: 319 1/23/2026 - 12:11:06 AM Line coverage: 31.4% (28/89) Branch coverage: 0% (0/50) Total lines: 2994/7/2026 - 12:14:03 AM Line coverage: 28.5% (28/98) Branch coverage: 0% (0/62) Total lines: 3185/4/2026 - 12:15:16 AM Line coverage: 29.2% (29/99) Branch coverage: 0% (0/62) Total lines: 319

Coverage delta

Coverage delta 3 -3

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%210%
get_Order()100%210%
HasChanged(...)0%930300%
FetchAsync(...)100%210%
FetchAsync(...)100%210%
FetchAsync(...)100%210%
FetchAsync(...)100%210%
FetchAsync(...)100%210%
FetchAsync(...)100%210%
FetchAsync(...)100%210%
FetchVideoInfo(...)0%156120%
NormalizeStrmLine(...)100%210%
FetchShortcutInfo(...)0%156120%
FetchAudioInfo(...)0%7280%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/MediaInfo/ProbeProvider.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 Emby.Naming.Common;
 10using MediaBrowser.Controller.Chapters;
 11using MediaBrowser.Controller.Configuration;
 12using MediaBrowser.Controller.Entities;
 13using MediaBrowser.Controller.Entities.Audio;
 14using MediaBrowser.Controller.Entities.Movies;
 15using MediaBrowser.Controller.Entities.TV;
 16using MediaBrowser.Controller.Library;
 17using MediaBrowser.Controller.Lyrics;
 18using MediaBrowser.Controller.MediaEncoding;
 19using MediaBrowser.Controller.Persistence;
 20using MediaBrowser.Controller.Providers;
 21using MediaBrowser.Controller.Subtitles;
 22using MediaBrowser.Model.Entities;
 23using MediaBrowser.Model.Globalization;
 24using MediaBrowser.Model.IO;
 25using MediaBrowser.Model.MediaInfo;
 26using Microsoft.Extensions.Logging;
 27
 28namespace MediaBrowser.Providers.MediaInfo
 29{
 30    /// <summary>
 31    /// The probe provider.
 32    /// </summary>
 33    public class ProbeProvider : ICustomMetadataProvider<Episode>,
 34        ICustomMetadataProvider<MusicVideo>,
 35        ICustomMetadataProvider<Movie>,
 36        ICustomMetadataProvider<Trailer>,
 37        ICustomMetadataProvider<Video>,
 38        ICustomMetadataProvider<Audio>,
 39        ICustomMetadataProvider<AudioBook>,
 40        IHasOrder,
 41        IForcedProvider,
 42        IPreRefreshProvider,
 43        IHasItemChangeMonitor
 44    {
 45        private readonly ILogger<ProbeProvider> _logger;
 46        private readonly AudioResolver _audioResolver;
 47        private readonly SubtitleResolver _subtitleResolver;
 48        private readonly LyricResolver _lyricResolver;
 49        private readonly FFProbeVideoInfo _videoProber;
 50        private readonly AudioFileProber _audioProber;
 2151        private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
 52
 53        /// <summary>
 54        /// Initializes a new instance of the <see cref="ProbeProvider"/> class.
 55        /// </summary>
 56        /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
 57        /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
 58        /// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param>
 59        /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 60        /// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param>
 61        /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
 62        /// <param name="subtitleManager">Instance of the <see cref="ISubtitleManager"/> interface.</param>
 63        /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 64        /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/>.</param>
 65        /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
 66        /// <param name="namingOptions">The <see cref="NamingOptions"/>.</param>
 67        /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
 68        /// <param name="mediaAttachmentRepository">Instance of the <see cref="IMediaAttachmentRepository"/> interface.<
 69        /// <param name="mediaStreamRepository">Instance of the <see cref="IMediaStreamRepository"/> interface.</param>
 70        public ProbeProvider(
 71            IMediaSourceManager mediaSourceManager,
 72            IMediaEncoder mediaEncoder,
 73            IBlurayExaminer blurayExaminer,
 74            ILocalizationManager localization,
 75            IChapterManager chapterManager,
 76            IServerConfigurationManager config,
 77            ISubtitleManager subtitleManager,
 78            ILibraryManager libraryManager,
 79            IFileSystem fileSystem,
 80            ILoggerFactory loggerFactory,
 81            NamingOptions namingOptions,
 82            ILyricManager lyricManager,
 83            IMediaAttachmentRepository mediaAttachmentRepository,
 84            IMediaStreamRepository mediaStreamRepository)
 85        {
 2186            _logger = loggerFactory.CreateLogger<ProbeProvider>();
 2187            _audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, 
 2188            _subtitleResolver = new SubtitleResolver(loggerFactory.CreateLogger<SubtitleResolver>(), localization, media
 2189            _lyricResolver = new LyricResolver(loggerFactory.CreateLogger<LyricResolver>(), localization, mediaEncoder, 
 90
 2191            _videoProber = new FFProbeVideoInfo(
 2192                loggerFactory.CreateLogger<FFProbeVideoInfo>(),
 2193                mediaSourceManager,
 2194                mediaEncoder,
 2195                blurayExaminer,
 2196                localization,
 2197                chapterManager,
 2198                config,
 2199                subtitleManager,
 21100                libraryManager,
 21101                _audioResolver,
 21102                _subtitleResolver,
 21103                mediaAttachmentRepository,
 21104                mediaStreamRepository);
 105
 21106            _audioProber = new AudioFileProber(
 21107                loggerFactory.CreateLogger<AudioFileProber>(),
 21108                mediaSourceManager,
 21109                mediaEncoder,
 21110                libraryManager,
 21111                _lyricResolver,
 21112                lyricManager,
 21113                mediaStreamRepository,
 21114                chapterManager);
 21115        }
 116
 117        /// <inheritdoc />
 0118        public string Name => "Probe Provider";
 119
 120        /// <inheritdoc />
 0121        public int Order => 100;
 122
 123        /// <inheritdoc />
 124        public bool HasChanged(BaseItem item, IDirectoryService directoryService)
 125        {
 0126            var video = item as Video;
 0127            if (video is null || video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Iso)
 128            {
 0129                var path = item.Path;
 130
 0131                if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol)
 132                {
 0133                    var file = directoryService.GetFile(path);
 0134                    if (file is not null && item.HasChanged(file.LastWriteTimeUtc))
 135                    {
 0136                        _logger.LogDebug("Refreshing {ItemPath} due to file system modification.", path);
 0137                        return true;
 138                    }
 139                }
 140            }
 141
 0142            if (video is not null
 0143                && item.SupportsLocalMetadata
 0144                && !video.IsPlaceHolder)
 145            {
 0146                var externalFiles = new HashSet<string>(_subtitleResolver.GetExternalFiles(video, directoryService, fals
 0147                if (!new HashSet<string>(video.SubtitleFiles, StringComparer.Ordinal).SetEquals(externalFiles))
 148                {
 0149                    _logger.LogDebug("Refreshing {ItemPath} due to external subtitles change.", item.Path);
 0150                    return true;
 151                }
 152
 0153                externalFiles = new HashSet<string>(_audioResolver.GetExternalFiles(video, directoryService, false).Sele
 0154                if (!new HashSet<string>(video.AudioFiles, StringComparer.Ordinal).SetEquals(externalFiles))
 155                {
 0156                    _logger.LogDebug("Refreshing {ItemPath} due to external audio change.", item.Path);
 0157                    return true;
 158                }
 159            }
 160
 0161            if (item is Audio audio
 0162                && item.SupportsLocalMetadata)
 163            {
 0164                var externalFiles = new HashSet<string>(_lyricResolver.GetExternalFiles(audio, directoryService, false).
 0165                if (!new HashSet<string>(audio.LyricFiles, StringComparer.Ordinal).SetEquals(externalFiles))
 166                {
 0167                    _logger.LogDebug("Refreshing {ItemPath} due to external lyrics change.", item.Path);
 0168                    return true;
 169                }
 170            }
 171
 0172            return false;
 173        }
 174
 175        /// <inheritdoc />
 176        public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellat
 177        {
 0178            return FetchVideoInfo(item, options, cancellationToken);
 179        }
 180
 181        /// <inheritdoc />
 182        public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancel
 183        {
 0184            return FetchVideoInfo(item, options, cancellationToken);
 185        }
 186
 187        /// <inheritdoc />
 188        public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellatio
 189        {
 0190            return FetchVideoInfo(item, options, cancellationToken);
 191        }
 192
 193        /// <inheritdoc />
 194        public Task<ItemUpdateType> FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellat
 195        {
 0196            return FetchVideoInfo(item, options, cancellationToken);
 197        }
 198
 199        /// <inheritdoc />
 200        public Task<ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellatio
 201        {
 0202            return FetchVideoInfo(item, options, cancellationToken);
 203        }
 204
 205        /// <inheritdoc />
 206        public Task<ItemUpdateType> FetchAsync(Audio item, MetadataRefreshOptions options, CancellationToken cancellatio
 207        {
 0208            return FetchAudioInfo(item, options, cancellationToken);
 209        }
 210
 211        /// <inheritdoc />
 212        public Task<ItemUpdateType> FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancell
 213        {
 0214            return FetchAudioInfo(item, options, cancellationToken);
 215        }
 216
 217        /// <summary>
 218        /// Fetches video information for an item.
 219        /// </summary>
 220        /// <param name="item">The item.</param>
 221        /// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param>
 222        /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
 223        /// <typeparam name="T">The type of item to resolve.</typeparam>
 224        /// <returns>A <see cref="Task"/> fetching the <see cref="ItemUpdateType"/> for an item.</returns>
 225        public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancella
 226            where T : Video
 227        {
 0228            if (item.IsPlaceHolder)
 229            {
 0230                return _cachedTask;
 231            }
 232
 0233            if (!item.IsCompleteMedia)
 234            {
 0235                return _cachedTask;
 236            }
 237
 0238            if (item.IsVirtualItem)
 239            {
 0240                return _cachedTask;
 241            }
 242
 0243            if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
 244            {
 0245                return _cachedTask;
 246            }
 247
 0248            if (item.IsShortcut)
 249            {
 0250                FetchShortcutInfo(item);
 251            }
 252
 0253            return _videoProber.ProbeVideo(item, options, cancellationToken);
 254        }
 255
 256        private string NormalizeStrmLine(string line)
 257        {
 0258            return line.Replace("\t", string.Empty, StringComparison.Ordinal)
 0259                .Replace("\r", string.Empty, StringComparison.Ordinal)
 0260                .Replace("\n", string.Empty, StringComparison.Ordinal)
 0261                .Trim();
 262        }
 263
 264        private void FetchShortcutInfo(BaseItem item)
 265        {
 0266            var shortcutPath = File.ReadAllLines(item.Path)
 0267                .Select(NormalizeStrmLine)
 0268                .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith('#'));
 269
 0270            if (string.IsNullOrWhiteSpace(shortcutPath))
 271            {
 0272                return;
 273            }
 274
 275            // Only allow remote URLs in .strm files to prevent local file access
 0276            if (Uri.TryCreate(shortcutPath, UriKind.Absolute, out var uri)
 0277                && (string.Equals(uri.Scheme, "http", StringComparison.OrdinalIgnoreCase)
 0278                    || string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase)
 0279                    || string.Equals(uri.Scheme, "rtsp", StringComparison.OrdinalIgnoreCase)
 0280                    || string.Equals(uri.Scheme, "rtp", StringComparison.OrdinalIgnoreCase)))
 281            {
 0282                item.ShortcutPath = shortcutPath;
 283            }
 284            else
 285            {
 0286                _logger.LogWarning("Ignoring invalid or non-remote .strm path in {File}: {Path}", item.Path, shortcutPat
 287            }
 0288        }
 289
 290        /// <summary>
 291        /// Fetches audio information for an item.
 292        /// </summary>
 293        /// <param name="item">The item.</param>
 294        /// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param>
 295        /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
 296        /// <typeparam name="T">The type of item to resolve.</typeparam>
 297        /// <returns>A <see cref="Task"/> fetching the <see cref="ItemUpdateType"/> for an item.</returns>
 298        public Task<ItemUpdateType> FetchAudioInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancella
 299            where T : Audio
 300        {
 0301            if (item.IsVirtualItem)
 302            {
 0303                return _cachedTask;
 304            }
 305
 0306            if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
 307            {
 0308                return _cachedTask;
 309            }
 310
 0311            if (item.IsShortcut)
 312            {
 0313                FetchShortcutInfo(item);
 314            }
 315
 0316            return _audioProber.Probe(item, options, cancellationToken);
 317        }
 318    }
 319}

Methods/Properties

.ctor(MediaBrowser.Controller.Library.IMediaSourceManager,MediaBrowser.Controller.MediaEncoding.IMediaEncoder,MediaBrowser.Model.MediaInfo.IBlurayExaminer,MediaBrowser.Model.Globalization.ILocalizationManager,MediaBrowser.Controller.Chapters.IChapterManager,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Subtitles.ISubtitleManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Model.IO.IFileSystem,Microsoft.Extensions.Logging.ILoggerFactory,Emby.Naming.Common.NamingOptions,MediaBrowser.Controller.Lyrics.ILyricManager,MediaBrowser.Controller.Persistence.IMediaAttachmentRepository,MediaBrowser.Controller.Persistence.IMediaStreamRepository)
get_Name()
get_Order()
HasChanged(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)
FetchAsync(MediaBrowser.Controller.Entities.TV.Episode,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
FetchAsync(MediaBrowser.Controller.Entities.MusicVideo,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
FetchAsync(MediaBrowser.Controller.Entities.Movies.Movie,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
FetchAsync(MediaBrowser.Controller.Entities.Trailer,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
FetchAsync(MediaBrowser.Controller.Entities.Video,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
FetchAsync(MediaBrowser.Controller.Entities.Audio.Audio,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
FetchAsync(MediaBrowser.Controller.Entities.AudioBook,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
FetchVideoInfo(T,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)
NormalizeStrmLine(System.String)
FetchShortcutInfo(MediaBrowser.Controller.Entities.BaseItem)
FetchAudioInfo(T,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)