< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.Resolvers.Movies.MovieResolver
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
Line coverage
39%
Covered lines: 99
Uncovered lines: 154
Coverable lines: 253
Total lines: 593
Line coverage: 39.1%
Branch coverage
33%
Covered branches: 58
Total branches: 174
Branch coverage: 33.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 2/11/2026 - 12:13:19 AM Line coverage: 7.2% (18/250) Branch coverage: 4% (7/172) Total lines: 5865/16/2026 - 12:15:55 AM Line coverage: 39.1% (99/253) Branch coverage: 35.6% (62/174) Total lines: 5935/20/2026 - 12:15:44 AM Line coverage: 39.1% (99/253) Branch coverage: 33.3% (58/174) Total lines: 593 2/11/2026 - 12:13:19 AM Line coverage: 7.2% (18/250) Branch coverage: 4% (7/172) Total lines: 5865/16/2026 - 12:15:55 AM Line coverage: 39.1% (99/253) Branch coverage: 35.6% (62/174) Total lines: 5935/20/2026 - 12:15:44 AM Line coverage: 39.1% (99/253) Branch coverage: 33.3% (58/174) Total lines: 593

Coverage delta

Coverage delta 32 -32

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
get_Priority()100%11100%
ResolveMultiple(...)100%44100%
Resolve(...)7.14%12114212.82%
ResolveMultipleInternal(...)45.45%942247.05%
ResolveVideos(...)60%343084.09%
ContainsFile(...)91.66%121290.9%
ContainsFile(...)100%11100%
SetInitialItemValues(...)100%11100%
SetProviderIdsFromPath(...)66.66%1212100%
FindMovie(...)0%1482380%
GetMultiDiscMovie(...)0%7280%
IsInvalid(...)66.66%7666.66%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Collections.Generic;
 5using System.IO;
 6using System.Linq;
 7using System.Text.RegularExpressions;
 8using Emby.Naming.Common;
 9using Emby.Naming.Video;
 10using Jellyfin.Data.Enums;
 11using Jellyfin.Extensions;
 12using MediaBrowser.Controller.Drawing;
 13using MediaBrowser.Controller.Entities;
 14using MediaBrowser.Controller.Entities.Movies;
 15using MediaBrowser.Controller.Entities.TV;
 16using MediaBrowser.Controller.Library;
 17using MediaBrowser.Controller.Providers;
 18using MediaBrowser.Controller.Resolvers;
 19using MediaBrowser.Model.Entities;
 20using MediaBrowser.Model.IO;
 21using Microsoft.Extensions.Logging;
 22
 23namespace Emby.Server.Implementations.Library.Resolvers.Movies
 24{
 25    /// <summary>
 26    /// Class MovieResolver.
 27    /// </summary>
 28    public partial class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
 29    {
 30        private readonly IImageProcessor _imageProcessor;
 31        private readonly VideoListResolver _videoListResolver;
 32
 133        private static readonly CollectionType[] _validCollectionTypes =
 134        [
 135            CollectionType.movies,
 136            CollectionType.homevideos,
 137            CollectionType.musicvideos,
 138            CollectionType.tvshows,
 139            CollectionType.photos
 140        ];
 41
 42        /// <summary>
 43        /// Initializes a new instance of the <see cref="MovieResolver"/> class.
 44        /// </summary>
 45        /// <param name="imageProcessor">The image processor.</param>
 46        /// <param name="logger">The logger.</param>
 47        /// <param name="namingOptions">The naming options.</param>
 48        /// <param name="directoryService">The directory service.</param>
 49        /// <param name="videoListResolver">The video list resolver.</param>
 50        public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions,
 2451            : base(logger, namingOptions, directoryService)
 52        {
 2453            _imageProcessor = imageProcessor;
 2454            _videoListResolver = videoListResolver;
 2455        }
 56
 57        /// <summary>
 58        /// Gets the priority.
 59        /// </summary>
 60        /// <value>The priority.</value>
 2161        public override ResolverPriority Priority => ResolverPriority.Fourth;
 62
 63        [GeneratedRegex(@"\bsample\b", RegexOptions.IgnoreCase)]
 64        private static partial Regex IsIgnoredRegex();
 65
 66        /// <inheritdoc />
 67        public MultiItemResolverResult ResolveMultiple(
 68            Folder parent,
 69            List<FileSystemMetadata> files,
 70            CollectionType? collectionType,
 71            IDirectoryService directoryService)
 72        {
 6073            var result = ResolveMultipleInternal(parent, files, collectionType);
 74
 6075            if (result is not null)
 76            {
 1077                foreach (var item in result.Items)
 78                {
 379                    SetInitialItemValues((Video)item, null);
 80                }
 81            }
 82
 6083            return result;
 84        }
 85
 86        /// <summary>
 87        /// Resolves the specified args.
 88        /// </summary>
 89        /// <param name="args">The args.</param>
 90        /// <returns>Video.</returns>
 91        protected override Video Resolve(ItemResolveArgs args)
 92        {
 1293            var collectionType = args.GetCollectionType();
 94
 95            // Find movies with their own folders
 1296            if (args.IsDirectory)
 97            {
 098                if (IsInvalid(args.Parent, collectionType))
 99                {
 0100                    return null;
 101                }
 102
 0103                Video movie = null;
 0104                var files = args.GetActualFileSystemChildren().ToList();
 105
 0106                if (collectionType == CollectionType.musicvideos)
 107                {
 0108                    movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, DirectoryService, collectionType,
 109                }
 110
 0111                if (collectionType == CollectionType.homevideos)
 112                {
 0113                    movie = FindMovie<Video>(args, args.Path, args.Parent, files, DirectoryService, collectionType, fals
 114                }
 115
 0116                if (collectionType is null)
 117                {
 118                    // Owned items will be caught by the video extra resolver
 0119                    if (args.Parent is null)
 120                    {
 0121                        return null;
 122                    }
 123
 0124                    if (args.HasParent<Series>())
 125                    {
 0126                        return null;
 127                    }
 128
 0129                    movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true
 130                }
 131
 0132                if (collectionType == CollectionType.movies)
 133                {
 0134                    movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true
 135                }
 136
 137                // ignore extras
 0138                return movie?.ExtraType is null ? movie : null;
 139            }
 140
 12141            if (args.Parent is null)
 142            {
 1143                return base.Resolve(args);
 144            }
 145
 11146            if (IsInvalid(args.Parent, collectionType))
 147            {
 11148                return null;
 149            }
 150
 0151            Video item = null;
 152
 0153            if (collectionType == CollectionType.musicvideos)
 154            {
 0155                item = ResolveVideo<MusicVideo>(args, false);
 156            }
 157
 158            // To find a movie file, the collection type must be movies or boxsets
 0159            else if (collectionType == CollectionType.movies)
 160            {
 0161                item = ResolveVideo<Movie>(args, true);
 162            }
 0163            else if (collectionType == CollectionType.homevideos || collectionType == CollectionType.photos)
 164            {
 0165                item = ResolveVideo<Video>(args, false);
 166            }
 0167            else if (collectionType is null)
 168            {
 0169                if (args.HasParent<Series>())
 170                {
 0171                    return null;
 172                }
 173
 0174                item = ResolveVideo<Video>(args, false);
 175            }
 176
 177            // Ignore extras
 0178            if (item?.ExtraType is not null)
 179            {
 0180                return null;
 181            }
 182
 0183            if (item is not null)
 184            {
 0185                item.IsInMixedFolder = true;
 186            }
 187
 0188            return item;
 189        }
 190
 191        private MultiItemResolverResult ResolveMultipleInternal(
 192            Folder parent,
 193            List<FileSystemMetadata> files,
 194            CollectionType? collectionType)
 195        {
 60196            if (IsInvalid(parent, collectionType))
 197            {
 58198                return null;
 199            }
 200
 2201            if (collectionType is CollectionType.musicvideos)
 202            {
 0203                return ResolveVideos<MusicVideo>(parent, files, true, collectionType, false);
 204            }
 205
 2206            if (collectionType == CollectionType.homevideos || collectionType == CollectionType.photos)
 207            {
 0208                return ResolveVideos<Video>(parent, files, false, collectionType, false);
 209            }
 210
 2211            if (collectionType is null)
 212            {
 213                // Owned items should just use the plain video type
 0214                if (parent is null)
 215                {
 0216                    return ResolveVideos<Video>(parent, files, false, collectionType, false);
 217                }
 218
 0219                if (parent is Series || parent.GetParents().OfType<Series>().Any())
 220                {
 0221                    return null;
 222                }
 223
 0224                return ResolveVideos<Movie>(parent, files, false, collectionType, true);
 225            }
 226
 2227            if (collectionType == CollectionType.movies)
 228            {
 1229                return ResolveVideos<Movie>(parent, files, true, collectionType, true);
 230            }
 231
 1232            if (collectionType == CollectionType.tvshows)
 233            {
 1234                return ResolveVideos<Episode>(parent, files, true, collectionType, true);
 235            }
 236
 0237            return null;
 238        }
 239
 240        private MultiItemResolverResult ResolveVideos<T>(
 241            Folder parent,
 242            IEnumerable<FileSystemMetadata> fileSystemEntries,
 243            bool supportMultiEditions,
 244            CollectionType? collectionType,
 245            bool parseName)
 246            where T : Video, new()
 247        {
 2248            var files = new List<FileSystemMetadata>();
 2249            var leftOver = new List<FileSystemMetadata>();
 2250            var hasCollectionType = collectionType is not null;
 251
 252            // Loop through each child file/folder and see if we find a video
 14253            foreach (var child in fileSystemEntries)
 254            {
 255                // This is a hack but currently no better way to resolve a sometimes ambiguous situation
 5256                if (!hasCollectionType)
 257                {
 0258                    if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase)
 0259                        || string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase))
 260                    {
 0261                        return null;
 262                    }
 263                }
 264
 5265                if (child.IsDirectory)
 266                {
 0267                    leftOver.Add(child);
 268                }
 5269                else if (!IsIgnoredRegex().IsMatch(child.Name))
 270                {
 5271                    files.Add(child);
 272                }
 273            }
 274
 2275            var videoInfos = files
 2276                .Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, NamingOptions, parseName, parent.Containin
 2277                .Where(f => f is not null)
 2278                .ToList();
 279
 2280            var resolverResult = _videoListResolver.Resolve(videoInfos, supportMultiEditions, parseName, parent.Containi
 281
 2282            var result = new MultiItemResolverResult
 2283            {
 2284                ExtraFiles = leftOver
 2285            };
 286
 2287            var isInMixedFolder = resolverResult.Count > 1 || parent?.IsTopParent == true;
 288
 10289            foreach (var video in resolverResult)
 290            {
 3291                var firstVideo = video.Files[0];
 3292                var path = firstVideo.Path;
 3293                if (video.ExtraType is not null)
 294                {
 0295                    result.ExtraFiles.Add(files.Find(f => string.Equals(f.FullName, path, StringComparison.OrdinalIgnore
 0296                    continue;
 297                }
 298
 3299                var additionalParts = video.Files.Count > 1 ? video.Files.Skip(1).Select(i => i.Path).ToArray() : Array.
 300
 3301                var videoItem = new T
 3302                {
 3303                    Path = path,
 3304                    IsInMixedFolder = isInMixedFolder,
 3305                    ProductionYear = video.Year,
 3306                    Name = parseName ? video.Name : firstVideo.Name,
 3307                    AdditionalParts = additionalParts,
 3308                    LocalAlternateVersions = video.AlternateVersions.Select(av => av.Files[0].Path).ToArray()
 3309                };
 310
 3311                SetVideoType(videoItem, firstVideo);
 3312                Set3DFormat(videoItem, firstVideo);
 313
 3314                result.Items.Add(videoItem);
 315            }
 316
 2317            result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i)));
 318
 2319            return result;
 0320        }
 321
 322        private static bool ContainsFile(IReadOnlyList<VideoInfo> result, FileSystemMetadata file)
 323        {
 12324            for (var i = 0; i < result.Count; i++)
 325            {
 6326                var current = result[i];
 18327                for (var j = 0; j < current.Files.Count; j++)
 328                {
 6329                    if (ContainsFile(current.Files[j], file))
 330                    {
 3331                        return true;
 332                    }
 333                }
 334
 8335                for (var j = 0; j < current.AlternateVersions.Count; j++)
 336                {
 3337                    var alternate = current.AlternateVersions[j];
 8338                    for (var k = 0; k < alternate.Files.Count; k++)
 339                    {
 3340                        if (ContainsFile(alternate.Files[k], file))
 341                        {
 2342                            return true;
 343                        }
 344                    }
 345                }
 346            }
 347
 0348            return false;
 349        }
 350
 351        private static bool ContainsFile(VideoFileInfo result, FileSystemMetadata file)
 352        {
 9353            return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase);
 354        }
 355
 356        /// <summary>
 357        /// Sets the initial item values.
 358        /// </summary>
 359        /// <param name="item">The item.</param>
 360        /// <param name="args">The args.</param>
 361        protected override void SetInitialItemValues(Video item, ItemResolveArgs args)
 362        {
 3363            base.SetInitialItemValues(item, args);
 364
 3365            SetProviderIdsFromPath(item);
 3366        }
 367
 368        /// <summary>
 369        /// Sets the provider id from path.
 370        /// </summary>
 371        /// <param name="item">The item.</param>
 372        private static void SetProviderIdsFromPath(Video item)
 373        {
 3374            if (item is Movie || item is MusicVideo)
 375            {
 376                // We need to only look at the name of this actual item (not parents)
 1377                var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path.AsSpan()) : Path.GetFileName(item.Conta
 378
 1379                var tmdbid = justName.GetAttributeValue("tmdbid");
 380
 381                // If not in a mixed folder and ID not found in folder path, check filename
 1382                if (string.IsNullOrEmpty(tmdbid) && !item.IsInMixedFolder)
 383                {
 1384                    tmdbid = Path.GetFileName(item.Path.AsSpan()).GetAttributeValue("tmdbid");
 385                }
 386
 1387                item.TrySetProviderId(MetadataProvider.Tmdb, tmdbid);
 388
 1389                if (!string.IsNullOrEmpty(item.Path))
 390                {
 391                    // Check for IMDb id - we use full media path, as we can assume that this will match in any use case
 1392                    var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid");
 1393                    item.TrySetProviderId(MetadataProvider.Imdb, imdbid);
 394                }
 395            }
 3396        }
 397
 398        /// <summary>
 399        /// Finds a movie based on a child file system entries.
 400        /// </summary>
 401        /// <returns>Movie.</returns>
 402        private T FindMovie<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntr
 403            where T : Video, new()
 404        {
 0405            var multiDiscFolders = new List<FileSystemMetadata>();
 406
 0407            var libraryOptions = args.LibraryOptions;
 0408            var supportPhotos = collectionType == CollectionType.homevideos && libraryOptions.EnablePhotos;
 0409            var photos = new List<FileSystemMetadata>();
 410
 411            // Search for a folder rip
 0412            foreach (var child in fileSystemEntries)
 413            {
 0414                var filename = child.Name;
 415
 0416                if (child.IsDirectory)
 417                {
 0418                    if (NamingOptions.AllExtrasTypesFolderNames.ContainsKey(filename))
 419                    {
 420                        continue;
 421                    }
 422
 0423                    if (IsDvdDirectory(child.FullName, filename, directoryService))
 424                    {
 0425                        var movie = new T
 0426                        {
 0427                            Path = path,
 0428                            VideoType = VideoType.Dvd
 0429                        };
 0430                        Set3DFormat(movie);
 0431                        return movie;
 432                    }
 433
 0434                    if (IsBluRayDirectory(filename))
 435                    {
 0436                        var movie = new T
 0437                        {
 0438                            Path = path,
 0439                            VideoType = VideoType.BluRay
 0440                        };
 0441                        Set3DFormat(movie);
 0442                        return movie;
 443                    }
 444
 0445                    multiDiscFolders.Add(child);
 446                }
 0447                else if (IsDvdFile(filename))
 448                {
 0449                    var movie = new T
 0450                    {
 0451                        Path = path,
 0452                        VideoType = VideoType.Dvd
 0453                    };
 0454                    Set3DFormat(movie);
 0455                    return movie;
 456                }
 0457                else if (supportPhotos && PhotoResolver.IsImageFile(child.FullName, _imageProcessor))
 458                {
 0459                    photos.Add(child);
 460                }
 461            }
 462
 463            // TODO: Allow GetMultiDiscMovie in here
 464            const bool SupportsMultiVersion = true;
 465
 0466            var result = ResolveVideos<T>(parent, fileSystemEntries, SupportsMultiVersion, collectionType, parseName) ??
 0467                new MultiItemResolverResult();
 468
 0469            var isPhotosCollection = collectionType == CollectionType.homevideos || collectionType == CollectionType.pho
 0470            if (!isPhotosCollection && result.Items.Count == 1)
 471            {
 0472                var videoPath = result.Items[0].Path;
 0473                var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(videoPath, i.Name));
 0474                var hasOtherSubfolders = multiDiscFolders.Count > 0;
 475
 0476                if (!hasPhotos && !hasOtherSubfolders)
 477                {
 0478                    var movie = (T)result.Items[0];
 0479                    movie.IsInMixedFolder = false;
 0480                    if (collectionType == CollectionType.movies || collectionType is null)
 481                    {
 0482                        movie.Name = Path.GetFileName(movie.ContainingFolderPath);
 483                    }
 484
 0485                    return movie;
 486                }
 487            }
 0488            else if (result.Items.Count == 0 && multiDiscFolders.Count > 0)
 489            {
 0490                return GetMultiDiscMovie<T>(multiDiscFolders, directoryService);
 491            }
 492
 0493            return null;
 0494        }
 495
 496        /// <summary>
 497        /// Gets the multi disc movie.
 498        /// </summary>
 499        /// <param name="multiDiscFolders">The folders.</param>
 500        /// <param name="directoryService">The directory service.</param>
 501        /// <returns>``0.</returns>
 502        private T GetMultiDiscMovie<T>(List<FileSystemMetadata> multiDiscFolders, IDirectoryService directoryService)
 503               where T : Video, new()
 504        {
 0505            var videoTypes = new List<VideoType>();
 506
 0507            var folderPaths = multiDiscFolders.Select(i => i.FullName).Where(i =>
 0508            {
 0509                var subFileEntries = directoryService.GetFileSystemEntries(i);
 0510
 0511                var subfolders = subFileEntries
 0512                    .Where(e => e.IsDirectory)
 0513                    .ToList();
 0514
 0515                if (subfolders.Any(s => IsDvdDirectory(s.FullName, s.Name, directoryService)))
 0516                {
 0517                    videoTypes.Add(VideoType.Dvd);
 0518                    return true;
 0519                }
 0520
 0521                if (subfolders.Any(s => IsBluRayDirectory(s.Name)))
 0522                {
 0523                    videoTypes.Add(VideoType.BluRay);
 0524                    return true;
 0525                }
 0526
 0527                var subFiles = subFileEntries
 0528                 .Where(e => !e.IsDirectory)
 0529                 .Select(d => d.Name);
 0530
 0531                if (subFiles.Any(IsDvdFile))
 0532                {
 0533                    videoTypes.Add(VideoType.Dvd);
 0534                    return true;
 0535                }
 0536
 0537                return false;
 0538            }).Order().ToList();
 539
 540            // If different video types were found, don't allow this
 0541            if (videoTypes.Distinct().Count() > 1)
 542            {
 0543                return null;
 544            }
 545
 0546            if (folderPaths.Count == 0)
 547            {
 0548                return null;
 549            }
 550
 0551            var result = StackResolver.ResolveDirectories(folderPaths, NamingOptions).ToList();
 552
 0553            if (result.Count != 1)
 554            {
 0555                return null;
 556            }
 557
 0558            int additionalPartsLen = folderPaths.Count - 1;
 0559            var additionalParts = new string[additionalPartsLen];
 0560            folderPaths.CopyTo(1, additionalParts, 0, additionalPartsLen);
 561
 0562            var returnVideo = new T
 0563            {
 0564                Path = folderPaths[0],
 0565                AdditionalParts = additionalParts,
 0566                VideoType = videoTypes[0],
 0567                Name = result[0].Name
 0568            };
 569
 0570            SetIsoType(returnVideo);
 571
 0572            return returnVideo;
 573        }
 574
 575        private bool IsInvalid(Folder parent, CollectionType? collectionType)
 576        {
 71577            if (parent is not null)
 578            {
 71579                if (parent.IsRoot)
 580                {
 69581                    return true;
 582                }
 583            }
 584
 2585            if (collectionType is null)
 586            {
 0587                return false;
 588            }
 589
 2590            return !_validCollectionTypes.Contains(collectionType.Value);
 591        }
 592    }
 593}

Methods/Properties

.cctor()
.ctor(MediaBrowser.Controller.Drawing.IImageProcessor,Microsoft.Extensions.Logging.ILogger`1<Emby.Server.Implementations.Library.Resolvers.Movies.MovieResolver>,Emby.Naming.Common.NamingOptions,MediaBrowser.Controller.Providers.IDirectoryService,Emby.Naming.Video.VideoListResolver)
get_Priority()
ResolveMultiple(MediaBrowser.Controller.Entities.Folder,System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.Nullable`1<Jellyfin.Data.Enums.CollectionType>,MediaBrowser.Controller.Providers.IDirectoryService)
Resolve(MediaBrowser.Controller.Library.ItemResolveArgs)
ResolveMultipleInternal(MediaBrowser.Controller.Entities.Folder,System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.Nullable`1<Jellyfin.Data.Enums.CollectionType>)
ResolveVideos(MediaBrowser.Controller.Entities.Folder,System.Collections.Generic.IEnumerable`1<MediaBrowser.Model.IO.FileSystemMetadata>,System.Boolean,System.Nullable`1<Jellyfin.Data.Enums.CollectionType>,System.Boolean)
ContainsFile(System.Collections.Generic.IReadOnlyList`1<Emby.Naming.Video.VideoInfo>,MediaBrowser.Model.IO.FileSystemMetadata)
ContainsFile(Emby.Naming.Video.VideoFileInfo,MediaBrowser.Model.IO.FileSystemMetadata)
SetInitialItemValues(MediaBrowser.Controller.Entities.Video,MediaBrowser.Controller.Library.ItemResolveArgs)
SetProviderIdsFromPath(MediaBrowser.Controller.Entities.Video)
FindMovie(MediaBrowser.Controller.Library.ItemResolveArgs,System.String,MediaBrowser.Controller.Entities.Folder,System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,MediaBrowser.Controller.Providers.IDirectoryService,System.Nullable`1<Jellyfin.Data.Enums.CollectionType>,System.Boolean)
GetMultiDiscMovie(System.Collections.Generic.List`1<MediaBrowser.Model.IO.FileSystemMetadata>,MediaBrowser.Controller.Providers.IDirectoryService)
IsInvalid(MediaBrowser.Controller.Entities.Folder,System.Nullable`1<Jellyfin.Data.Enums.CollectionType>)