瀏覽代碼

Merge remote-tracking branch 'upstream/master' into query-fields

crobibero 4 年之前
父節點
當前提交
b9406e5444

+ 3 - 2
Jellyfin.Api/Controllers/ArtistsController.cs

@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -99,7 +100,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,
@@ -308,7 +309,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,

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

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -100,7 +101,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,

+ 8 - 7
Jellyfin.Api/Controllers/InstantMixController.cs

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -71,7 +72,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var item = _libraryManager.GetItemById(id);
             var user = userId.HasValue && !userId.Equals(Guid.Empty)
@@ -108,7 +109,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var album = _libraryManager.GetItemById(id);
             var user = userId.HasValue && !userId.Equals(Guid.Empty)
@@ -145,7 +146,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var playlist = (Playlist)_libraryManager.GetItemById(id);
             var user = userId.HasValue && !userId.Equals(Guid.Empty)
@@ -182,7 +183,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var user = userId.HasValue && !userId.Equals(Guid.Empty)
                 ? _userManager.GetUserById(userId.Value)
@@ -218,7 +219,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var item = _libraryManager.GetItemById(id);
             var user = userId.HasValue && !userId.Equals(Guid.Empty)
@@ -255,7 +256,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var item = _libraryManager.GetItemById(id);
             var user = userId.HasValue && !userId.Equals(Guid.Empty)
@@ -292,7 +293,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var item = _libraryManager.GetItemById(id);
             var user = userId.HasValue && !userId.Equals(Guid.Empty)

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

@@ -185,7 +185,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] ItemFilter[] filters,
             [FromQuery] bool? isFavorite,
             [FromQuery] string? mediaTypes,
-            [FromQuery] string? imageTypes,
+            [FromQuery] ImageType[] imageTypes,
             [FromQuery] string? sortBy,
             [FromQuery] bool? isPlayed,
             [FromQuery] string? genres,
@@ -194,7 +194,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,
@@ -342,7 +342,7 @@ namespace Jellyfin.Api.Controllers
                     PersonIds = RequestHelpers.GetGuids(personIds),
                     PersonTypes = RequestHelpers.Split(personTypes, ',', true),
                     Years = RequestHelpers.Split(years, ',', true).Select(int.Parse).ToArray(),
-                    ImageTypes = RequestHelpers.Split(imageTypes, ',', true).Select(v => Enum.Parse<ImageType>(v, true)).ToArray(),
+                    ImageTypes = imageTypes,
                     VideoTypes = RequestHelpers.Split(videoTypes, ',', true).Select(v => Enum.Parse<VideoType>(v, true)).ToArray(),
                     AdjacentTo = adjacentTo,
                     ItemIds = RequestHelpers.GetGuids(ids),
@@ -536,7 +536,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? mediaTypes,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? excludeItemTypes,
             [FromQuery] string? includeItemTypes,
             [FromQuery] bool enableTotalRecordCount = true,

+ 6 - 5
Jellyfin.Api/Controllers/LiveTvController.cs

@@ -26,6 +26,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Querying;
@@ -145,7 +146,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? isDisliked,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] ItemFields[] fields,
             [FromQuery] bool? enableUserData,
             [FromQuery] string? sortBy,
@@ -262,7 +263,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? seriesTimerId,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] ItemFields[] fields,
             [FromQuery] bool? enableUserData,
             [FromQuery] bool? isMovie,
@@ -349,7 +350,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? seriesTimerId,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] ItemFields[] fields,
             [FromQuery] bool? enableUserData,
             [FromQuery] bool enableTotalRecordCount = true)
@@ -560,7 +561,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? genreIds,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] bool? enableUserData,
             [FromQuery] string? seriesTimerId,
             [FromQuery] Guid? librarySeriesId,
@@ -704,7 +705,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? isSports,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? genreIds,
             [FromQuery] ItemFields[] fields,
             [FromQuery] bool? enableUserData,

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

@@ -11,6 +11,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -99,7 +100,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,

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

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -99,7 +100,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,

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

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Playlists;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
@@ -151,7 +152,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? enableImages,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes)
+            [FromQuery] ImageType[] enableImageTypes)
         {
             var playlist = (Playlist)_libraryManager.GetItemById(playlistId);
             if (playlist == null)

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

@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -98,7 +99,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,

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

@@ -150,7 +150,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] ItemFilter[] filters,
             [FromQuery] bool? isFavorite,
             [FromQuery] string? mediaTypes,
-            [FromQuery] string? imageTypes,
+            [FromQuery] ImageType[] imageTypes,
             [FromQuery] string? sortBy,
             [FromQuery] bool? isPlayed,
             [FromQuery] string? genres,
@@ -159,7 +159,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? years,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] string? person,
             [FromQuery] string? personIds,
             [FromQuery] string? personTypes,

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

@@ -13,6 +13,7 @@ using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.TV;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -77,7 +78,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? parentId,
             [FromQuery] bool? enableImges,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] bool? enableUserData,
             [FromQuery] bool enableTotalRecordCount = true)
         {
@@ -134,7 +135,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? parentId,
             [FromQuery] bool? enableImges,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] bool? enableUserData)
         {
             var user = userId.HasValue && !userId.Equals(Guid.Empty)
@@ -206,7 +207,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] int? limit,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] bool? enableUserData,
             [FromQuery] string? sortBy)
         {
@@ -325,7 +326,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? adjacentTo,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] bool? enableUserData)
         {
             var user = userId.HasValue && !userId.Equals(Guid.Empty)

+ 1 - 1
Jellyfin.Api/Controllers/UserLibraryController.cs

@@ -272,7 +272,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] bool? isPlayed,
             [FromQuery] bool? enableImages,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] bool? enableUserData,
             [FromQuery] int limit = 20,
             [FromQuery] bool groupItems = true)

+ 1 - 1
Jellyfin.Api/Controllers/VideoHlsController.cs

@@ -371,7 +371,7 @@ namespace Jellyfin.Api.Controllers
 
             var baseUrlParam = string.Format(
                 CultureInfo.InvariantCulture,
-                "\"hls{0}\"",
+                "\"hls/{0}/\"",
                 Path.GetFileNameWithoutExtension(outputPath));
 
             return string.Format(

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

@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
@@ -77,7 +78,7 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? sortBy,
             [FromQuery] bool? enableUserData,
             [FromQuery] int? imageTypeLimit,
-            [FromQuery] string? enableImageTypes,
+            [FromQuery] ImageType[] enableImageTypes,
             [FromQuery] Guid? userId,
             [FromQuery] bool recursive = true,
             [FromQuery] bool? enableImages = true)

+ 3 - 5
Jellyfin.Api/Extensions/DtoExtensions.cs

@@ -106,7 +106,7 @@ namespace Jellyfin.Api.Extensions
             bool? enableImages,
             bool? enableUserData,
             int? imageTypeLimit,
-            string? enableImageTypes)
+            ImageType[] enableImageTypes)
         {
             dtoOptions.EnableImages = enableImages ?? true;
 
@@ -120,11 +120,9 @@ namespace Jellyfin.Api.Extensions
                 dtoOptions.EnableUserData = enableUserData.Value;
             }
 
-            if (!string.IsNullOrWhiteSpace(enableImageTypes))
+            if (enableImageTypes.Length != 0)
             {
-                dtoOptions.ImageTypes = enableImageTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
-                    .Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true))
-                    .ToArray();
+                dtoOptions.ImageTypes = enableImageTypes;
             }
 
             return dtoOptions;

+ 1 - 1
Jellyfin.Api/Helpers/DynamicHlsHelper.cs

@@ -155,7 +155,7 @@ namespace Jellyfin.Api.Helpers
                 return new FileContentResult(Array.Empty<byte>(), MimeTypes.GetMimeType("playlist.m3u8"));
             }
 
-            var totalBitrate = state.OutputAudioBitrate ?? 0 + state.OutputVideoBitrate ?? 0;
+            var totalBitrate = (state.OutputAudioBitrate ?? 0) + (state.OutputVideoBitrate ?? 0);
 
             var builder = new StringBuilder();
 

+ 27 - 0
Jellyfin.Api/Helpers/RequestHelpers.cs

@@ -4,6 +4,7 @@ using Jellyfin.Data.Enums;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Querying;
 using Microsoft.AspNetCore.Http;
 
 namespace Jellyfin.Api.Helpers
@@ -132,5 +133,31 @@ namespace Jellyfin.Api.Helpers
                 .Select(i => new Guid(i))
                 .ToArray();
         }
+
+        /// <summary>
+        /// Gets the item fields.
+        /// </summary>
+        /// <param name="fields">The fields string.</param>
+        /// <returns>IEnumerable{ItemFields}.</returns>
+        internal static ItemFields[] GetItemFields(string? fields)
+        {
+            if (string.IsNullOrEmpty(fields))
+            {
+                return Array.Empty<ItemFields>();
+            }
+
+            return Split(fields, ',', true)
+                .Select(v =>
+                {
+                    if (Enum.TryParse(v, true, out ItemFields value))
+                    {
+                        return (ItemFields?)value;
+                    }
+
+                    return null;
+                }).Where(i => i.HasValue)
+                .Select(i => i!.Value)
+                .ToArray();
+        }
     }
 }

+ 4 - 1
Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs

@@ -2,6 +2,7 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Text.Json.Serialization;
 using MediaBrowser.Common.Json.Converters;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 
 namespace Jellyfin.Api.Models.LiveTvDtos
@@ -141,7 +142,9 @@ namespace Jellyfin.Api.Models.LiveTvDtos
         /// Gets or sets the image types to include in the output.
         /// Optional.
         /// </summary>
-        public string? EnableImageTypes { get; set; }
+        [JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
+        [SuppressMessage("Microsoft.Performance", "CA1819:ReturnArrays", MessageId = "EnableImageTypes", Justification = "Imported from ServiceStack")]
+        public ImageType[] EnableImageTypes { get; set; } = Array.Empty<ImageType>();
 
         /// <summary>
         /// Gets or sets include user data.

+ 33 - 61
Jellyfin.Server.Implementations/Users/UserManager.cs

@@ -2,6 +2,7 @@
 #pragma warning disable CA1307
 
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
@@ -48,6 +49,8 @@ namespace Jellyfin.Server.Implementations.Users
         private readonly DefaultAuthenticationProvider _defaultAuthenticationProvider;
         private readonly DefaultPasswordResetProvider _defaultPasswordResetProvider;
 
+        private readonly IDictionary<Guid, User> _users;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="UserManager"/> class.
         /// </summary>
@@ -81,38 +84,28 @@ namespace Jellyfin.Server.Implementations.Users
             _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
             _defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
             _defaultPasswordResetProvider = _passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
+
+            _users = new ConcurrentDictionary<Guid, User>();
+            using var dbContext = _dbProvider.CreateContext();
+            foreach (var user in dbContext.Users
+                .Include(user => user.Permissions)
+                .Include(user => user.Preferences)
+                .Include(user => user.AccessSchedules)
+                .Include(user => user.ProfileImage)
+                .AsEnumerable())
+            {
+                _users.Add(user.Id, user);
+            }
         }
 
         /// <inheritdoc/>
         public event EventHandler<GenericEventArgs<User>>? OnUserUpdated;
 
         /// <inheritdoc/>
-        public IEnumerable<User> Users
-        {
-            get
-            {
-                using var dbContext = _dbProvider.CreateContext();
-                return dbContext.Users
-                    .Include(user => user.Permissions)
-                    .Include(user => user.Preferences)
-                    .Include(user => user.AccessSchedules)
-                    .Include(user => user.ProfileImage)
-                    .ToList();
-            }
-        }
+        public IEnumerable<User> Users => _users.Values;
 
         /// <inheritdoc/>
-        public IEnumerable<Guid> UsersIds
-        {
-            get
-            {
-                using var dbContext = _dbProvider.CreateContext();
-                return dbContext.Users
-                    .AsQueryable()
-                    .Select(user => user.Id)
-                    .ToList();
-            }
-        }
+        public IEnumerable<Guid> UsersIds => _users.Keys;
 
         /// <inheritdoc/>
         public User? GetUserById(Guid id)
@@ -122,13 +115,8 @@ namespace Jellyfin.Server.Implementations.Users
                 throw new ArgumentException("Guid can't be empty", nameof(id));
             }
 
-            using var dbContext = _dbProvider.CreateContext();
-            return dbContext.Users
-                .Include(user => user.Permissions)
-                .Include(user => user.Preferences)
-                .Include(user => user.AccessSchedules)
-                .Include(user => user.ProfileImage)
-                .FirstOrDefault(user => user.Id == id);
+            _users.TryGetValue(id, out var user);
+            return user;
         }
 
         /// <inheritdoc/>
@@ -139,14 +127,7 @@ namespace Jellyfin.Server.Implementations.Users
                 throw new ArgumentException("Invalid username", nameof(name));
             }
 
-            using var dbContext = _dbProvider.CreateContext();
-            return dbContext.Users
-                .Include(user => user.Permissions)
-                .Include(user => user.Preferences)
-                .Include(user => user.AccessSchedules)
-                .Include(user => user.ProfileImage)
-                .AsEnumerable()
-                .FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase));
+            return _users.Values.FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase));
         }
 
         /// <inheritdoc/>
@@ -205,13 +186,17 @@ namespace Jellyfin.Server.Implementations.Users
                 ? await dbContext.Users.AsQueryable().Select(u => u.InternalId).MaxAsync().ConfigureAwait(false)
                 : 0;
 
-            return new User(
+            var user = new User(
                 name,
                 _defaultAuthenticationProvider.GetType().FullName,
                 _defaultPasswordResetProvider.GetType().FullName)
             {
                 InternalId = max + 1
             };
+
+            _users.Add(user.Id, user);
+
+            return user;
         }
 
         /// <inheritdoc/>
@@ -237,28 +222,12 @@ namespace Jellyfin.Server.Implementations.Users
         /// <inheritdoc/>
         public void DeleteUser(Guid userId)
         {
-            using var dbContext = _dbProvider.CreateContext();
-            var user = dbContext.Users
-                .Include(u => u.Permissions)
-                .Include(u => u.Preferences)
-                .Include(u => u.AccessSchedules)
-                .Include(u => u.ProfileImage)
-                .FirstOrDefault(u => u.Id == userId);
-            if (user == null)
+            if (!_users.TryGetValue(userId, out var user))
             {
                 throw new ResourceNotFoundException(nameof(userId));
             }
 
-            if (dbContext.Users.Find(user.Id) == null)
-            {
-                throw new ArgumentException(string.Format(
-                    CultureInfo.InvariantCulture,
-                    "The user cannot be deleted because there is no user with the Name {0} and Id {1}.",
-                    user.Username,
-                    user.Id));
-            }
-
-            if (dbContext.Users.Count() == 1)
+            if (_users.Count == 1)
             {
                 throw new InvalidOperationException(string.Format(
                     CultureInfo.InvariantCulture,
@@ -277,6 +246,8 @@ namespace Jellyfin.Server.Implementations.Users
                     nameof(userId));
             }
 
+            using var dbContext = _dbProvider.CreateContext();
+
             // Clear all entities related to the user from the database.
             if (user.ProfileImage != null)
             {
@@ -288,6 +259,7 @@ namespace Jellyfin.Server.Implementations.Users
             dbContext.RemoveRange(user.AccessSchedules);
             dbContext.Users.Remove(user);
             dbContext.SaveChanges();
+            _users.Remove(userId);
 
             _eventManager.Publish(new UserDeletedEventArgs(user));
         }
@@ -589,9 +561,7 @@ namespace Jellyfin.Server.Implementations.Users
         public async Task InitializeAsync()
         {
             // TODO: Refactor the startup wizard so that it doesn't require a user to already exist.
-            await using var dbContext = _dbProvider.CreateContext();
-
-            if (await dbContext.Users.AsQueryable().AnyAsync().ConfigureAwait(false))
+            if (_users.Any())
             {
                 return;
             }
@@ -604,6 +574,7 @@ namespace Jellyfin.Server.Implementations.Users
 
             _logger.LogWarning("No users, creating one with username {UserName}", defaultName);
 
+            await using var dbContext = _dbProvider.CreateContext();
             var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false);
             newUser.SetPermission(PermissionKind.IsAdministrator, true);
             newUser.SetPermission(PermissionKind.EnableContentDeletion, true);
@@ -753,6 +724,7 @@ namespace Jellyfin.Server.Implementations.Users
             using var dbContext = _dbProvider.CreateContext();
             dbContext.Remove(user.ProfileImage);
             dbContext.SaveChanges();
+            user.ProfileImage = null;
         }
 
         private static bool IsValidUsername(string name)

+ 1 - 1
Jellyfin.Server/Jellyfin.Server.csproj

@@ -42,7 +42,7 @@
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.9" />
     <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.9" />
     <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="3.1.9" />
-    <PackageReference Include="prometheus-net" Version="3.6.0" />
+    <PackageReference Include="prometheus-net" Version="4.0.0" />
     <PackageReference Include="prometheus-net.AspNetCore" Version="3.6.0" />
     <PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
     <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />

+ 18 - 2
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -1380,24 +1380,40 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
         {
+            if (audioStream == null)
+            {
+                return null;
+            }
+
             if (request.AudioBitRate.HasValue)
             {
                 // Don't encode any higher than this
                 return Math.Min(384000, request.AudioBitRate.Value);
             }
 
-            return null;
+            // Empty bitrate area is not allow on iOS
+            // Default audio bitrate to 128K if it is not being requested
+            // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
+            return 128000;
         }
 
         public int? GetAudioBitrateParam(int? audioBitRate, MediaStream audioStream)
         {
+            if (audioStream == null)
+            {
+                return null;
+            }
+
             if (audioBitRate.HasValue)
             {
                 // Don't encode any higher than this
                 return Math.Min(384000, audioBitRate.Value);
             }
 
-            return null;
+            // Empty bitrate area is not allow on iOS
+            // Default audio bitrate to 128K if it is not being requested
+            // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options
+            return 128000;
         }
 
         public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions, bool isHls)

+ 1 - 1
tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj

@@ -22,7 +22,7 @@
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="coverlet.collector" Version="1.3.0" />
-    <PackageReference Include="Moq" Version="4.14.6" />
+    <PackageReference Include="Moq" Version="4.14.7" />
   </ItemGroup>
 
   <!-- Code Analyzers -->

+ 1 - 1
tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj

@@ -17,7 +17,7 @@
     <PackageReference Include="AutoFixture" Version="4.13.0" />
     <PackageReference Include="AutoFixture.AutoMoq" Version="4.13.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
-    <PackageReference Include="Moq" Version="4.14.6" />
+    <PackageReference Include="Moq" Version="4.14.7" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
     <PackageReference Include="coverlet.collector" Version="1.3.0" />