diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 4b108b89ea..2991861129 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -487,7 +487,7 @@ namespace Emby.Dlna.ContentDirectory
User = user,
Recursive = true,
IsMissing = false,
- ExcludeItemTypes = new[] { typeof(Book).Name },
+ ExcludeItemTypes = new[] { nameof(Book) },
IsFolder = isFolder,
MediaTypes = mediaTypes,
DtoOptions = GetDtoOptions()
@@ -556,7 +556,7 @@ namespace Emby.Dlna.ContentDirectory
Limit = limit,
StartIndex = startIndex,
IsVirtualItem = false,
- ExcludeItemTypes = new[] { typeof(Book).Name },
+ ExcludeItemTypes = new[] { nameof(Book) },
IsPlaceHolder = false,
DtoOptions = GetDtoOptions()
};
@@ -575,7 +575,7 @@ namespace Emby.Dlna.ContentDirectory
StartIndex = startIndex,
Limit = limit,
};
- query.IncludeItemTypes = new[] { typeof(LiveTvChannel).Name };
+ query.IncludeItemTypes = new[] { nameof(LiveTvChannel) };
SetSorting(query, sort, false);
@@ -910,7 +910,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(Series).Name };
+ query.IncludeItemTypes = new[] { nameof(Series) };
var result = _libraryManager.GetItemsResult(query);
@@ -923,7 +923,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
var result = _libraryManager.GetItemsResult(query);
@@ -936,7 +936,7 @@ namespace Emby.Dlna.ContentDirectory
// query.Parent = parent;
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
+ query.IncludeItemTypes = new[] { nameof(BoxSet) };
var result = _libraryManager.GetItemsResult(query);
@@ -949,7 +949,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
+ query.IncludeItemTypes = new[] { nameof(MusicAlbum) };
var result = _libraryManager.GetItemsResult(query);
@@ -962,7 +962,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(Audio).Name };
+ query.IncludeItemTypes = new[] { nameof(Audio) };
var result = _libraryManager.GetItemsResult(query);
@@ -975,7 +975,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Audio).Name };
+ query.IncludeItemTypes = new[] { nameof(Audio) };
var result = _libraryManager.GetItemsResult(query);
@@ -988,7 +988,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Series).Name };
+ query.IncludeItemTypes = new[] { nameof(Series) };
var result = _libraryManager.GetItemsResult(query);
@@ -1001,7 +1001,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
+ query.IncludeItemTypes = new[] { nameof(Episode) };
var result = _libraryManager.GetItemsResult(query);
@@ -1014,7 +1014,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
var result = _libraryManager.GetItemsResult(query);
@@ -1027,7 +1027,7 @@ namespace Emby.Dlna.ContentDirectory
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(MusicAlbum).Name };
+ query.IncludeItemTypes = new[] { nameof(MusicAlbum) };
var result = _libraryManager.GetItemsResult(query);
@@ -1181,7 +1181,7 @@ namespace Emby.Dlna.ContentDirectory
{
UserId = user.Id,
Limit = 50,
- IncludeItemTypes = new[] { typeof(Episode).Name },
+ IncludeItemTypes = new[] { nameof(Episode) },
ParentId = parent == null ? Guid.Empty : parent.Id,
GroupItems = false
},
@@ -1215,7 +1215,7 @@ namespace Emby.Dlna.ContentDirectory
Recursive = true,
ParentId = parentId,
ArtistIds = new[] { item.Id },
- IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
+ IncludeItemTypes = new[] { nameof(MusicAlbum) },
Limit = limit,
StartIndex = startIndex,
DtoOptions = GetDtoOptions()
@@ -1259,7 +1259,7 @@ namespace Emby.Dlna.ContentDirectory
Recursive = true,
ParentId = parentId,
GenreIds = new[] { item.Id },
- IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
+ IncludeItemTypes = new[] { nameof(MusicAlbum) },
Limit = limit,
StartIndex = startIndex,
DtoOptions = GetDtoOptions()
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index 2adc1d6c34..660bbb2deb 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.AppBase
}
///
- public string VirtualDataPath { get; } = "%AppDataPath%";
+ public string VirtualDataPath => "%AppDataPath%";
///
/// Gets the image cache path.
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 31ca738296..58edbe9878 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -339,7 +339,7 @@ namespace Emby.Server.Implementations
/// Gets the email address for use within a comment section of a user agent field.
/// Presently used to provide contact information to MusicBrainz service.
///
- public string ApplicationUserAgentAddress { get; } = "team@jellyfin.org";
+ public string ApplicationUserAgentAddress => "team@jellyfin.org";
///
/// Gets the current application name.
@@ -403,7 +403,7 @@ namespace Emby.Server.Implementations
///
/// Resolves this instance.
///
- /// The type
+ /// The type.
/// ``0.
public T Resolve() => ServiceProvider.GetService();
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index fb1bb65a09..db44bf4898 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -543,7 +543,7 @@ namespace Emby.Server.Implementations.Channels
return _libraryManager.GetItemIds(
new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(Channel).Name },
+ IncludeItemTypes = new[] { nameof(Channel) },
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
}).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
}
diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
index eeb49b8fef..2391eed428 100644
--- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.Channels
var uninstalledChannels = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(Channel).Name },
+ IncludeItemTypes = new[] { nameof(Channel) },
ExcludeItemIds = installedChannelIds.ToArray()
});
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 1c2aeda709..81e8e38b33 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -3914,7 +3914,7 @@ namespace Emby.Server.Implementations.Data
if (query.IsPlayed.HasValue)
{
// We should probably figure this out for all folders, but for right now, this is the only place where we need it
- if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], typeof(Series).Name, StringComparison.OrdinalIgnoreCase))
+ if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], nameof(Series), StringComparison.OrdinalIgnoreCase))
{
if (query.IsPlayed.Value)
{
@@ -4755,29 +4755,29 @@ namespace Emby.Server.Implementations.Data
{
var list = new List();
- if (IsTypeInQuery(typeof(Person).Name, query))
+ if (IsTypeInQuery(nameof(Person), query))
{
- list.Add(typeof(Person).Name);
+ list.Add(nameof(Person));
}
- if (IsTypeInQuery(typeof(Genre).Name, query))
+ if (IsTypeInQuery(nameof(Genre), query))
{
- list.Add(typeof(Genre).Name);
+ list.Add(nameof(Genre));
}
- if (IsTypeInQuery(typeof(MusicGenre).Name, query))
+ if (IsTypeInQuery(nameof(MusicGenre), query))
{
- list.Add(typeof(MusicGenre).Name);
+ list.Add(nameof(MusicGenre));
}
- if (IsTypeInQuery(typeof(MusicArtist).Name, query))
+ if (IsTypeInQuery(nameof(MusicArtist), query))
{
- list.Add(typeof(MusicArtist).Name);
+ list.Add(nameof(MusicArtist));
}
- if (IsTypeInQuery(typeof(Studio).Name, query))
+ if (IsTypeInQuery(nameof(Studio), query))
{
- list.Add(typeof(Studio).Name);
+ list.Add(nameof(Studio));
}
return list;
@@ -4832,12 +4832,12 @@ namespace Emby.Server.Implementations.Data
var types = new[]
{
- typeof(Episode).Name,
- typeof(Video).Name,
- typeof(Movie).Name,
- typeof(MusicVideo).Name,
- typeof(Series).Name,
- typeof(Season).Name
+ nameof(Episode),
+ nameof(Video),
+ nameof(Movie),
+ nameof(MusicVideo),
+ nameof(Series),
+ nameof(Season)
};
if (types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)))
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index edb8753fd4..73502c2c9f 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -465,7 +465,7 @@ namespace Emby.Server.Implementations.Dto
{
var parentAlbumIds = _libraryManager.GetItemIds(new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
+ IncludeItemTypes = new[] { nameof(MusicAlbum) },
Name = item.Album,
Limit = 1
});
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index 4b407dd9d2..8140fe81be 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -267,7 +267,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
if (param.Length == 2)
{
var value = NormalizeValue(param[1].Trim(new[] { '"' }));
- result.Add(param[0], value);
+ result[param[0]] = value;
}
}
diff --git a/Emby.Server.Implementations/Images/ArtistImageProvider.cs b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
index bf57382ed4..afa4ec7b1b 100644
--- a/Emby.Server.Implementations/Images/ArtistImageProvider.cs
+++ b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
@@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Images
// return _libraryManager.GetItemList(new InternalItemsQuery
// {
// ArtistIds = new[] { item.Id },
- // IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
+ // IncludeItemTypes = new[] { nameof(MusicAlbum) },
// OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) },
// Limit = 4,
// Recursive = true,
diff --git a/Emby.Server.Implementations/Images/GenreImageProvider.cs b/Emby.Server.Implementations/Images/GenreImageProvider.cs
index 1cd4cd66bd..3817882312 100644
--- a/Emby.Server.Implementations/Images/GenreImageProvider.cs
+++ b/Emby.Server.Implementations/Images/GenreImageProvider.cs
@@ -42,7 +42,12 @@ namespace Emby.Server.Implementations.Images
return _libraryManager.GetItemList(new InternalItemsQuery
{
Genres = new[] { item.Name },
- IncludeItemTypes = new[] { typeof(MusicAlbum).Name, typeof(MusicVideo).Name, typeof(Audio).Name },
+ IncludeItemTypes = new[]
+ {
+ nameof(MusicAlbum),
+ nameof(MusicVideo),
+ nameof(Audio)
+ },
OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) },
Limit = 4,
Recursive = true,
@@ -77,7 +82,7 @@ namespace Emby.Server.Implementations.Images
return _libraryManager.GetItemList(new InternalItemsQuery
{
Genres = new[] { item.Name },
- IncludeItemTypes = new[] { typeof(Series).Name, typeof(Movie).Name },
+ IncludeItemTypes = new[] { nameof(Series), nameof(Movie) },
OrderBy = new[] { (ItemSortBy.Random, SortOrder.Ascending) },
Limit = 4,
Recursive = true,
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 877fdec86e..658c53f288 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.Library
var genres = item
.GetRecursiveChildren(user, new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(Audio).Name },
+ IncludeItemTypes = new[] { nameof(Audio) },
DtoOptions = dtoOptions
})
.Cast
private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths;
+ private readonly IEventManager _eventManager;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerConfigurationManager _config;
@@ -65,23 +69,20 @@ namespace Emby.Server.Implementations.Updates
ILogger logger,
IApplicationHost appHost,
IApplicationPaths appPaths,
+ IEventManager eventManager,
IHttpClientFactory httpClientFactory,
IJsonSerializer jsonSerializer,
IServerConfigurationManager config,
IFileSystem fileSystem,
IZipClient zipClient)
{
- if (logger == null)
- {
- throw new ArgumentNullException(nameof(logger));
- }
-
_currentInstallations = new List<(InstallationInfo, CancellationTokenSource)>();
_completedInstallationsInternal = new ConcurrentBag();
_logger = logger;
_applicationHost = appHost;
_appPaths = appPaths;
+ _eventManager = eventManager;
_httpClientFactory = httpClientFactory;
_jsonSerializer = jsonSerializer;
_config = config;
@@ -89,27 +90,6 @@ namespace Emby.Server.Implementations.Updates
_zipClient = zipClient;
}
- ///
- public event EventHandler PackageInstalling;
-
- ///
- public event EventHandler PackageInstallationCompleted;
-
- ///
- public event EventHandler PackageInstallationFailed;
-
- ///
- public event EventHandler PackageInstallationCancelled;
-
- ///
- public event EventHandler PluginUninstalled;
-
- ///
- public event EventHandler PluginUpdated;
-
- ///
- public event EventHandler PluginInstalled;
-
///
public IEnumerable CompletedInstallations => _completedInstallationsInternal;
@@ -268,11 +248,11 @@ namespace Emby.Server.Implementations.Updates
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
- PackageInstalling?.Invoke(this, package);
+ await _eventManager.PublishAsync(new PluginInstallingEventArgs(package)).ConfigureAwait(false);
try
{
- await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
+ var isUpdate = await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
lock (_currentInstallationsLock)
{
@@ -280,8 +260,11 @@ namespace Emby.Server.Implementations.Updates
}
_completedInstallationsInternal.Add(package);
+ await _eventManager.PublishAsync(isUpdate
+ ? (GenericEventArgs)new PluginUpdatedEventArgs(package)
+ : new PluginInstalledEventArgs(package)).ConfigureAwait(false);
- PackageInstallationCompleted?.Invoke(this, package);
+ _applicationHost.NotifyPendingRestart();
}
catch (OperationCanceledException)
{
@@ -292,7 +275,7 @@ namespace Emby.Server.Implementations.Updates
_logger.LogInformation("Package installation cancelled: {0} {1}", package.Name, package.Version);
- PackageInstallationCancelled?.Invoke(this, package);
+ await _eventManager.PublishAsync(new PluginInstallationCancelledEventArgs(package)).ConfigureAwait(false);
throw;
}
@@ -305,11 +288,11 @@ namespace Emby.Server.Implementations.Updates
_currentInstallations.Remove(tuple);
}
- PackageInstallationFailed?.Invoke(this, new InstallationFailedEventArgs
+ await _eventManager.PublishAsync(new InstallationFailedEventArgs
{
InstallationInfo = package,
Exception = ex
- });
+ }).ConfigureAwait(false);
throw;
}
@@ -326,7 +309,7 @@ namespace Emby.Server.Implementations.Updates
/// The package.
/// The cancellation token.
/// .
- private async Task InstallPackageInternal(InstallationInfo package, CancellationToken cancellationToken)
+ private async Task InstallPackageInternal(InstallationInfo package, CancellationToken cancellationToken)
{
// Set last update time if we were installed before
IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => p.Id == package.Guid)
@@ -336,20 +319,9 @@ namespace Emby.Server.Implementations.Updates
await PerformPackageInstallation(package, cancellationToken).ConfigureAwait(false);
// Do plugin-specific processing
- if (plugin == null)
- {
- _logger.LogInformation("New plugin installed: {0} {1}", package.Name, package.Version);
+ _logger.LogInformation(plugin == null ? "New plugin installed: {0} {1}" : "Plugin updated: {0} {1}", package.Name, package.Version);
- PluginInstalled?.Invoke(this, package);
- }
- else
- {
- _logger.LogInformation("Plugin updated: {0} {1}", package.Name, package.Version);
-
- PluginUpdated?.Invoke(this, package);
- }
-
- _applicationHost.NotifyPendingRestart();
+ return plugin != null;
}
private async Task PerformPackageInstallation(InstallationInfo package, CancellationToken cancellationToken)
@@ -467,7 +439,7 @@ namespace Emby.Server.Implementations.Updates
_config.SaveConfiguration();
}
- PluginUninstalled?.Invoke(this, plugin);
+ _eventManager.Publish(new PluginUninstalledEventArgs(plugin));
_applicationHost.NotifyPendingRestart();
}
diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs
index 610c2aa646..826fce6b0c 100644
--- a/Jellyfin.Api/Controllers/ArtistsController.cs
+++ b/Jellyfin.Api/Controllers/ArtistsController.cs
@@ -4,6 +4,7 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -90,7 +91,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -146,9 +147,9 @@ namespace Jellyfin.Api.Controllers
NameLessThan = nameLessThan,
NameStartsWith = nameStartsWith,
NameStartsWithOrGreater = nameStartsWithOrGreater,
- Tags = RequestHelpers.Split(tags, ',', true),
- OfficialRatings = RequestHelpers.Split(officialRatings, ',', true),
- Genres = RequestHelpers.Split(genres, ',', true),
+ Tags = RequestHelpers.Split(tags, '|', true),
+ OfficialRatings = RequestHelpers.Split(officialRatings, '|', true),
+ Genres = RequestHelpers.Split(genres, '|', true),
GenreIds = RequestHelpers.GetGuids(genreIds),
StudioIds = RequestHelpers.GetGuids(studioIds),
Person = person,
@@ -299,7 +300,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -355,9 +356,9 @@ namespace Jellyfin.Api.Controllers
NameLessThan = nameLessThan,
NameStartsWith = nameStartsWith,
NameStartsWithOrGreater = nameStartsWithOrGreater,
- Tags = RequestHelpers.Split(tags, ',', true),
- OfficialRatings = RequestHelpers.Split(officialRatings, ',', true),
- Genres = RequestHelpers.Split(genres, ',', true),
+ Tags = RequestHelpers.Split(tags, '|', true),
+ OfficialRatings = RequestHelpers.Split(officialRatings, '|', true),
+ Genres = RequestHelpers.Split(genres, '|', true),
GenreIds = RequestHelpers.GetGuids(genreIds),
StudioIds = RequestHelpers.GetGuids(studioIds),
Person = person,
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index fda00b8d46..20fc96ba83 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.ModelBinders;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -121,7 +122,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? sortOrder,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] string? sortBy,
[FromQuery] string? fields)
{
@@ -196,7 +197,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] Guid? userId,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] string? fields,
[FromQuery] string? channelIds)
{
diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs
index 2fc697a6aa..eae06b767c 100644
--- a/Jellyfin.Api/Controllers/CollectionController.cs
+++ b/Jellyfin.Api/Controllers/CollectionController.cs
@@ -83,14 +83,14 @@ namespace Jellyfin.Api.Controllers
/// Adds items to a collection.
///
/// The collection id.
- /// Item ids, comma delimited.
+ /// Item ids, comma delimited.
/// Items added to collection.
/// A indicating success.
[HttpPost("{collectionId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public async Task AddToCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string itemIds)
+ public async Task AddToCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string ids)
{
- await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true);
+ await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(ids)).ConfigureAwait(true);
return NoContent();
}
@@ -98,14 +98,14 @@ namespace Jellyfin.Api.Controllers
/// Removes items from a collection.
///
/// The collection id.
- /// Item ids, comma delimited.
+ /// Item ids, comma delimited.
/// Items removed from collection.
/// A indicating success.
[HttpDelete("{collectionId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public async Task RemoveFromCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string itemIds)
+ public async Task RemoveFromCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string ids)
{
- await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false);
+ await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(ids)).ConfigureAwait(false);
return NoContent();
}
}
diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs
index 409d8e863d..aa7d02de00 100644
--- a/Jellyfin.Api/Controllers/GenresController.cs
+++ b/Jellyfin.Api/Controllers/GenresController.cs
@@ -5,6 +5,7 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -91,7 +92,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 05efe23553..4a67c1aede 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -109,7 +109,7 @@ namespace Jellyfin.Api.Controllers
var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
if (user.ProfileImage != null)
{
- _userManager.ClearProfileImage(user);
+ await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
}
user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
@@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
- public ActionResult DeleteUserImage(
+ public async Task DeleteUserImage(
[FromRoute, Required] Guid userId,
[FromRoute, Required] ImageType imageType,
[FromRoute] int? index = null)
@@ -158,7 +158,7 @@ namespace Jellyfin.Api.Controllers
_logger.LogError(e, "Error deleting user profile image:");
}
- _userManager.ClearProfileImage(user);
+ await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index 33fa14c646..7ca5775438 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -5,6 +5,7 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -159,7 +160,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isHd,
[FromQuery] bool? is4K,
[FromQuery] string? locationTypes,
- [FromQuery] LocationType[] excludeLocationTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] excludeLocationTypes,
[FromQuery] bool? isMissing,
[FromQuery] bool? isUnaired,
[FromQuery] double? minCommunityRating,
@@ -182,7 +183,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] ImageType[] imageTypes,
diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs
index d290e3c5bd..94995650cb 100644
--- a/Jellyfin.Api/Controllers/LibraryStructureController.cs
+++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
+using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.LibraryStructureDto;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -75,7 +76,7 @@ namespace Jellyfin.Api.Controllers
public async Task AddVirtualFolder(
[FromQuery] string? name,
[FromQuery] string? collectionType,
- [FromQuery] string[] paths,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] paths,
[FromBody] AddVirtualFolderDto? libraryOptionsDto,
[FromQuery] bool refreshLibrary = false)
{
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 2cc2f0e74d..58c7473c23 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -592,7 +592,7 @@ namespace Jellyfin.Api.Controllers
IsKids = isKids,
IsSports = isSports,
SeriesTimerId = seriesTimerId,
- Genres = RequestHelpers.Split(genres, ',', true),
+ Genres = RequestHelpers.Split(genres, '|', true),
GenreIds = RequestHelpers.GetGuids(genreIds)
};
@@ -648,7 +648,7 @@ namespace Jellyfin.Api.Controllers
IsKids = body.IsKids,
IsSports = body.IsSports,
SeriesTimerId = body.SeriesTimerId,
- Genres = RequestHelpers.Split(body.Genres, ',', true),
+ Genres = RequestHelpers.Split(body.Genres, '|', true),
GenreIds = RequestHelpers.GetGuids(body.GenreIds)
};
diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs
index 7fcfc749de..afb536e747 100644
--- a/Jellyfin.Api/Controllers/MoviesController.cs
+++ b/Jellyfin.Api/Controllers/MoviesController.cs
@@ -85,8 +85,8 @@ namespace Jellyfin.Api.Controllers
IncludeItemTypes = new[]
{
nameof(Movie),
- // typeof(Trailer).Name,
- // typeof(LiveTvProgram).Name
+ // nameof(Trailer),
+ // nameof(LiveTvProgram)
},
// IsMovie = true
OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple(i, SortOrder.Descending)).ToArray(),
diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs
index 2a13122739..4de7ef8c88 100644
--- a/Jellyfin.Api/Controllers/MusicGenresController.cs
+++ b/Jellyfin.Api/Controllers/MusicGenresController.cs
@@ -5,6 +5,7 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -90,7 +91,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs
index bd84c9228b..f173f75ba3 100644
--- a/Jellyfin.Api/Controllers/PersonsController.cs
+++ b/Jellyfin.Api/Controllers/PersonsController.cs
@@ -5,6 +5,7 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -90,7 +91,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index 5656709629..e506ac7bf1 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -379,7 +379,7 @@ namespace Jellyfin.Api.Controllers
public ActionResult PostCapabilities(
[FromQuery] string? id,
[FromQuery] string? playableMediaTypes,
- [FromQuery] GeneralCommandType[] supportedCommands,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] GeneralCommandType[] supportedCommands,
[FromQuery] bool supportsMediaControl = false,
[FromQuery] bool supportsSync = false,
[FromQuery] bool supportsPersistentIdentifier = true)
diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs
index 1d2bf22551..94eb3f7faa 100644
--- a/Jellyfin.Api/Controllers/StudiosController.cs
+++ b/Jellyfin.Api/Controllers/StudiosController.cs
@@ -4,6 +4,7 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
+using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -89,7 +90,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -145,9 +146,9 @@ namespace Jellyfin.Api.Controllers
NameLessThan = nameLessThan,
NameStartsWith = nameStartsWith,
NameStartsWithOrGreater = nameStartsWithOrGreater,
- Tags = RequestHelpers.Split(tags, ',', true),
- OfficialRatings = RequestHelpers.Split(officialRatings, ',', true),
- Genres = RequestHelpers.Split(genres, ',', true),
+ Tags = RequestHelpers.Split(tags, '|', true),
+ OfficialRatings = RequestHelpers.Split(officialRatings, '|', true),
+ Genres = RequestHelpers.Split(genres, '|', true),
GenreIds = RequestHelpers.GetGuids(genreIds),
StudioIds = RequestHelpers.GetGuids(studioIds),
Person = person,
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index 889c0e128d..281c0016e9 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -1,5 +1,6 @@
using System;
using Jellyfin.Api.Constants;
+using Jellyfin.Api.ModelBinders;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -125,7 +126,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isHd,
[FromQuery] bool? is4K,
[FromQuery] string? locationTypes,
- [FromQuery] LocationType[] excludeLocationTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] excludeLocationTypes,
[FromQuery] bool? isMissing,
[FromQuery] bool? isUnaired,
[FromQuery] double? minCommunityRating,
@@ -147,7 +148,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? parentId,
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
- [FromQuery] ItemFilter[] filters,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] ImageType[] imageTypes,
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index df20a92b3d..a219a74cf8 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -88,16 +88,14 @@ namespace Jellyfin.Api.Controllers
/// Redirected to remote audio stream.
/// A containing the audio file.
[HttpGet("Audio/{itemId}/universal")]
- [HttpGet("Audio/{itemId}/universal.{container}", Name = "GetUniversalAudioStream_2")]
[HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")]
- [HttpHead("Audio/{itemId}/universal.{container}", Name = "HeadUniversalAudioStream_2")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status302Found)]
[ProducesAudioFile]
public async Task GetUniversalAudioStream(
[FromRoute, Required] Guid itemId,
- [FromRoute] string? container,
+ [FromQuery] string? container,
[FromQuery] string? mediaSourceId,
[FromQuery] string? deviceId,
[FromQuery] Guid? userId,
@@ -276,7 +274,7 @@ namespace Jellyfin.Api.Controllers
foreach (var cont in containers)
{
- var parts = RequestHelpers.Split(cont, ',', true);
+ var parts = RequestHelpers.Split(cont, '|', true);
var audioCodecs = parts.Length == 1 ? null : string.Join(",", parts.Skip(1).ToArray());
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 50bb8bb2aa..7b0897bfb9 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -381,17 +381,13 @@ namespace Jellyfin.Api.Controllers
var user = _userManager.GetUserById(userId);
- if (string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal))
- {
- await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
- _userManager.UpdateConfiguration(user.Id, updateUser.Configuration);
- }
- else
+ if (!string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal))
{
await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false);
- _userManager.UpdateConfiguration(updateUser.Id, updateUser.Configuration);
}
+ await _userManager.UpdateConfigurationAsync(user.Id, updateUser.Configuration).ConfigureAwait(false);
+
return NoContent();
}
@@ -409,7 +405,7 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
- public ActionResult UpdateUserPolicy(
+ public async Task UpdateUserPolicy(
[FromRoute, Required] Guid userId,
[FromBody] UserPolicy newPolicy)
{
@@ -447,7 +443,7 @@ namespace Jellyfin.Api.Controllers
_sessionManager.RevokeUserTokens(user.Id, currentToken);
}
- _userManager.UpdatePolicy(userId, newPolicy);
+ await _userManager.UpdatePolicyAsync(userId, newPolicy).ConfigureAwait(false);
return NoContent();
}
@@ -464,7 +460,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
- public ActionResult UpdateUserConfiguration(
+ public async Task UpdateUserConfiguration(
[FromRoute, Required] Guid userId,
[FromBody] UserConfiguration userConfig)
{
@@ -473,7 +469,7 @@ namespace Jellyfin.Api.Controllers
return Forbid("User configuration update not allowed");
}
- _userManager.UpdateConfiguration(userId, userConfig);
+ await _userManager.UpdateConfigurationAsync(userId, userConfig).ConfigureAwait(false);
return NoContent();
}
diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
index 6b516977e8..366301d3ee 100644
--- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
+++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
@@ -123,9 +123,8 @@ namespace Jellyfin.Api.Helpers
state.Dispose();
}
- await new ProgressiveFileCopier(outputPath, job, transcodingJobHelper, CancellationToken.None)
- .WriteToAsync(httpContext.Response.Body, CancellationToken.None).ConfigureAwait(false);
- return new FileStreamResult(httpContext.Response.Body, contentType);
+ var stream = new ProgressiveFileStream(outputPath, job, transcodingJobHelper);
+ return new FileStreamResult(stream, contentType);
}
finally
{
diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
new file mode 100644
index 0000000000..b3566b6f80
--- /dev/null
+++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
@@ -0,0 +1,162 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Api.Models.PlaybackDtos;
+using MediaBrowser.Model.IO;
+
+namespace Jellyfin.Api.Helpers
+{
+ ///
+ /// A progressive file stream for transferring transcoded files as they are written to.
+ ///
+ public class ProgressiveFileStream : Stream
+ {
+ private readonly FileStream _fileStream;
+ private readonly TranscodingJobDto? _job;
+ private readonly TranscodingJobHelper _transcodingJobHelper;
+ private readonly bool _allowAsyncFileRead;
+ private int _bytesWritten;
+ private bool _disposed;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The path to the transcoded file.
+ /// The transcoding job information.
+ /// The transcoding job helper.
+ public ProgressiveFileStream(string filePath, TranscodingJobDto? job, TranscodingJobHelper transcodingJobHelper)
+ {
+ _job = job;
+ _transcodingJobHelper = transcodingJobHelper;
+ _bytesWritten = 0;
+
+ var fileOptions = FileOptions.SequentialScan;
+ _allowAsyncFileRead = false;
+
+ // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ fileOptions |= FileOptions.Asynchronous;
+ _allowAsyncFileRead = true;
+ }
+
+ _fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions);
+ }
+
+ ///
+ public override bool CanRead => _fileStream.CanRead;
+
+ ///
+ public override bool CanSeek => false;
+
+ ///
+ public override bool CanWrite => false;
+
+ ///
+ public override long Length => throw new NotSupportedException();
+
+ ///
+ public override long Position
+ {
+ get => throw new NotSupportedException();
+ set => throw new NotSupportedException();
+ }
+
+ ///
+ public override void Flush()
+ {
+ _fileStream.Flush();
+ }
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return _fileStream.Read(buffer, offset, count);
+ }
+
+ ///
+ public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ int totalBytesRead = 0;
+ int remainingBytesToRead = count;
+
+ while (remainingBytesToRead > 0)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ int bytesRead;
+ if (_allowAsyncFileRead)
+ {
+ bytesRead = await _fileStream.ReadAsync(buffer, offset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ bytesRead = _fileStream.Read(buffer, offset, remainingBytesToRead);
+ }
+
+ remainingBytesToRead -= bytesRead;
+ if (bytesRead > 0)
+ {
+ _bytesWritten += bytesRead;
+ totalBytesRead += bytesRead;
+
+ if (_job != null)
+ {
+ _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
+ }
+ }
+ else
+ {
+ if (_job == null || _job.HasExited)
+ {
+ break;
+ }
+
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ return totalBytesRead;
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ => throw new NotSupportedException();
+
+ ///
+ public override void SetLength(long value)
+ => throw new NotSupportedException();
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ => throw new NotSupportedException();
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ try
+ {
+ if (disposing)
+ {
+ _fileStream.Dispose();
+
+ if (_job != null)
+ {
+ _transcodingJobHelper.OnTranscodeEndRequest(_job);
+ }
+ }
+ }
+ finally
+ {
+ _disposed = true;
+ base.Dispose(disposing);
+ }
+ }
+ }
+}
diff --git a/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinderProvider.cs b/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinderProvider.cs
deleted file mode 100644
index b9785a73b8..0000000000
--- a/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinderProvider.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.AspNetCore.Mvc.ModelBinding;
-
-namespace Jellyfin.Api.ModelBinders
-{
- ///
- /// Comma delimited array model binder provider.
- ///
- public class CommaDelimitedArrayModelBinderProvider : IModelBinderProvider
- {
- private readonly IModelBinder _binder;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public CommaDelimitedArrayModelBinderProvider()
- {
- _binder = new CommaDelimitedArrayModelBinder();
- }
-
- ///
- public IModelBinder? GetBinder(ModelBinderProviderContext context)
- {
- return context.Metadata.ModelType.IsArray ? _binder : null;
- }
- }
-}
diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
index 5926abfe0d..7bde4f35be 100644
--- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
+++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
@@ -72,6 +72,18 @@ namespace Jellyfin.Server.Implementations.Activity
};
}
+ ///
+ public async Task CleanAsync(DateTime startDate)
+ {
+ await using var dbContext = _provider.CreateContext();
+ var entries = dbContext.ActivityLogs
+ .AsQueryable()
+ .Where(entry => entry.DateCreated <= startDate);
+
+ dbContext.RemoveRange(entries);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+
private static ActivityLogEntry ConvertToOldModel(ActivityLog entry)
{
return new ActivityLogEntry
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index b39333b5cd..40b89ed284 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -432,11 +432,9 @@ namespace Jellyfin.Server.Implementations.Users
// the authentication provider might have created it
user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
- if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
+ if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy && user != null)
{
- UpdatePolicy(user.Id, hasNewUserPolicy.GetNewUserPolicy());
-
- await UpdateUserAsync(user).ConfigureAwait(false);
+ await UpdatePolicyAsync(user.Id, hasNewUserPolicy.GetNewUserPolicy()).ConfigureAwait(false);
}
}
}
@@ -615,9 +613,9 @@ namespace Jellyfin.Server.Implementations.Users
}
///
- public void UpdateConfiguration(Guid userId, UserConfiguration config)
+ public async Task UpdateConfigurationAsync(Guid userId, UserConfiguration config)
{
- using var dbContext = _dbProvider.CreateContext();
+ await using var dbContext = _dbProvider.CreateContext();
var user = dbContext.Users
.Include(u => u.Permissions)
.Include(u => u.Preferences)
@@ -644,13 +642,13 @@ namespace Jellyfin.Server.Implementations.Users
user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
dbContext.Update(user);
- dbContext.SaveChanges();
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
///
- public void UpdatePolicy(Guid userId, UserPolicy policy)
+ public async Task UpdatePolicyAsync(Guid userId, UserPolicy policy)
{
- using var dbContext = _dbProvider.CreateContext();
+ await using var dbContext = _dbProvider.CreateContext();
var user = dbContext.Users
.Include(u => u.Permissions)
.Include(u => u.Preferences)
@@ -715,15 +713,15 @@ namespace Jellyfin.Server.Implementations.Users
user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
dbContext.Update(user);
- dbContext.SaveChanges();
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
///
- public void ClearProfileImage(User user)
+ public async Task ClearProfileImageAsync(User user)
{
- using var dbContext = _dbProvider.CreateContext();
+ await using var dbContext = _dbProvider.CreateContext();
dbContext.Remove(user.ProfileImage);
- dbContext.SaveChanges();
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
user.ProfileImage = null;
}
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index d7b9da5c2f..e180d0cd7a 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -16,7 +16,6 @@ using Jellyfin.Api.Auth.LocalAccessPolicy;
using Jellyfin.Api.Auth.RequiresElevationPolicy;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Controllers;
-using Jellyfin.Api.ModelBinders;
using Jellyfin.Server.Configuration;
using Jellyfin.Server.Filters;
using Jellyfin.Server.Formatters;
@@ -167,8 +166,6 @@ namespace Jellyfin.Server.Extensions
opts.OutputFormatters.Add(new CssOutputFormatter());
opts.OutputFormatters.Add(new XmlOutputFormatter());
-
- opts.ModelBinderProviders.Insert(0, new CommaDelimitedArrayModelBinderProvider());
})
// Clear app parts to avoid other assemblies being picked up
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 07f08ec72b..a64d2e1cdb 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -43,7 +43,7 @@
-
+
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index 5573c04395..97a51c202b 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -290,23 +290,19 @@ namespace Jellyfin.Server
{
_logger.LogInformation("Kestrel listening on {IpAddress}", address);
options.Listen(address, appHost.HttpPort);
+
if (appHost.ListenWithHttps)
{
- options.Listen(address, appHost.HttpsPort, listenOptions =>
- {
- listenOptions.UseHttps(appHost.Certificate);
- listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
- });
+ options.Listen(
+ address,
+ appHost.HttpsPort,
+ listenOptions => listenOptions.UseHttps(appHost.Certificate));
}
else if (builderContext.HostingEnvironment.IsDevelopment())
{
try
{
- options.Listen(address, appHost.HttpsPort, listenOptions =>
- {
- listenOptions.UseHttps();
- listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
- });
+ options.Listen(address, appHost.HttpsPort, listenOptions => listenOptions.UseHttps());
}
catch (InvalidOperationException ex)
{
@@ -322,21 +318,15 @@ namespace Jellyfin.Server
if (appHost.ListenWithHttps)
{
- options.ListenAnyIP(appHost.HttpsPort, listenOptions =>
- {
- listenOptions.UseHttps(appHost.Certificate);
- listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
- });
+ options.ListenAnyIP(
+ appHost.HttpsPort,
+ listenOptions => listenOptions.UseHttps(appHost.Certificate));
}
else if (builderContext.HostingEnvironment.IsDevelopment())
{
try
{
- options.ListenAnyIP(appHost.HttpsPort, listenOptions =>
- {
- listenOptions.UseHttps();
- listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
- });
+ options.ListenAnyIP(appHost.HttpsPort, listenOptions => listenOptions.UseHttps());
}
catch (InvalidOperationException ex)
{
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index 169aca2ca0..6aa16fea74 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -11,29 +11,6 @@ namespace MediaBrowser.Common.Updates
{
public interface IInstallationManager : IDisposable
{
- event EventHandler PackageInstalling;
-
- event EventHandler PackageInstallationCompleted;
-
- event EventHandler PackageInstallationFailed;
-
- event EventHandler PackageInstallationCancelled;
-
- ///
- /// Occurs when a plugin is uninstalled.
- ///
- event EventHandler PluginUninstalled;
-
- ///
- /// Occurs when a plugin is updated.
- ///
- event EventHandler PluginUpdated;
-
- ///
- /// Occurs when a plugin is installed.
- ///
- event EventHandler PluginInstalled;
-
///
/// Gets the completed installations.
///
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 397a68ff7e..c5e50cf45d 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -56,7 +56,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
if (query.IncludeItemTypes.Length == 0)
{
- query.IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicVideo).Name, typeof(MusicAlbum).Name };
+ query.IncludeItemTypes = new[] { nameof(Audio), nameof(MusicVideo), nameof(MusicAlbum) };
query.ArtistIds = new[] { Id };
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 5a117a6b15..f0c076108e 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public IList GetTaggedItems(InternalItemsQuery query)
{
query.GenreIds = new[] { Id };
- query.IncludeItemTypes = new[] { typeof(MusicVideo).Name, typeof(Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name };
+ query.IncludeItemTypes = new[] { nameof(MusicVideo), nameof(Audio), nameof(MusicAlbum), nameof(MusicArtist) };
return LibraryManager.GetItemList(query);
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 35ddaffadc..a76c8a376b 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -723,7 +723,7 @@ namespace MediaBrowser.Controller.Entities
private bool RequiresPostFiltering2(InternalItemsQuery query)
{
- if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], typeof(BoxSet).Name, StringComparison.OrdinalIgnoreCase))
+ if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase))
{
Logger.LogDebug("Query requires post-filtering due to BoxSet query");
return true;
@@ -813,7 +813,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsPlayed.HasValue)
{
- if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes.Contains(typeof(Series).Name))
+ if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes.Contains(nameof(Series)))
{
Logger.LogDebug("Query requires post-filtering due to IsPlayed");
return true;
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index db6c85caf7..74a1702040 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -59,7 +59,13 @@ namespace MediaBrowser.Controller.Entities
public IList GetTaggedItems(InternalItemsQuery query)
{
query.GenreIds = new[] { Id };
- query.ExcludeItemTypes = new[] { typeof(MusicVideo).Name, typeof(Audio.Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name };
+ query.ExcludeItemTypes = new[]
+ {
+ nameof(MusicVideo),
+ nameof(Entities.Audio.Audio),
+ nameof(MusicAlbum),
+ nameof(MusicArtist)
+ };
return LibraryManager.GetItemList(query);
}
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 75a746bfb3..e8afa9a490 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -151,7 +151,7 @@ namespace MediaBrowser.Controller.Entities.TV
if (query.IncludeItemTypes.Length == 0)
{
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
+ query.IncludeItemTypes = new[] { nameof(Episode) };
}
query.IsVirtualItem = false;
@@ -207,7 +207,7 @@ namespace MediaBrowser.Controller.Entities.TV
query.AncestorWithPresentationUniqueKey = null;
query.SeriesPresentationUniqueKey = seriesKey;
- query.IncludeItemTypes = new[] { typeof(Season).Name };
+ query.IncludeItemTypes = new[] { nameof(Season) };
query.OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray();
if (user != null && !user.DisplayMissingEpisodes)
@@ -233,7 +233,7 @@ namespace MediaBrowser.Controller.Entities.TV
if (query.IncludeItemTypes.Length == 0)
{
- query.IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name };
+ query.IncludeItemTypes = new[] { nameof(Episode), nameof(Season) };
}
query.IsVirtualItem = false;
@@ -253,7 +253,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
+ IncludeItemTypes = new[] { nameof(Episode), nameof(Season) },
OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(),
DtoOptions = options
};
@@ -364,7 +364,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
- IncludeItemTypes = new[] { typeof(Episode).Name },
+ IncludeItemTypes = new[] { nameof(Episode) },
OrderBy = new[] { ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(),
DtoOptions = options
};
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 7bb311900e..a262fee15d 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -142,7 +142,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IncludeItemTypes.Length == 0)
{
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
}
return parent.QueryRecursive(query);
@@ -167,7 +167,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
return _libraryManager.GetItemsResult(query);
}
@@ -178,7 +178,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Series).Name };
+ query.IncludeItemTypes = new[] { nameof(Series) };
return _libraryManager.GetItemsResult(query);
}
@@ -189,7 +189,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
query.IsFavorite = true;
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
+ query.IncludeItemTypes = new[] { nameof(Episode) };
return _libraryManager.GetItemsResult(query);
}
@@ -200,7 +200,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
return _libraryManager.GetItemsResult(query);
}
@@ -208,7 +208,7 @@ namespace MediaBrowser.Controller.Entities
private QueryResult GetMovieCollections(Folder parent, User user, InternalItemsQuery query)
{
query.Parent = null;
- query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
+ query.IncludeItemTypes = new[] { nameof(BoxSet) };
query.SetUser(user);
query.Recursive = true;
@@ -223,7 +223,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
return ConvertToResult(_libraryManager.GetItemList(query));
}
@@ -236,7 +236,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
return ConvertToResult(_libraryManager.GetItemList(query));
}
@@ -255,7 +255,7 @@ namespace MediaBrowser.Controller.Entities
{
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(Movie).Name },
+ IncludeItemTypes = new[] { nameof(Movie) },
Recursive = true,
EnableTotalRecordCount = false
}).Items
@@ -286,7 +286,7 @@ namespace MediaBrowser.Controller.Entities
query.GenreIds = new[] { displayParent.Id };
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(Movie).Name };
+ query.IncludeItemTypes = new[] { nameof(Movie) };
return _libraryManager.GetItemsResult(query);
}
@@ -333,7 +333,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
+ query.IncludeItemTypes = new[] { nameof(Episode) };
query.IsVirtualItem = false;
return ConvertToResult(_libraryManager.GetItemList(query));
@@ -362,7 +362,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
query.Limit = GetSpecialItemsLimit();
- query.IncludeItemTypes = new[] { typeof(Episode).Name };
+ query.IncludeItemTypes = new[] { nameof(Episode) };
return ConvertToResult(_libraryManager.GetItemList(query));
}
@@ -373,7 +373,7 @@ namespace MediaBrowser.Controller.Entities
query.Parent = parent;
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(Series).Name };
+ query.IncludeItemTypes = new[] { nameof(Series) };
return _libraryManager.GetItemsResult(query);
}
@@ -382,7 +382,7 @@ namespace MediaBrowser.Controller.Entities
{
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
{
- IncludeItemTypes = new[] { typeof(Series).Name },
+ IncludeItemTypes = new[] { nameof(Series) },
Recursive = true,
EnableTotalRecordCount = false
}).Items
@@ -413,7 +413,7 @@ namespace MediaBrowser.Controller.Entities
query.GenreIds = new[] { displayParent.Id };
query.SetUser(user);
- query.IncludeItemTypes = new[] { typeof(Series).Name };
+ query.IncludeItemTypes = new[] { nameof(Series) };
return _libraryManager.GetItemsResult(query);
}
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index 6a4f5cf679..8fd3b8c347 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -158,7 +158,8 @@ namespace MediaBrowser.Controller.Library
///
/// The user's Id.
/// The request containing the new user configuration.
- void UpdateConfiguration(Guid userId, UserConfiguration config);
+ /// A task representing the update.
+ Task UpdateConfigurationAsync(Guid userId, UserConfiguration config);
///
/// This method updates the user's policy.
@@ -167,12 +168,14 @@ namespace MediaBrowser.Controller.Library
///
/// The user's Id.
/// The request containing the new user policy.
- void UpdatePolicy(Guid userId, UserPolicy policy);
+ /// A task representing the update.
+ Task UpdatePolicyAsync(Guid userId, UserPolicy policy);
///
/// Clears the user's profile image.
///
/// The user.
- void ClearProfileImage(User user);
+ /// A task representing the clearing of the profile image.
+ Task ClearProfileImageAsync(User user);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 1d733479cc..5846a603a2 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -2675,9 +2675,10 @@ namespace MediaBrowser.Controller.MediaEncoding
state.MediaSource = mediaSource;
var request = state.BaseRequest;
- if (!string.IsNullOrWhiteSpace(request.AudioCodec))
+ var supportedAudioCodecs = state.SupportedAudioCodecs;
+ if (request != null && supportedAudioCodecs != null && supportedAudioCodecs.Length > 0)
{
- var supportedAudioCodecsList = request.AudioCodec.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ var supportedAudioCodecsList = supportedAudioCodecs.ToList();
ShiftAudioCodecsIfNeeded(supportedAudioCodecsList, state.AudioStream);
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 6cd0c70d2f..6e9362cd14 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -287,6 +287,11 @@ namespace MediaBrowser.Controller.MediaEncoding
return BaseRequest.AudioChannels;
}
+ if (BaseRequest.TranscodingMaxAudioChannels.HasValue)
+ {
+ return BaseRequest.TranscodingMaxAudioChannels;
+ }
+
if (!string.IsNullOrEmpty(codec))
{
var value = BaseRequest.GetOption(codec, "audiochannels");
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 216dd27098..e8b7be7e20 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -160,7 +160,7 @@ namespace MediaBrowser.Controller.Playlists
return LibraryManager.GetItemList(new InternalItemsQuery(user)
{
Recursive = true,
- IncludeItemTypes = new[] { typeof(Audio).Name },
+ IncludeItemTypes = new[] { nameof(Audio) },
GenreIds = new[] { musicGenre.Id },
OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(),
DtoOptions = options
@@ -172,7 +172,7 @@ namespace MediaBrowser.Controller.Playlists
return LibraryManager.GetItemList(new InternalItemsQuery(user)
{
Recursive = true,
- IncludeItemTypes = new[] { typeof(Audio).Name },
+ IncludeItemTypes = new[] { nameof(Audio) },
ArtistIds = new[] { musicArtist.Id },
OrderBy = new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }.Select(i => new ValueTuple(i, SortOrder.Ascending)).ToArray(),
DtoOptions = options
diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs
index 3e4ea208ed..28073fb8d7 100644
--- a/MediaBrowser.Model/Activity/IActivityManager.cs
+++ b/MediaBrowser.Model/Activity/IActivityManager.cs
@@ -16,5 +16,12 @@ namespace MediaBrowser.Model.Activity
Task CreateAsync(ActivityLog entry);
Task> GetPagedResultAsync(ActivityLogQuery query);
+
+ ///
+ /// Remove all activity logs before the specified date.
+ ///
+ /// Activity log start date.
+ /// A representing the asynchronous operation.
+ Task CleanAsync(DateTime startDate);
}
}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 8b78ad842e..23a5201f7a 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -271,6 +271,11 @@ namespace MediaBrowser.Model.Configuration
///
public string[] KnownProxies { get; set; }
+ ///
+ /// Gets or sets the number of days we should retain activity logs.
+ ///
+ public int? ActivityLogRetentionDays { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -381,6 +386,7 @@ namespace MediaBrowser.Model.Configuration
SlowResponseThresholdMs = 500;
CorsHosts = new[] { "*" };
KnownProxies = Array.Empty();
+ ActivityLogRetentionDays = 30;
}
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 24400eae53..9465fe42c2 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
index dc3c60dee0..a5cd425f6d 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs
@@ -54,7 +54,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
var seriesWithPerson = _libraryManager.GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(Series).Name },
+ IncludeItemTypes = new[] { nameof(Series) },
PersonIds = new[] { item.Id },
DtoOptions = new DtoOptions(false)
{
diff --git a/debian/control b/debian/control
index 39c2aa055b..9216d24fe3 100644
--- a/debian/control
+++ b/debian/control
@@ -20,7 +20,6 @@ Breaks: jellyfin (<<10.6.0)
Architecture: any
Depends: at,
libsqlite3-0,
- jellyfin-ffmpeg (>= 4.2.1-2),
libfontconfig1,
libfreetype6,
libssl1.1
diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
index e7993d2e7c..0236f2ac1e 100644
--- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
+++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj
@@ -13,9 +13,9 @@
-
-
-
+
+
+
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
index 6e5a908022..05323490e2 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
+++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj
@@ -14,8 +14,8 @@
-
-
+
+