Browse Source

Convert CollectionType, SpecialFolderType to enum (#9764)

* Convert CollectionType, SpecialFolderType to enum

* Hide internal enum CollectionType values

* Apply suggestions from code review

Co-authored-by: Shadowghost <Shadowghost@users.noreply.github.com>

* Fix recent change

* Update Jellyfin.Data/Attributes/OpenApiIgnoreEnumAttribute.cs

Co-authored-by: Patrick Barron <barronpm@gmail.com>

---------

Co-authored-by: Shadowghost <Shadowghost@users.noreply.github.com>
Co-authored-by: Patrick Barron <barronpm@gmail.com>
Cody Robibero 1 year ago
parent
commit
906f701fa8
43 changed files with 486 additions and 288 deletions
  1. 11 23
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  2. 28 32
      Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
  3. 3 3
      Emby.Server.Implementations/Images/DynamicImageProvider.cs
  4. 37 32
      Emby.Server.Implementations/Library/LibraryManager.cs
  5. 7 7
      Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
  6. 2 1
      Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
  7. 2 1
      Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
  8. 2 1
      Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
  9. 30 32
      Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  10. 3 2
      Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
  11. 3 2
      Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
  12. 4 3
      Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
  13. 4 2
      Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
  14. 4 3
      Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
  15. 4 3
      Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  16. 17 18
      Emby.Server.Implementations/Library/UserViewManager.cs
  17. 1 1
      Emby.Server.Implementations/Playlists/PlaylistsFolder.cs
  18. 2 2
      Jellyfin.Api/Controllers/GenresController.cs
  19. 4 5
      Jellyfin.Api/Controllers/ItemUpdateController.cs
  20. 2 2
      Jellyfin.Api/Controllers/ItemsController.cs
  21. 2 2
      Jellyfin.Api/Controllers/LibraryController.cs
  22. 2 1
      Jellyfin.Api/Controllers/UserViewsController.cs
  23. 11 0
      Jellyfin.Data/Attributes/OpenApiIgnoreEnumAttribute.cs
  24. 164 0
      Jellyfin.Data/Enums/CollectionType.cs
  25. 1 0
      Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
  26. 42 0
      Jellyfin.Server/Filters/IgnoreEnumSchemaFilter.cs
  27. 1 1
      MediaBrowser.Controller/Entities/BaseItem.cs
  28. 2 1
      MediaBrowser.Controller/Entities/BasePluginFolder.cs
  29. 2 1
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  30. 2 1
      MediaBrowser.Controller/Entities/ICollectionFolder.cs
  31. 2 2
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  32. 19 18
      MediaBrowser.Controller/Entities/UserView.cs
  33. 39 39
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  34. 10 10
      MediaBrowser.Controller/Library/ILibraryManager.cs
  35. 2 1
      MediaBrowser.Controller/Library/IUserViewManager.cs
  36. 4 3
      MediaBrowser.Controller/Library/ItemResolveArgs.cs
  37. 2 1
      MediaBrowser.Controller/Resolvers/IItemResolver.cs
  38. 1 1
      MediaBrowser.Model/Dto/BaseItemDto.cs
  39. 2 1
      MediaBrowser.Model/Dto/MetadataEditorInfo.cs
  40. 0 27
      MediaBrowser.Model/Entities/CollectionType.cs
  41. 3 2
      MediaBrowser.Model/Library/UserViewQuery.cs
  42. 2 1
      tests/Jellyfin.Server.Implementations.Tests/Library/AudioResolverTests.cs
  43. 1 0
      tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs

+ 11 - 23
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -565,30 +565,18 @@ namespace Emby.Dlna.ContentDirectory
 
             if (stubType != StubType.Folder && item is IHasCollectionType collectionFolder)
             {
-                var collectionType = collectionFolder.CollectionType;
-                if (string.Equals(CollectionType.Music, collectionType, StringComparison.OrdinalIgnoreCase))
+                switch (collectionFolder.CollectionType)
                 {
-                    return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
-                }
-
-                if (string.Equals(CollectionType.Movies, collectionType, StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
-                }
-
-                if (string.Equals(CollectionType.TvShows, collectionType, StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetTvFolders(item, user, stubType, sort, startIndex, limit);
-                }
-
-                if (string.Equals(CollectionType.Folders, collectionType, StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetFolders(user, startIndex, limit);
-                }
-
-                if (string.Equals(CollectionType.LiveTv, collectionType, StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetLiveTvChannels(user, sort, startIndex, limit);
+                    case CollectionType.Music:
+                        return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
+                    case CollectionType.Movies:
+                        return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
+                    case CollectionType.TvShows:
+                        return GetTvFolders(item, user, stubType, sort, startIndex, limit);
+                    case CollectionType.Folders:
+                        return GetFolders(user, startIndex, limit);
+                    case CollectionType.LiveTv:
+                        return GetLiveTvChannels(user, sort, startIndex, limit);
                 }
             }
 

+ 28 - 32
Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs

@@ -30,47 +30,43 @@ namespace Emby.Server.Implementations.Images
 
             BaseItemKind[] includeItemTypes;
 
-            if (string.Equals(viewType, CollectionType.Movies, StringComparison.Ordinal))
+            switch (viewType)
             {
-                includeItemTypes = new[] { BaseItemKind.Movie };
-            }
-            else if (string.Equals(viewType, CollectionType.TvShows, StringComparison.Ordinal))
-            {
-                includeItemTypes = new[] { BaseItemKind.Series };
-            }
-            else if (string.Equals(viewType, CollectionType.Music, StringComparison.Ordinal))
-            {
-                includeItemTypes = new[] { BaseItemKind.MusicAlbum };
-            }
-            else if (string.Equals(viewType, CollectionType.MusicVideos, StringComparison.Ordinal))
-            {
-                includeItemTypes = new[] { BaseItemKind.MusicVideo };
-            }
-            else if (string.Equals(viewType, CollectionType.Books, StringComparison.Ordinal))
-            {
-                includeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook };
-            }
-            else if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.Ordinal))
-            {
-                includeItemTypes = new[] { BaseItemKind.BoxSet };
-            }
-            else if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.Ordinal) || string.Equals(viewType, CollectionType.Photos, StringComparison.Ordinal))
-            {
-                includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Photo };
-            }
-            else
-            {
-                includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Audio, BaseItemKind.Photo, BaseItemKind.Movie, BaseItemKind.Series };
+                case CollectionType.Movies:
+                    includeItemTypes = new[] { BaseItemKind.Movie };
+                    break;
+                case CollectionType.TvShows:
+                    includeItemTypes = new[] { BaseItemKind.Series };
+                    break;
+                case CollectionType.Music:
+                    includeItemTypes = new[] { BaseItemKind.MusicAlbum };
+                    break;
+                case CollectionType.MusicVideos:
+                    includeItemTypes = new[] { BaseItemKind.MusicVideo };
+                    break;
+                case CollectionType.Books:
+                    includeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook };
+                    break;
+                case CollectionType.BoxSets:
+                    includeItemTypes = new[] { BaseItemKind.BoxSet };
+                    break;
+                case CollectionType.HomeVideos:
+                case CollectionType.Photos:
+                    includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Photo };
+                    break;
+                default:
+                    includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Audio, BaseItemKind.Photo, BaseItemKind.Movie, BaseItemKind.Series };
+                    break;
             }
 
-            var recursive = !string.Equals(CollectionType.Playlists, viewType, StringComparison.OrdinalIgnoreCase);
+            var recursive = viewType != CollectionType.Playlists;
 
             return view.GetItemList(new InternalItemsQuery
             {
                 CollapseBoxSetItems = false,
                 Recursive = recursive,
                 DtoOptions = new DtoOptions(false),
-                ImageTypes = new ImageType[] { ImageType.Primary },
+                ImageTypes = new[] { ImageType.Primary },
                 Limit = 8,
                 OrderBy = new[]
                 {

+ 3 - 3
Emby.Server.Implementations/Images/DynamicImageProvider.cs

@@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Images
             var view = (UserView)item;
 
             var isUsingCollectionStrip = IsUsingCollectionStrip(view);
-            var recursive = isUsingCollectionStrip && !new[] { CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
+            var recursive = isUsingCollectionStrip && view?.ViewType is not null && view.ViewType != CollectionType.BoxSets && view.ViewType != CollectionType.Playlists;
 
             var result = view.GetItemList(new InternalItemsQuery
             {
@@ -112,14 +112,14 @@ namespace Emby.Server.Implementations.Images
 
         private static bool IsUsingCollectionStrip(UserView view)
         {
-            string[] collectionStripViewTypes =
+            CollectionType[] collectionStripViewTypes =
             {
                 CollectionType.Movies,
                 CollectionType.TvShows,
                 CollectionType.Playlists
             };
 
-            return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);
+            return view?.ViewType is not null && collectionStripViewTypes.Contains(view.ViewType.Value);
         }
 
         protected override string CreateImage(BaseItem item, IReadOnlyCollection<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)

+ 37 - 32
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -525,14 +525,14 @@ namespace Emby.Server.Implementations.Library
             IDirectoryService directoryService,
             IItemResolver[] resolvers,
             Folder parent = null,
-            string collectionType = null,
+            CollectionType? collectionType = null,
             LibraryOptions libraryOptions = null)
         {
             ArgumentNullException.ThrowIfNull(fileInfo);
 
             var fullPath = fileInfo.FullName;
 
-            if (string.IsNullOrEmpty(collectionType) && parent is not null)
+            if (collectionType is null && parent is not null)
             {
                 collectionType = GetContentTypeOverride(fullPath, true);
             }
@@ -635,7 +635,7 @@ namespace Emby.Server.Implementations.Library
             return !args.ContainsFileSystemEntryByName(".ignore");
         }
 
-        public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType = null)
+        public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, CollectionType? collectionType = null)
         {
             return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
         }
@@ -645,7 +645,7 @@ namespace Emby.Server.Implementations.Library
             IDirectoryService directoryService,
             Folder parent,
             LibraryOptions libraryOptions,
-            string collectionType,
+            CollectionType? collectionType,
             IItemResolver[] resolvers)
         {
             var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList();
@@ -675,7 +675,7 @@ namespace Emby.Server.Implementations.Library
             IReadOnlyList<FileSystemMetadata> fileList,
             IDirectoryService directoryService,
             Folder parent,
-            string collectionType,
+            CollectionType? collectionType,
             IItemResolver[] resolvers,
             LibraryOptions libraryOptions)
         {
@@ -1514,7 +1514,7 @@ namespace Emby.Server.Implementations.Library
         {
             if (item is UserView view)
             {
-                if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.Ordinal))
+                if (view.ViewType == CollectionType.LiveTv)
                 {
                     return new[] { view.Id };
                 }
@@ -1543,13 +1543,13 @@ namespace Emby.Server.Implementations.Library
                 }
 
                 // Handle grouping
-                if (user is not null && !string.IsNullOrEmpty(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)
+                if (user is not null && view.ViewType != CollectionType.Unknown && UserView.IsEligibleForGrouping(view.ViewType)
                     && user.GetPreference(PreferenceKind.GroupedFolders).Length > 0)
                 {
                     return GetUserRootFolder()
                         .GetChildren(user, true)
                         .OfType<CollectionFolder>()
-                        .Where(i => string.IsNullOrEmpty(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase))
+                        .Where(i => i.CollectionType is null || i.CollectionType == view.ViewType)
                         .Where(i => user.IsFolderGrouped(i.Id))
                         .SelectMany(i => GetTopParentIdsForQuery(i, user));
                 }
@@ -2065,16 +2065,16 @@ namespace Emby.Server.Implementations.Library
                 : collectionFolder.GetLibraryOptions();
         }
 
-        public string GetContentType(BaseItem item)
+        public CollectionType? GetContentType(BaseItem item)
         {
-            string configuredContentType = GetConfiguredContentType(item, false);
-            if (!string.IsNullOrEmpty(configuredContentType))
+            var configuredContentType = GetConfiguredContentType(item, false);
+            if (configuredContentType is not null)
             {
                 return configuredContentType;
             }
 
             configuredContentType = GetConfiguredContentType(item, true);
-            if (!string.IsNullOrEmpty(configuredContentType))
+            if (configuredContentType is not null)
             {
                 return configuredContentType;
             }
@@ -2082,31 +2082,31 @@ namespace Emby.Server.Implementations.Library
             return GetInheritedContentType(item);
         }
 
-        public string GetInheritedContentType(BaseItem item)
+        public CollectionType? GetInheritedContentType(BaseItem item)
         {
             var type = GetTopFolderContentType(item);
 
-            if (!string.IsNullOrEmpty(type))
+            if (type is not null)
             {
                 return type;
             }
 
             return item.GetParents()
                 .Select(GetConfiguredContentType)
-                .LastOrDefault(i => !string.IsNullOrEmpty(i));
+                .LastOrDefault(i => i is not null);
         }
 
-        public string GetConfiguredContentType(BaseItem item)
+        public CollectionType? GetConfiguredContentType(BaseItem item)
         {
             return GetConfiguredContentType(item, false);
         }
 
-        public string GetConfiguredContentType(string path)
+        public CollectionType? GetConfiguredContentType(string path)
         {
             return GetContentTypeOverride(path, false);
         }
 
-        public string GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath)
+        public CollectionType? GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath)
         {
             if (item is ICollectionFolder collectionFolder)
             {
@@ -2116,16 +2116,21 @@ namespace Emby.Server.Implementations.Library
             return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath);
         }
 
-        private string GetContentTypeOverride(string path, bool inherit)
+        private CollectionType? GetContentTypeOverride(string path, bool inherit)
         {
             var nameValuePair = _configurationManager.Configuration.ContentTypes
                                     .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path)
                                                          || (inherit && !string.IsNullOrEmpty(i.Name)
                                                                      && _fileSystem.ContainsSubPath(i.Name, path)));
-            return nameValuePair?.Value;
+            if (Enum.TryParse<CollectionType>(nameValuePair?.Value, out var collectionType))
+            {
+                return collectionType;
+            }
+
+            return null;
         }
 
-        private string GetTopFolderContentType(BaseItem item)
+        private CollectionType? GetTopFolderContentType(BaseItem item)
         {
             if (item is null)
             {
@@ -2147,13 +2152,13 @@ namespace Emby.Server.Implementations.Library
                 .OfType<ICollectionFolder>()
                 .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path))
                 .Select(i => i.CollectionType)
-                .FirstOrDefault(i => !string.IsNullOrEmpty(i));
+                .FirstOrDefault(i => i is not null);
         }
 
         public UserView GetNamedView(
             User user,
             string name,
-            string viewType,
+            CollectionType? viewType,
             string sortName)
         {
             return GetNamedView(user, name, Guid.Empty, viewType, sortName);
@@ -2161,13 +2166,13 @@ namespace Emby.Server.Implementations.Library
 
         public UserView GetNamedView(
             string name,
-            string viewType,
+            CollectionType viewType,
             string sortName)
         {
             var path = Path.Combine(
                 _configurationManager.ApplicationPaths.InternalMetadataPath,
                 "views",
-                _fileSystem.GetValidFilename(viewType));
+                _fileSystem.GetValidFilename(viewType.ToString()));
 
             var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
 
@@ -2207,13 +2212,13 @@ namespace Emby.Server.Implementations.Library
             User user,
             string name,
             Guid parentId,
-            string viewType,
+            CollectionType? viewType,
             string sortName)
         {
             var parentIdString = parentId.Equals(default)
                 ? null
                 : parentId.ToString("N", CultureInfo.InvariantCulture);
-            var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
+            var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType?.ToString() ?? string.Empty);
 
             var id = GetNewItemId(idValues, typeof(UserView));
 
@@ -2269,7 +2274,7 @@ namespace Emby.Server.Implementations.Library
 
         public UserView GetShadowView(
             BaseItem parent,
-            string viewType,
+            CollectionType? viewType,
             string sortName)
         {
             ArgumentNullException.ThrowIfNull(parent);
@@ -2277,7 +2282,7 @@ namespace Emby.Server.Implementations.Library
             var name = parent.Name;
             var parentId = parent.Id;
 
-            var idValues = "38_namedview_" + name + parentId + (viewType ?? string.Empty);
+            var idValues = "38_namedview_" + name + parentId + (viewType?.ToString() ?? string.Empty);
 
             var id = GetNewItemId(idValues, typeof(UserView));
 
@@ -2334,7 +2339,7 @@ namespace Emby.Server.Implementations.Library
         public UserView GetNamedView(
             string name,
             Guid parentId,
-            string viewType,
+            CollectionType? viewType,
             string sortName,
             string uniqueId)
         {
@@ -2343,7 +2348,7 @@ namespace Emby.Server.Implementations.Library
             var parentIdString = parentId.Equals(default)
                 ? null
                 : parentId.ToString("N", CultureInfo.InvariantCulture);
-            var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
+            var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType?.ToString() ?? string.Empty);
             if (!string.IsNullOrEmpty(uniqueId))
             {
                 idValues += uniqueId;
@@ -2378,7 +2383,7 @@ namespace Emby.Server.Implementations.Library
                 isNew = true;
             }
 
-            if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
+            if (viewType != item.ViewType)
             {
                 item.ViewType = viewType;
                 item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();

+ 7 - 7
Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs

@@ -10,11 +10,11 @@ using Emby.Naming.Audio;
 using Emby.Naming.AudioBook;
 using Emby.Naming.Common;
 using Emby.Naming.Video;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 
 namespace Emby.Server.Implementations.Library.Resolvers.Audio
@@ -40,7 +40,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
         public MultiItemResolverResult ResolveMultiple(
             Folder parent,
             List<FileSystemMetadata> files,
-            string collectionType,
+            CollectionType? collectionType,
             IDirectoryService directoryService)
         {
             var result = ResolveMultipleInternal(parent, files, collectionType);
@@ -59,9 +59,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
         private MultiItemResolverResult ResolveMultipleInternal(
             Folder parent,
             List<FileSystemMetadata> files,
-            string collectionType)
+            CollectionType? collectionType)
         {
-            if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
+            if (collectionType == CollectionType.Books)
             {
                 return ResolveMultipleAudio(parent, files, true);
             }
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
 
             var collectionType = args.GetCollectionType();
 
-            var isBooksCollectionType = string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase);
+            var isBooksCollectionType = collectionType == CollectionType.Books;
 
             if (args.IsDirectory)
             {
@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
                     return null;
                 }
 
-                var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
+                var isMixedCollectionType = collectionType is null;
 
                 // For conflicting extensions, give priority to videos
                 if (isMixedCollectionType && VideoResolver.IsVideoFile(args.Path, _namingOptions))
@@ -112,7 +112,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
 
                 MediaBrowser.Controller.Entities.Audio.Audio item = null;
 
-                var isMusicCollectionType = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
+                var isMusicCollectionType = collectionType == CollectionType.Music;
 
                 // Use regular audio type for mixed libraries, owned items and music
                 if (isMixedCollectionType ||

+ 2 - 1
Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs

@@ -8,6 +8,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Emby.Naming.Audio;
 using Emby.Naming.Common;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
@@ -54,7 +55,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
         protected override MusicAlbum Resolve(ItemResolveArgs args)
         {
             var collectionType = args.GetCollectionType();
-            var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
+            var isMusicMediaFolder = collectionType == CollectionType.Music;
 
             // If there's a collection type and it's not music, don't allow it.
             if (!isMusicMediaFolder)

+ 2 - 1
Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs

@@ -4,6 +4,7 @@ using System;
 using System.Linq;
 using System.Threading.Tasks;
 using Emby.Naming.Common;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
@@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
 
             var collectionType = args.GetCollectionType();
 
-            var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
+            var isMusicMediaFolder = collectionType == CollectionType.Music;
 
             // If there's a collection type and it's not music, it can't be a music artist
             if (!isMusicMediaFolder)

+ 2 - 1
Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs

@@ -5,6 +5,7 @@
 using System;
 using System.IO;
 using System.Linq;
+using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -22,7 +23,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
             var collectionType = args.GetCollectionType();
 
             // Only process items that are in a collection folder containing books
-            if (!string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
+            if (collectionType != CollectionType.Books)
             {
                 return null;
             }

+ 30 - 32
Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -7,6 +7,7 @@ using System.Linq;
 using System.Text.RegularExpressions;
 using Emby.Naming.Common;
 using Emby.Naming.Video;
+using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
@@ -28,13 +29,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
     {
         private readonly IImageProcessor _imageProcessor;
 
-        private string[] _validCollectionTypes = new[]
+        private static readonly CollectionType[] _validCollectionTypes = new[]
         {
-                CollectionType.Movies,
-                CollectionType.HomeVideos,
-                CollectionType.MusicVideos,
-                CollectionType.TvShows,
-                CollectionType.Photos
+            CollectionType.Movies,
+            CollectionType.HomeVideos,
+            CollectionType.MusicVideos,
+            CollectionType.TvShows,
+            CollectionType.Photos
         };
 
         /// <summary>
@@ -63,7 +64,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         public MultiItemResolverResult ResolveMultiple(
             Folder parent,
             List<FileSystemMetadata> files,
-            string collectionType,
+            CollectionType? collectionType,
             IDirectoryService directoryService)
         {
             var result = ResolveMultipleInternal(parent, files, collectionType);
@@ -99,17 +100,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 Video movie = null;
                 var files = args.GetActualFileSystemChildren().ToList();
 
-                if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
+                if (collectionType == CollectionType.MusicVideos)
                 {
                     movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false);
                 }
 
-                if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
+                if (collectionType == CollectionType.HomeVideos)
                 {
                     movie = FindMovie<Video>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false);
                 }
 
-                if (string.IsNullOrEmpty(collectionType))
+                if (collectionType is null)
                 {
                     // Owned items will be caught by the video extra resolver
                     if (args.Parent is null)
@@ -125,7 +126,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                     movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true);
                 }
 
-                if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
+                if (collectionType == CollectionType.Movies)
                 {
                     movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true);
                 }
@@ -146,22 +147,21 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
 
             Video item = null;
 
-            if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
+            if (collectionType == CollectionType.MusicVideos)
             {
                 item = ResolveVideo<MusicVideo>(args, false);
             }
 
             // To find a movie file, the collection type must be movies or boxsets
-            else if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
+            else if (collectionType == CollectionType.Movies)
             {
                 item = ResolveVideo<Movie>(args, true);
             }
-            else if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
-                string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
+            else if (collectionType == CollectionType.HomeVideos || collectionType == CollectionType.Photos)
             {
                 item = ResolveVideo<Video>(args, false);
             }
-            else if (string.IsNullOrEmpty(collectionType))
+            else if (collectionType is null)
             {
                 if (args.HasParent<Series>())
                 {
@@ -188,25 +188,24 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         private MultiItemResolverResult ResolveMultipleInternal(
             Folder parent,
             List<FileSystemMetadata> files,
-            string collectionType)
+            CollectionType? collectionType)
         {
             if (IsInvalid(parent, collectionType))
             {
                 return null;
             }
 
-            if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
+            if (collectionType is CollectionType.MusicVideos)
             {
                 return ResolveVideos<MusicVideo>(parent, files, true, collectionType, false);
             }
 
-            if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
-                            string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
+            if (collectionType == CollectionType.HomeVideos || collectionType == CollectionType.Photos)
             {
                 return ResolveVideos<Video>(parent, files, false, collectionType, false);
             }
 
-            if (string.IsNullOrEmpty(collectionType))
+            if (collectionType is null)
             {
                 // Owned items should just use the plain video type
                 if (parent is null)
@@ -222,12 +221,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 return ResolveVideos<Movie>(parent, files, false, collectionType, true);
             }
 
-            if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
+            if (collectionType == CollectionType.Movies)
             {
                 return ResolveVideos<Movie>(parent, files, true, collectionType, true);
             }
 
-            if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+            if (collectionType == CollectionType.TvShows)
             {
                 return ResolveVideos<Episode>(parent, files, false, collectionType, true);
             }
@@ -239,13 +238,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             Folder parent,
             IEnumerable<FileSystemMetadata> fileSystemEntries,
             bool supportMultiEditions,
-            string collectionType,
+            CollectionType? collectionType,
             bool parseName)
             where T : Video, new()
         {
             var files = new List<FileSystemMetadata>();
             var leftOver = new List<FileSystemMetadata>();
-            var hasCollectionType = !string.IsNullOrEmpty(collectionType);
+            var hasCollectionType = collectionType is not null;
 
             // Loop through each child file/folder and see if we find a video
             foreach (var child in fileSystemEntries)
@@ -398,13 +397,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
         /// Finds a movie based on a child file system entries.
         /// </summary>
         /// <returns>Movie.</returns>
-        private T FindMovie<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName)
+        private T FindMovie<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, CollectionType? collectionType, bool parseName)
             where T : Video, new()
         {
             var multiDiscFolders = new List<FileSystemMetadata>();
 
             var libraryOptions = args.LibraryOptions;
-            var supportPhotos = string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && libraryOptions.EnablePhotos;
+            var supportPhotos = collectionType == CollectionType.HomeVideos && libraryOptions.EnablePhotos;
             var photos = new List<FileSystemMetadata>();
 
             // Search for a folder rip
@@ -460,8 +459,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             var result = ResolveVideos<T>(parent, fileSystemEntries, SupportsMultiVersion, collectionType, parseName) ??
                 new MultiItemResolverResult();
 
-            var isPhotosCollection = string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)
-                                         || string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase);
+            var isPhotosCollection = collectionType == CollectionType.HomeVideos || collectionType == CollectionType.Photos;
             if (!isPhotosCollection && result.Items.Count == 1)
             {
                 var videoPath = result.Items[0].Path;
@@ -562,7 +560,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             return returnVideo;
         }
 
-        private bool IsInvalid(Folder parent, ReadOnlySpan<char> collectionType)
+        private bool IsInvalid(Folder parent, CollectionType? collectionType)
         {
             if (parent is not null)
             {
@@ -572,12 +570,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 }
             }
 
-            if (collectionType.IsEmpty)
+            if (collectionType is null)
             {
                 return false;
             }
 
-            return !_validCollectionTypes.Contains(collectionType, StringComparison.OrdinalIgnoreCase);
+            return !_validCollectionTypes.Contains(collectionType.Value);
         }
     }
 }

+ 3 - 2
Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs

@@ -2,6 +2,7 @@
 
 using System;
 using Emby.Naming.Common;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -45,8 +46,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
                 // Must be an image file within a photo collection
                 var collectionType = args.GetCollectionType();
 
-                if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)
-                    || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.LibraryOptions.EnablePhotos))
+                if (collectionType == CollectionType.Photos
+                    || (collectionType == CollectionType.HomeVideos && args.LibraryOptions.EnablePhotos))
                 {
                     if (HasPhotos(args))
                     {

+ 3 - 2
Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs

@@ -3,6 +3,7 @@ using System.IO;
 using System.Linq;
 using Emby.Naming.Common;
 using Emby.Naming.Video;
+using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
@@ -60,8 +61,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
                 // Must be an image file within a photo collection
                 var collectionType = args.CollectionType;
 
-                if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase)
-                    || (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.LibraryOptions.EnablePhotos))
+                if (collectionType == CollectionType.Photos
+                    || (collectionType == CollectionType.HomeVideos && args.LibraryOptions.EnablePhotos))
                 {
                     if (IsImageFile(args.Path, _imageProcessor))
                     {

+ 4 - 3
Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs

@@ -5,6 +5,7 @@
 using System;
 using System.IO;
 using System.Linq;
+using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Playlists;
@@ -19,9 +20,9 @@ namespace Emby.Server.Implementations.Library.Resolvers
     /// </summary>
     public class PlaylistResolver : GenericFolderResolver<Playlist>
     {
-        private string[] _musicPlaylistCollectionTypes =
+        private CollectionType?[] _musicPlaylistCollectionTypes =
         {
-            string.Empty,
+            null,
             CollectionType.Music
         };
 
@@ -62,7 +63,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
 
             // Check if this is a music playlist file
             // It should have the correct collection type and a supported file extension
-            else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+            else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType))
             {
                 var extension = Path.GetExtension(args.Path.AsSpan());
                 if (Playlist.SupportedExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))

+ 4 - 2
Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs

@@ -5,6 +5,7 @@
 using System;
 using System.IO;
 using System.Linq;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -62,7 +63,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
             return null;
         }
 
-        private string GetCollectionType(ItemResolveArgs args)
+        private CollectionType? GetCollectionType(ItemResolveArgs args)
         {
             return args.FileSystemChildren
                 .Where(i =>
@@ -78,7 +79,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
                     }
                 })
                 .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
-                .FirstOrDefault();
+                .Select(i => Enum.TryParse<CollectionType>(i, out var collectionType) ? collectionType : (CollectionType?)null)
+                .FirstOrDefault(i => i is not null);
         }
     }
 }

+ 4 - 3
Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Linq;
 using Emby.Naming.Common;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
@@ -48,9 +49,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
 
             // If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something
             // Also handle flat tv folders
-            if (season is not null ||
-                string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) ||
-                args.HasParent<Series>())
+            if (season is not null
+                || args.GetCollectionType() == CollectionType.TvShows
+                || args.HasParent<Series>())
             {
                 var episode = ResolveVideo<Episode>(args, false);
 

+ 4 - 3
Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -8,6 +8,7 @@ using System.IO;
 using Emby.Naming.Common;
 using Emby.Naming.TV;
 using Emby.Naming.Video;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Resolvers;
@@ -59,11 +60,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                 var seriesInfo = Naming.TV.SeriesResolver.Resolve(_namingOptions, args.Path);
 
                 var collectionType = args.GetCollectionType();
-                if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+                if (collectionType == CollectionType.TvShows)
                 {
                     // TODO refactor into separate class or something, this is copied from LibraryManager.GetConfiguredContentType
                     var configuredContentType = args.GetConfiguredContentType();
-                    if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+                    if (configuredContentType != CollectionType.TvShows)
                     {
                         return new Series
                         {
@@ -72,7 +73,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                         };
                     }
                 }
-                else if (string.IsNullOrEmpty(collectionType))
+                else if (collectionType is null)
                 {
                     if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
                     {

+ 17 - 18
Emby.Server.Implementations/Library/UserViewManager.cs

@@ -8,7 +8,6 @@ using System.Linq;
 using System.Threading;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
-using Jellyfin.Extensions;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
@@ -64,8 +63,8 @@ namespace Emby.Server.Implementations.Library
                 var collectionFolder = folder as ICollectionFolder;
                 var folderViewType = collectionFolder?.CollectionType;
 
-                // Playlist library requires special handling because the folder only refrences user playlists
-                if (string.Equals(folderViewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+                // Playlist library requires special handling because the folder only references user playlists
+                if (folderViewType == CollectionType.Playlists)
                 {
                     var items = folder.GetItemList(new InternalItemsQuery(user)
                     {
@@ -90,7 +89,7 @@ namespace Emby.Server.Implementations.Library
                     continue;
                 }
 
-                if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+                if (query.PresetViews.Contains(folderViewType))
                 {
                     list.Add(GetUserView(folder, folderViewType, string.Empty));
                 }
@@ -102,14 +101,14 @@ namespace Emby.Server.Implementations.Library
 
             foreach (var viewType in new[] { CollectionType.Movies, CollectionType.TvShows })
             {
-                var parents = groupedFolders.Where(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(i.CollectionType))
+                var parents = groupedFolders.Where(i => i.CollectionType == viewType || i.CollectionType is null)
                     .ToList();
 
                 if (parents.Count > 0)
                 {
-                    var localizationKey = string.Equals(viewType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) ?
-                        "TvShows" :
-                        "Movies";
+                    var localizationKey = viewType == CollectionType.TvShows
+                        ? "TvShows"
+                        : "Movies";
 
                     list.Add(GetUserView(parents, viewType, localizationKey, string.Empty, user, query.PresetViews));
                 }
@@ -164,14 +163,14 @@ namespace Emby.Server.Implementations.Library
                 .ToArray();
         }
 
-        public UserView GetUserSubViewWithName(string name, Guid parentId, string type, string sortName)
+        public UserView GetUserSubViewWithName(string name, Guid parentId, CollectionType? type, string sortName)
         {
             var uniqueId = parentId + "subview" + type;
 
             return _libraryManager.GetNamedView(name, parentId, type, sortName, uniqueId);
         }
 
-        public UserView GetUserSubView(Guid parentId, string type, string localizationKey, string sortName)
+        public UserView GetUserSubView(Guid parentId, CollectionType? type, string localizationKey, string sortName)
         {
             var name = _localizationManager.GetLocalizedString(localizationKey);
 
@@ -180,15 +179,15 @@ namespace Emby.Server.Implementations.Library
 
         private Folder GetUserView(
             List<ICollectionFolder> parents,
-            string viewType,
+            CollectionType? viewType,
             string localizationKey,
             string sortName,
             User user,
-            string[] presetViews)
+            CollectionType?[] presetViews)
         {
-            if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
+            if (parents.Count == 1 && parents.All(i => i.CollectionType == viewType))
             {
-                if (!presetViews.Contains(viewType, StringComparison.OrdinalIgnoreCase))
+                if (!presetViews.Contains(viewType))
                 {
                     return (Folder)parents[0];
                 }
@@ -200,7 +199,7 @@ namespace Emby.Server.Implementations.Library
             return _libraryManager.GetNamedView(user, name, viewType, sortName);
         }
 
-        public UserView GetUserView(Folder parent, string viewType, string sortName)
+        public UserView GetUserView(Folder parent, CollectionType? viewType, string sortName)
         {
             return _libraryManager.GetShadowView(parent, viewType, sortName);
         }
@@ -280,7 +279,7 @@ namespace Emby.Server.Implementations.Library
 
             var isPlayed = request.IsPlayed;
 
-            if (parents.OfType<ICollectionFolder>().Any(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)))
+            if (parents.OfType<ICollectionFolder>().Any(i => i.CollectionType == CollectionType.Music))
             {
                 isPlayed = null;
             }
@@ -306,11 +305,11 @@ namespace Emby.Server.Implementations.Library
                 var hasCollectionType = parents.OfType<UserView>().ToArray();
                 if (hasCollectionType.Length > 0)
                 {
-                    if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)))
+                    if (hasCollectionType.All(i => i.CollectionType == CollectionType.Movies))
                     {
                         includeItemTypes = new[] { BaseItemKind.Movie };
                     }
-                    else if (hasCollectionType.All(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)))
+                    else if (hasCollectionType.All(i => i.CollectionType == CollectionType.TvShows))
                     {
                         includeItemTypes = new[] { BaseItemKind.Episode };
                     }

+ 1 - 1
Emby.Server.Implementations/Playlists/PlaylistsFolder.cs

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Playlists
         public override bool SupportsInheritedParentImages => false;
 
         [JsonIgnore]
-        public override string CollectionType => MediaBrowser.Model.Entities.CollectionType.Playlists;
+        public override CollectionType? CollectionType => Jellyfin.Data.Enums.CollectionType.Playlists;
 
         protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
         {

+ 2 - 2
Jellyfin.Api/Controllers/GenresController.cs

@@ -131,8 +131,8 @@ public class GenresController : BaseJellyfinApiController
 
         QueryResult<(BaseItem, ItemCounts)> result;
         if (parentItem is ICollectionFolder parentCollectionFolder
-            && (string.Equals(parentCollectionFolder.CollectionType, CollectionType.Music, StringComparison.Ordinal)
-                || string.Equals(parentCollectionFolder.CollectionType, CollectionType.MusicVideos, StringComparison.Ordinal)))
+            && (parentCollectionFolder.CollectionType == CollectionType.Music
+                || parentCollectionFolder.CollectionType == CollectionType.MusicVideos))
         {
             result = _libraryManager.GetMusicGenres(query);
         }

+ 4 - 5
Jellyfin.Api/Controllers/ItemUpdateController.cs

@@ -5,6 +5,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Constants;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -164,18 +165,16 @@ public class ItemUpdateController : BaseJellyfinApiController
             var inheritedContentType = _libraryManager.GetInheritedContentType(item);
             var configuredContentType = _libraryManager.GetConfiguredContentType(item);
 
-            if (string.IsNullOrWhiteSpace(inheritedContentType) ||
-                !string.IsNullOrWhiteSpace(configuredContentType))
+            if (inheritedContentType is null || configuredContentType is not null)
             {
                 info.ContentTypeOptions = GetContentTypeOptions(true).ToArray();
                 info.ContentType = configuredContentType;
 
-                if (string.IsNullOrWhiteSpace(inheritedContentType)
-                    || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+                if (inheritedContentType is null || inheritedContentType == CollectionType.TvShows)
                 {
                     info.ContentTypeOptions = info.ContentTypeOptions
                         .Where(i => string.IsNullOrWhiteSpace(i.Value)
-                                    || string.Equals(i.Value, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+                                    || string.Equals(i.Value, "TvShows", StringComparison.OrdinalIgnoreCase))
                         .ToArray();
                 }
             }

+ 2 - 2
Jellyfin.Api/Controllers/ItemsController.cs

@@ -269,13 +269,13 @@ public class ItemsController : BaseJellyfinApiController
             folder = _libraryManager.GetUserRootFolder();
         }
 
-        string? collectionType = null;
+        CollectionType? collectionType = null;
         if (folder is IHasCollectionType hasCollectionType)
         {
             collectionType = hasCollectionType.CollectionType;
         }
 
-        if (string.Equals(collectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+        if (collectionType == CollectionType.Playlists)
         {
             recursive = true;
             includeItemTypes = new[] { BaseItemKind.Playlist };

+ 2 - 2
Jellyfin.Api/Controllers/LibraryController.cs

@@ -788,7 +788,7 @@ public class LibraryController : BaseJellyfinApiController
     [Authorize(Policy = Policies.FirstTimeSetupOrDefault)]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public ActionResult<LibraryOptionsResultDto> GetLibraryOptionsInfo(
-        [FromQuery] string? libraryContentType,
+        [FromQuery] CollectionType? libraryContentType,
         [FromQuery] bool isNewLibrary = false)
     {
         var result = new LibraryOptionsResultDto();
@@ -922,7 +922,7 @@ public class LibraryController : BaseJellyfinApiController
         }
     }
 
-    private static string[] GetRepresentativeItemTypes(string? contentType)
+    private static string[] GetRepresentativeItemTypes(CollectionType? contentType)
     {
         return contentType switch
         {

+ 2 - 1
Jellyfin.Api/Controllers/UserViewsController.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.ModelBinders;
 using Jellyfin.Api.Models.UserViewDtos;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -63,7 +64,7 @@ public class UserViewsController : BaseJellyfinApiController
     public QueryResult<BaseItemDto> GetUserViews(
         [FromRoute, Required] Guid userId,
         [FromQuery] bool? includeExternalContent,
-        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] presetViews,
+        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] CollectionType?[] presetViews,
         [FromQuery] bool includeHidden = false)
     {
         var query = new UserViewQuery

+ 11 - 0
Jellyfin.Data/Attributes/OpenApiIgnoreEnumAttribute.cs

@@ -0,0 +1,11 @@
+using System;
+
+namespace Jellyfin.Data.Attributes;
+
+/// <summary>
+/// Attribute to specify that the enum value is to be ignored when generating the openapi spec.
+/// </summary>
+[AttributeUsage(AttributeTargets.Field)]
+public sealed class OpenApiIgnoreEnumAttribute : Attribute
+{
+}

+ 164 - 0
Jellyfin.Data/Enums/CollectionType.cs

@@ -0,0 +1,164 @@
+using Jellyfin.Data.Attributes;
+
+namespace Jellyfin.Data.Enums;
+
+/// <summary>
+/// Collection type.
+/// </summary>
+public enum CollectionType
+{
+    /// <summary>
+    /// Unknown collection.
+    /// </summary>
+    Unknown = 0,
+
+    /// <summary>
+    /// Movies collection.
+    /// </summary>
+    Movies = 1,
+
+    /// <summary>
+    /// Tv shows collection.
+    /// </summary>
+    TvShows = 2,
+
+    /// <summary>
+    /// Music collection.
+    /// </summary>
+    Music = 3,
+
+    /// <summary>
+    /// Music videos collection.
+    /// </summary>
+    MusicVideos = 4,
+
+    /// <summary>
+    /// Trailers collection.
+    /// </summary>
+    Trailers = 5,
+
+    /// <summary>
+    /// Home videos collection.
+    /// </summary>
+    HomeVideos = 6,
+
+    /// <summary>
+    /// Box sets collection.
+    /// </summary>
+    BoxSets = 7,
+
+    /// <summary>
+    /// Books collection.
+    /// </summary>
+    Books = 8,
+
+    /// <summary>
+    /// Photos collection.
+    /// </summary>
+    Photos = 9,
+
+    /// <summary>
+    /// Live tv collection.
+    /// </summary>
+    LiveTv = 10,
+
+    /// <summary>
+    /// Playlists collection.
+    /// </summary>
+    Playlists = 11,
+
+    /// <summary>
+    /// Folders collection.
+    /// </summary>
+    Folders = 12,
+
+    /// <summary>
+    /// Tv show series collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvShowSeries = 101,
+
+    /// <summary>
+    /// Tv genres collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvGenres = 102,
+
+    /// <summary>
+    /// Tv genre collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvGenre = 103,
+
+    /// <summary>
+    /// Tv latest collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvLatest = 104,
+
+    /// <summary>
+    /// Tv next up collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvNextUp = 105,
+
+    /// <summary>
+    /// Tv resume collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvResume = 106,
+
+    /// <summary>
+    /// Tv favorite series collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvFavoriteSeries = 107,
+
+    /// <summary>
+    /// Tv favorite episodes collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    TvFavoriteEpisodes = 108,
+
+    /// <summary>
+    /// Latest movies collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    MovieLatest = 109,
+
+    /// <summary>
+    /// Movies to resume collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    MovieResume = 110,
+
+    /// <summary>
+    /// Movie movie collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    MovieMovies = 111,
+
+    /// <summary>
+    /// Movie collections collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    MovieCollections = 112,
+
+    /// <summary>
+    /// Movie favorites collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    MovieFavorites = 113,
+
+    /// <summary>
+    /// Movie genres collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    MovieGenres = 114,
+
+    /// <summary>
+    /// Movie genre collection.
+    /// </summary>
+    [OpenApiIgnoreEnum]
+    MovieGenre = 115
+}

+ 1 - 0
Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs

@@ -246,6 +246,7 @@ namespace Jellyfin.Server.Extensions
                 // TODO - remove when all types are supported in System.Text.Json
                 c.AddSwaggerTypeMappings();
 
+                c.SchemaFilter<IgnoreEnumSchemaFilter>();
                 c.OperationFilter<SecurityRequirementsOperationFilter>();
                 c.OperationFilter<FileResponseFilter>();
                 c.OperationFilter<FileRequestFilter>();

+ 42 - 0
Jellyfin.Server/Filters/IgnoreEnumSchemaFilter.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Jellyfin.Data.Attributes;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace Jellyfin.Server.Filters;
+
+/// <summary>
+/// Filter to remove ignored enum values.
+/// </summary>
+public class IgnoreEnumSchemaFilter : ISchemaFilter
+{
+    /// <inheritdoc />
+    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
+    {
+        if (context.Type.IsEnum || (Nullable.GetUnderlyingType(context.Type)?.IsEnum ?? false))
+        {
+            var type = context.Type.IsEnum ? context.Type : Nullable.GetUnderlyingType(context.Type);
+            if (type is null)
+            {
+                return;
+            }
+
+            var enumOpenApiStrings = new List<IOpenApiAny>();
+
+            foreach (var enumName in Enum.GetNames(type))
+            {
+                var member = type.GetMember(enumName)[0];
+                if (!member.GetCustomAttributes<OpenApiIgnoreEnumAttribute>().Any())
+                {
+                    enumOpenApiStrings.Add(new OpenApiString(enumName));
+                }
+            }
+
+            schema.Enum = enumOpenApiStrings;
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -724,7 +724,7 @@ namespace MediaBrowser.Controller.Entities
 
                 if (this is IHasCollectionType view)
                 {
-                    if (string.Equals(view.CollectionType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+                    if (view.CollectionType == CollectionType.LiveTv)
                     {
                         return true;
                     }

+ 2 - 1
MediaBrowser.Controller/Entities/BasePluginFolder.cs

@@ -1,6 +1,7 @@
 #pragma warning disable CS1591
 
 using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -11,7 +12,7 @@ namespace MediaBrowser.Controller.Entities
     public abstract class BasePluginFolder : Folder, ICollectionFolder
     {
         [JsonIgnore]
-        public virtual string? CollectionType => null;
+        public virtual CollectionType? CollectionType => null;
 
         [JsonIgnore]
         public override bool SupportsInheritedParentImages => false;

+ 2 - 1
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -11,6 +11,7 @@ using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Threading;
 using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
 using Jellyfin.Extensions.Json;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
@@ -69,7 +70,7 @@ namespace MediaBrowser.Controller.Entities
         [JsonIgnore]
         public override bool SupportsInheritedParentImages => false;
 
-        public string CollectionType { get; set; }
+        public CollectionType? CollectionType { get; set; }
 
         /// <summary>
         /// Gets the item's children.

+ 2 - 1
MediaBrowser.Controller/Entities/ICollectionFolder.cs

@@ -3,6 +3,7 @@
 #pragma warning disable CA1819, CS1591
 
 using System;
+using Jellyfin.Data.Enums;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -27,6 +28,6 @@ namespace MediaBrowser.Controller.Entities
 
     public interface IHasCollectionType
     {
-        string CollectionType { get; }
+        CollectionType? CollectionType { get; }
     }
 }

+ 2 - 2
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Entities
             OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
             PersonIds = Array.Empty<Guid>();
             PersonTypes = Array.Empty<string>();
-            PresetViews = Array.Empty<string>();
+            PresetViews = Array.Empty<CollectionType?>();
             SeriesStatuses = Array.Empty<SeriesStatus>();
             SourceTypes = Array.Empty<SourceType>();
             StudioIds = Array.Empty<Guid>();
@@ -248,7 +248,7 @@ namespace MediaBrowser.Controller.Entities
 
         public Guid[] TopParentIds { get; set; }
 
-        public string[] PresetViews { get; set; }
+        public CollectionType?[] PresetViews { get; set; }
 
         public TrailerType[] TrailerTypes { get; set; }
 

+ 19 - 18
MediaBrowser.Controller/Entities/UserView.cs

@@ -8,6 +8,7 @@ using System.Linq;
 using System.Text.Json.Serialization;
 using System.Threading.Tasks;
 using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.TV;
 using MediaBrowser.Model.Querying;
@@ -16,21 +17,21 @@ namespace MediaBrowser.Controller.Entities
 {
     public class UserView : Folder, IHasCollectionType
     {
-        private static readonly string[] _viewTypesEligibleForGrouping = new string[]
+        private static readonly CollectionType?[] _viewTypesEligibleForGrouping =
         {
-            Model.Entities.CollectionType.Movies,
-            Model.Entities.CollectionType.TvShows,
-            string.Empty
+            Jellyfin.Data.Enums.CollectionType.Movies,
+            Jellyfin.Data.Enums.CollectionType.TvShows,
+            null
         };
 
-        private static readonly string[] _originalFolderViewTypes = new string[]
+        private static readonly CollectionType?[] _originalFolderViewTypes =
         {
-            Model.Entities.CollectionType.Books,
-            Model.Entities.CollectionType.MusicVideos,
-            Model.Entities.CollectionType.HomeVideos,
-            Model.Entities.CollectionType.Photos,
-            Model.Entities.CollectionType.Music,
-            Model.Entities.CollectionType.BoxSets
+            Jellyfin.Data.Enums.CollectionType.Books,
+            Jellyfin.Data.Enums.CollectionType.MusicVideos,
+            Jellyfin.Data.Enums.CollectionType.HomeVideos,
+            Jellyfin.Data.Enums.CollectionType.Photos,
+            Jellyfin.Data.Enums.CollectionType.Music,
+            Jellyfin.Data.Enums.CollectionType.BoxSets
         };
 
         public static ITVSeriesManager TVSeriesManager { get; set; }
@@ -38,7 +39,7 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// Gets or sets the view type.
         /// </summary>
-        public string ViewType { get; set; }
+        public CollectionType? ViewType { get; set; }
 
         /// <summary>
         /// Gets or sets the display parent id.
@@ -52,7 +53,7 @@ namespace MediaBrowser.Controller.Entities
 
         /// <inheritdoc />
         [JsonIgnore]
-        public string CollectionType => ViewType;
+        public CollectionType? CollectionType => ViewType;
 
         /// <inheritdoc />
         [JsonIgnore]
@@ -160,7 +161,7 @@ namespace MediaBrowser.Controller.Entities
                 return true;
             }
 
-            return string.Equals(Model.Entities.CollectionType.Playlists, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase);
+            return collectionFolder.CollectionType == Jellyfin.Data.Enums.CollectionType.Playlists;
         }
 
         public static bool IsEligibleForGrouping(Folder folder)
@@ -169,14 +170,14 @@ namespace MediaBrowser.Controller.Entities
                     && IsEligibleForGrouping(collectionFolder.CollectionType);
         }
 
-        public static bool IsEligibleForGrouping(string viewType)
+        public static bool IsEligibleForGrouping(CollectionType? viewType)
         {
-            return _viewTypesEligibleForGrouping.Contains(viewType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
+            return _viewTypesEligibleForGrouping.Contains(viewType);
         }
 
-        public static bool EnableOriginalFolder(string viewType)
+        public static bool EnableOriginalFolder(CollectionType? viewType)
         {
-            return _originalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
+            return _originalFolderViewTypes.Contains(viewType);
         }
 
         protected override Task ValidateChildrenInternal(IProgress<double> progress, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService, System.Threading.CancellationToken cancellationToken)

+ 39 - 39
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Entities
             _tvSeriesManager = tvSeriesManager;
         }
 
-        public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, string viewType, InternalItemsQuery query)
+        public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, CollectionType? viewType, InternalItemsQuery query)
         {
             var user = query.User;
 
@@ -67,49 +67,49 @@ namespace MediaBrowser.Controller.Entities
                 case CollectionType.Movies:
                     return GetMovieFolders(queryParent, user, query);
 
-                case SpecialFolder.TvShowSeries:
+                case CollectionType.TvShowSeries:
                     return GetTvSeries(queryParent, user, query);
 
-                case SpecialFolder.TvGenres:
+                case CollectionType.TvGenres:
                     return GetTvGenres(queryParent, user, query);
 
-                case SpecialFolder.TvGenre:
+                case CollectionType.TvGenre:
                     return GetTvGenreItems(queryParent, displayParent, user, query);
 
-                case SpecialFolder.TvResume:
+                case CollectionType.TvResume:
                     return GetTvResume(queryParent, user, query);
 
-                case SpecialFolder.TvNextUp:
+                case CollectionType.TvNextUp:
                     return GetTvNextUp(queryParent, query);
 
-                case SpecialFolder.TvLatest:
+                case CollectionType.TvLatest:
                     return GetTvLatest(queryParent, user, query);
 
-                case SpecialFolder.MovieFavorites:
+                case CollectionType.MovieFavorites:
                     return GetFavoriteMovies(queryParent, user, query);
 
-                case SpecialFolder.MovieLatest:
+                case CollectionType.MovieLatest:
                     return GetMovieLatest(queryParent, user, query);
 
-                case SpecialFolder.MovieGenres:
+                case CollectionType.MovieGenres:
                     return GetMovieGenres(queryParent, user, query);
 
-                case SpecialFolder.MovieGenre:
+                case CollectionType.MovieGenre:
                     return GetMovieGenreItems(queryParent, displayParent, user, query);
 
-                case SpecialFolder.MovieResume:
+                case CollectionType.MovieResume:
                     return GetMovieResume(queryParent, user, query);
 
-                case SpecialFolder.MovieMovies:
+                case CollectionType.MovieMovies:
                     return GetMovieMovies(queryParent, user, query);
 
-                case SpecialFolder.MovieCollections:
+                case CollectionType.MovieCollections:
                     return GetMovieCollections(user, query);
 
-                case SpecialFolder.TvFavoriteEpisodes:
+                case CollectionType.TvFavoriteEpisodes:
                     return GetFavoriteEpisodes(queryParent, user, query);
 
-                case SpecialFolder.TvFavoriteSeries:
+                case CollectionType.TvFavoriteSeries:
                     return GetFavoriteSeries(queryParent, user, query);
 
                 default:
@@ -146,12 +146,12 @@ namespace MediaBrowser.Controller.Entities
 
             var list = new List<BaseItem>
             {
-                GetUserView(SpecialFolder.MovieResume, "HeaderContinueWatching", "0", parent),
-                GetUserView(SpecialFolder.MovieLatest, "Latest", "1", parent),
-                GetUserView(SpecialFolder.MovieMovies, "Movies", "2", parent),
-                GetUserView(SpecialFolder.MovieCollections, "Collections", "3", parent),
-                GetUserView(SpecialFolder.MovieFavorites, "Favorites", "4", parent),
-                GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent)
+                GetUserView(CollectionType.MovieResume, "HeaderContinueWatching", "0", parent),
+                GetUserView(CollectionType.MovieLatest, "Latest", "1", parent),
+                GetUserView(CollectionType.MovieMovies, "Movies", "2", parent),
+                GetUserView(CollectionType.MovieCollections, "Collections", "3", parent),
+                GetUserView(CollectionType.MovieFavorites, "Favorites", "4", parent),
+                GetUserView(CollectionType.MovieGenres, "Genres", "5", parent)
             };
 
             return GetResult(list, query);
@@ -264,7 +264,7 @@ namespace MediaBrowser.Controller.Entities
                     }
                 })
                 .Where(i => i is not null)
-                .Select(i => GetUserViewWithName(SpecialFolder.MovieGenre, i.SortName, parent));
+                .Select(i => GetUserViewWithName(CollectionType.MovieGenre, i.SortName, parent));
 
             return GetResult(genres, query);
         }
@@ -303,13 +303,13 @@ namespace MediaBrowser.Controller.Entities
 
             var list = new List<BaseItem>
             {
-                GetUserView(SpecialFolder.TvResume, "HeaderContinueWatching", "0", parent),
-                GetUserView(SpecialFolder.TvNextUp, "HeaderNextUp", "1", parent),
-                GetUserView(SpecialFolder.TvLatest, "Latest", "2", parent),
-                GetUserView(SpecialFolder.TvShowSeries, "Shows", "3", parent),
-                GetUserView(SpecialFolder.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent),
-                GetUserView(SpecialFolder.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent),
-                GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent)
+                GetUserView(CollectionType.TvResume, "HeaderContinueWatching", "0", parent),
+                GetUserView(CollectionType.TvNextUp, "HeaderNextUp", "1", parent),
+                GetUserView(CollectionType.TvLatest, "Latest", "2", parent),
+                GetUserView(CollectionType.TvShowSeries, "Shows", "3", parent),
+                GetUserView(CollectionType.TvFavoriteSeries, "HeaderFavoriteShows", "4", parent),
+                GetUserView(CollectionType.TvFavoriteEpisodes, "HeaderFavoriteEpisodes", "5", parent),
+                GetUserView(CollectionType.TvGenres, "Genres", "6", parent)
             };
 
             return GetResult(list, query);
@@ -330,7 +330,7 @@ namespace MediaBrowser.Controller.Entities
 
         private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query)
         {
-            var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty });
+            var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows });
 
             var result = _tvSeriesManager.GetNextUp(
                 new NextUpQuery
@@ -392,7 +392,7 @@ namespace MediaBrowser.Controller.Entities
                     }
                 })
                 .Where(i => i is not null)
-                .Select(i => GetUserViewWithName(SpecialFolder.TvGenre, i.SortName, parent));
+                .Select(i => GetUserViewWithName(CollectionType.TvGenre, i.SortName, parent));
 
             return GetResult(genres, query);
         }
@@ -943,7 +943,7 @@ namespace MediaBrowser.Controller.Entities
                 .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
         }
 
-        private BaseItem[] GetMediaFolders(User user, IEnumerable<string> viewTypes)
+        private BaseItem[] GetMediaFolders(User user, IEnumerable<CollectionType> viewTypes)
         {
             if (user is null)
             {
@@ -952,7 +952,7 @@ namespace MediaBrowser.Controller.Entities
                     {
                         var folder = i as ICollectionFolder;
 
-                        return folder is not null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
+                        return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
                     }).ToArray();
             }
 
@@ -961,11 +961,11 @@ namespace MediaBrowser.Controller.Entities
                 {
                     var folder = i as ICollectionFolder;
 
-                    return folder is not null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
+                    return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
                 }).ToArray();
         }
 
-        private BaseItem[] GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
+        private BaseItem[] GetMediaFolders(Folder parent, User user, IEnumerable<CollectionType> viewTypes)
         {
             if (parent is null || parent is UserView)
             {
@@ -975,12 +975,12 @@ namespace MediaBrowser.Controller.Entities
             return new BaseItem[] { parent };
         }
 
-        private UserView GetUserViewWithName(string type, string sortName, BaseItem parent)
+        private UserView GetUserViewWithName(CollectionType? type, string sortName, BaseItem parent)
         {
-            return _userViewManager.GetUserSubView(parent.Id, parent.Id.ToString("N", CultureInfo.InvariantCulture), type, sortName);
+            return _userViewManager.GetUserSubView(parent.Id, type, parent.Id.ToString("N", CultureInfo.InvariantCulture), sortName);
         }
 
-        private UserView GetUserView(string type, string localizationKey, string sortName, BaseItem parent)
+        private UserView GetUserView(CollectionType? type, string localizationKey, string sortName, BaseItem parent)
         {
             return _userViewManager.GetUserSubView(parent.Id, type, localizationKey, sortName);
         }

+ 10 - 10
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -79,7 +79,7 @@ namespace MediaBrowser.Controller.Library
             IDirectoryService directoryService,
             Folder parent,
             LibraryOptions libraryOptions,
-            string collectionType = null);
+            CollectionType? collectionType = null);
 
         /// <summary>
         /// Gets a Person.
@@ -256,28 +256,28 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>System.String.</returns>
-        string GetContentType(BaseItem item);
+        CollectionType? GetContentType(BaseItem item);
 
         /// <summary>
         /// Gets the type of the inherited content.
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>System.String.</returns>
-        string GetInheritedContentType(BaseItem item);
+        CollectionType? GetInheritedContentType(BaseItem item);
 
         /// <summary>
         /// Gets the type of the configured content.
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>System.String.</returns>
-        string GetConfiguredContentType(BaseItem item);
+        CollectionType? GetConfiguredContentType(BaseItem item);
 
         /// <summary>
         /// Gets the type of the configured content.
         /// </summary>
         /// <param name="path">The path.</param>
         /// <returns>System.String.</returns>
-        string GetConfiguredContentType(string path);
+        CollectionType? GetConfiguredContentType(string path);
 
         /// <summary>
         /// Normalizes the root path list.
@@ -329,7 +329,7 @@ namespace MediaBrowser.Controller.Library
             User user,
             string name,
             Guid parentId,
-            string viewType,
+            CollectionType? viewType,
             string sortName);
 
         /// <summary>
@@ -343,7 +343,7 @@ namespace MediaBrowser.Controller.Library
         UserView GetNamedView(
             User user,
             string name,
-            string viewType,
+            CollectionType? viewType,
             string sortName);
 
         /// <summary>
@@ -355,7 +355,7 @@ namespace MediaBrowser.Controller.Library
         /// <returns>The named view.</returns>
         UserView GetNamedView(
             string name,
-            string viewType,
+            CollectionType viewType,
             string sortName);
 
         /// <summary>
@@ -370,7 +370,7 @@ namespace MediaBrowser.Controller.Library
         UserView GetNamedView(
             string name,
             Guid parentId,
-            string viewType,
+            CollectionType? viewType,
             string sortName,
             string uniqueId);
 
@@ -383,7 +383,7 @@ namespace MediaBrowser.Controller.Library
         /// <returns>The shadow view.</returns>
         UserView GetShadowView(
             BaseItem parent,
-            string viewType,
+            CollectionType? viewType,
             string sortName);
 
         /// <summary>

+ 2 - 1
MediaBrowser.Controller/Library/IUserViewManager.cs

@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Library;
@@ -28,7 +29,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="localizationKey">Localization key to use.</param>
         /// <param name="sortName">Sort to use.</param>
         /// <returns>User view.</returns>
-        UserView GetUserSubView(Guid parentId, string type, string localizationKey, string sortName);
+        UserView GetUserSubView(Guid parentId, CollectionType? type, string localizationKey, string sortName);
 
         /// <summary>
         /// Gets latest items.

+ 4 - 3
MediaBrowser.Controller/Library/ItemResolveArgs.cs

@@ -5,6 +5,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.IO;
@@ -120,7 +121,7 @@ namespace MediaBrowser.Controller.Library
             }
         }
 
-        public string CollectionType { get; set; }
+        public CollectionType? CollectionType { get; set; }
 
         public bool HasParent<T>()
             where T : Folder
@@ -220,7 +221,7 @@ namespace MediaBrowser.Controller.Library
             return GetFileSystemEntryByName(name) is not null;
         }
 
-        public string GetCollectionType()
+        public CollectionType? GetCollectionType()
         {
             return CollectionType;
         }
@@ -229,7 +230,7 @@ namespace MediaBrowser.Controller.Library
         /// Gets the configured content type for the path.
         /// </summary>
         /// <returns>The configured content type.</returns>
-        public string GetConfiguredContentType()
+        public CollectionType? GetConfiguredContentType()
         {
             return _libraryManager.GetConfiguredContentType(Path);
         }

+ 2 - 1
MediaBrowser.Controller/Resolvers/IItemResolver.cs

@@ -1,6 +1,7 @@
 #pragma warning disable CS1591
 
 using System.Collections.Generic;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
@@ -32,7 +33,7 @@ namespace MediaBrowser.Controller.Resolvers
         MultiItemResolverResult ResolveMultiple(
             Folder parent,
             List<FileSystemMetadata> files,
-            string collectionType,
+            CollectionType? collectionType,
             IDirectoryService directoryService);
     }
 

+ 1 - 1
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -420,7 +420,7 @@ namespace MediaBrowser.Model.Dto
         /// Gets or sets the type of the collection.
         /// </summary>
         /// <value>The type of the collection.</value>
-        public string CollectionType { get; set; }
+        public CollectionType? CollectionType { get; set; }
 
         /// <summary>
         /// Gets or sets the display order.

+ 2 - 1
MediaBrowser.Model/Dto/MetadataEditorInfo.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Providers;
@@ -27,7 +28,7 @@ namespace MediaBrowser.Model.Dto
 
         public IReadOnlyList<ExternalIdInfo> ExternalIdInfos { get; set; }
 
-        public string? ContentType { get; set; }
+        public CollectionType? ContentType { get; set; }
 
         public IReadOnlyList<NameValuePair> ContentTypeOptions { get; set; }
     }

+ 0 - 27
MediaBrowser.Model/Entities/CollectionType.cs

@@ -1,27 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Entities
-{
-    public static class CollectionType
-    {
-        public const string Movies = "movies";
-
-        public const string TvShows = "tvshows";
-
-        public const string Music = "music";
-
-        public const string MusicVideos = "musicvideos";
-
-        public const string Trailers = "trailers";
-
-        public const string HomeVideos = "homevideos";
-
-        public const string BoxSets = "boxsets";
-
-        public const string Books = "books";
-        public const string Photos = "photos";
-        public const string LiveTv = "livetv";
-        public const string Playlists = "playlists";
-        public const string Folders = "folders";
-    }
-}

+ 3 - 2
MediaBrowser.Model/Library/UserViewQuery.cs

@@ -1,6 +1,7 @@
 #pragma warning disable CS1591
 
 using System;
+using Jellyfin.Data.Enums;
 
 namespace MediaBrowser.Model.Library
 {
@@ -9,7 +10,7 @@ namespace MediaBrowser.Model.Library
         public UserViewQuery()
         {
             IncludeExternalContent = true;
-            PresetViews = Array.Empty<string>();
+            PresetViews = Array.Empty<CollectionType?>();
         }
 
         /// <summary>
@@ -30,6 +31,6 @@ namespace MediaBrowser.Model.Library
         /// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value>
         public bool IncludeHidden { get; set; }
 
-        public string[] PresetViews { get; set; }
+        public CollectionType?[] PresetViews { get; set; }
     }
 }

+ 2 - 1
tests/Jellyfin.Server.Implementations.Tests/Library/AudioResolverTests.cs

@@ -1,6 +1,7 @@
 using System.Linq;
 using Emby.Naming.Common;
 using Emby.Server.Implementations.Library.Resolvers.Audio;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.IO;
@@ -62,7 +63,7 @@ public class AudioResolverTests
             null,
             Mock.Of<ILibraryManager>())
         {
-            CollectionType = "books",
+            CollectionType = CollectionType.Books,
             FileInfo = new FileSystemMetadata
             {
                 FullName = parent,

+ 1 - 0
tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs

@@ -1,5 +1,6 @@
 using Emby.Naming.Common;
 using Emby.Server.Implementations.Library.Resolvers.TV;
+using Jellyfin.Data.Enums;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;