aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api
diff options
context:
space:
mode:
authorOrry Verducci <orry@orryverducci.co.uk>2021-12-01 22:13:52 +0000
committerGitHub <noreply@github.com>2021-12-01 22:13:52 +0000
commite446e9fde935ad5744500e6efaab8fcacf89b600 (patch)
tree9012e91423660bf4bc9992f06cf26f53e826fb65 /Jellyfin.Api
parent9abe9e7e54cc454667ba2128b5d321631b5ece51 (diff)
parentf6d8c19a7ac41c6c7c217d9e9ccbf98f78122327 (diff)
Merge branch 'master' into mbaff-interlace-detection
Diffstat (limited to 'Jellyfin.Api')
-rw-r--r--Jellyfin.Api/Attributes/AcceptsFileAttribute.cs4
-rw-r--r--Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesFileAttribute.cs4
-rw-r--r--Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs2
-rw-r--r--Jellyfin.Api/Auth/CustomAuthenticationHandler.cs5
-rw-r--r--Jellyfin.Api/Controllers/ClientLogController.cs80
-rw-r--r--Jellyfin.Api/Controllers/DisplayPreferencesController.cs13
-rw-r--r--Jellyfin.Api/Controllers/DlnaController.cs2
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs10
-rw-r--r--Jellyfin.Api/Controllers/HlsSegmentController.cs6
-rw-r--r--Jellyfin.Api/Controllers/ImageByNameController.cs6
-rw-r--r--Jellyfin.Api/Controllers/ItemLookupController.cs6
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs23
-rw-r--r--Jellyfin.Api/Controllers/PersonsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/PluginsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/RemoteImageController.cs4
-rw-r--r--Jellyfin.Api/Controllers/SubtitleController.cs2
-rw-r--r--Jellyfin.Api/Controllers/SystemController.cs5
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs6
-rw-r--r--Jellyfin.Api/Helpers/ClassMigrationHelper.cs2
-rw-r--r--Jellyfin.Api/Helpers/ProgressiveFileStream.cs76
-rw-r--r--Jellyfin.Api/Helpers/StreamingHelpers.cs2
-rw-r--r--Jellyfin.Api/Helpers/TranscodingJobHelper.cs8
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj4
-rw-r--r--Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs22
-rw-r--r--Jellyfin.Api/Models/DisplayPreferencesDtos/DisplayPreferencesDto.cs106
-rw-r--r--Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs2
-rw-r--r--Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs4
34 files changed, 230 insertions, 200 deletions
diff --git a/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs b/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
index 49b6689cde..58552d847d 100644
--- a/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
@@ -1,4 +1,6 @@
-using System;
+#pragma warning disable CA1813 // Avoid unsealed attributes
+
+using System;
namespace Jellyfin.Api.Attributes
{
diff --git a/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs b/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
index 001f27409e..244a29da45 100644
--- a/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
- public class AcceptsImageFileAttribute : AcceptsFileAttribute
+ public sealed class AcceptsImageFileAttribute : AcceptsFileAttribute
{
private const string ContentType = "image/*";
diff --git a/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs b/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs
index 2fdd1e4899..af8727552c 100644
--- a/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs
+++ b/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs
@@ -7,7 +7,7 @@ namespace Jellyfin.Api.Attributes
/// <summary>
/// Identifies an action that supports the HTTP GET method.
/// </summary>
- public class HttpSubscribeAttribute : HttpMethodAttribute
+ public sealed class HttpSubscribeAttribute : HttpMethodAttribute
{
private static readonly IEnumerable<string> _supportedMethods = new[] { "SUBSCRIBE" };
diff --git a/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs b/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs
index d6d7e4563d..1c0b70e719 100644
--- a/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs
+++ b/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs
@@ -7,7 +7,7 @@ namespace Jellyfin.Api.Attributes
/// <summary>
/// Identifies an action that supports the HTTP GET method.
/// </summary>
- public class HttpUnsubscribeAttribute : HttpMethodAttribute
+ public sealed class HttpUnsubscribeAttribute : HttpMethodAttribute
{
private static readonly IEnumerable<string> _supportedMethods = new[] { "UNSUBSCRIBE" };
diff --git a/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs b/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
index 56c9772b6d..514e7ce974 100644
--- a/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
+++ b/Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
@@ -6,7 +6,7 @@ namespace Jellyfin.Api.Attributes
/// Attribute to mark a parameter as obsolete.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
- public class ParameterObsoleteAttribute : Attribute
+ public sealed class ParameterObsoleteAttribute : Attribute
{
}
}
diff --git a/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs
index 3adb700eb5..9fc25f192e 100644
--- a/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesAudioFileAttribute.cs
@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
- public class ProducesAudioFileAttribute : ProducesFileAttribute
+ public sealed class ProducesAudioFileAttribute : ProducesFileAttribute
{
private const string ContentType = "audio/*";
diff --git a/Jellyfin.Api/Attributes/ProducesFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesFileAttribute.cs
index 62a576ede2..2bf77d729a 100644
--- a/Jellyfin.Api/Attributes/ProducesFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesFileAttribute.cs
@@ -1,4 +1,6 @@
-using System;
+#pragma warning disable CA1813 // Avoid unsealed attributes
+
+using System;
namespace Jellyfin.Api.Attributes
{
diff --git a/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs
index e158136762..1e5b542e27 100644
--- a/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesImageFileAttribute.cs
@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
- public class ProducesImageFileAttribute : ProducesFileAttribute
+ public sealed class ProducesImageFileAttribute : ProducesFileAttribute
{
private const string ContentType = "image/*";
diff --git a/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs
index 5d928ab914..5b15cb1a56 100644
--- a/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesPlaylistFileAttribute.cs
@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "image/*".
/// </summary>
- public class ProducesPlaylistFileAttribute : ProducesFileAttribute
+ public sealed class ProducesPlaylistFileAttribute : ProducesFileAttribute
{
private const string ContentType = "application/x-mpegURL";
diff --git a/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs b/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs
index d8b2856dca..6857d45ecc 100644
--- a/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs
+++ b/Jellyfin.Api/Attributes/ProducesVideoFileAttribute.cs
@@ -3,7 +3,7 @@
/// <summary>
/// Produces file attribute of "video/*".
/// </summary>
- public class ProducesVideoFileAttribute : ProducesFileAttribute
+ public sealed class ProducesVideoFileAttribute : ProducesFileAttribute
{
private const string ContentType = "video/*";
diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
index 369e846aef..bd3e7d9e3e 100644
--- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
+++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
@@ -45,6 +45,11 @@ namespace Jellyfin.Api.Auth
try
{
var authorizationInfo = await _authService.Authenticate(Request).ConfigureAwait(false);
+ if (!authorizationInfo.HasToken)
+ {
+ return AuthenticateResult.NoResult();
+ }
+
var role = UserRoles.User;
if (authorizationInfo.IsApiKey || authorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
{
diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs
new file mode 100644
index 0000000000..98fd224307
--- /dev/null
+++ b/Jellyfin.Api/Controllers/ClientLogController.cs
@@ -0,0 +1,80 @@
+using System.Net.Mime;
+using System.Threading.Tasks;
+using Jellyfin.Api.Attributes;
+using Jellyfin.Api.Constants;
+using Jellyfin.Api.Helpers;
+using Jellyfin.Api.Models.ClientLogDtos;
+using MediaBrowser.Controller.ClientEvent;
+using MediaBrowser.Controller.Configuration;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers
+{
+ /// <summary>
+ /// Client log controller.
+ /// </summary>
+ [Authorize(Policy = Policies.DefaultAuthorization)]
+ public class ClientLogController : BaseJellyfinApiController
+ {
+ private const int MaxDocumentSize = 1_000_000;
+ private readonly IClientEventLogger _clientEventLogger;
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientLogController"/> class.
+ /// </summary>
+ /// <param name="clientEventLogger">Instance of the <see cref="IClientEventLogger"/> interface.</param>
+ /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
+ public ClientLogController(
+ IClientEventLogger clientEventLogger,
+ IServerConfigurationManager serverConfigurationManager)
+ {
+ _clientEventLogger = clientEventLogger;
+ _serverConfigurationManager = serverConfigurationManager;
+ }
+
+ /// <summary>
+ /// Upload a document.
+ /// </summary>
+ /// <response code="200">Document saved.</response>
+ /// <response code="403">Event logging disabled.</response>
+ /// <response code="413">Upload size too large.</response>
+ /// <returns>Create response.</returns>
+ [HttpPost("Document")]
+ [ProducesResponseType(typeof(ClientLogDocumentResponseDto), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+ [AcceptsFile(MediaTypeNames.Text.Plain)]
+ [RequestSizeLimit(MaxDocumentSize)]
+ public async Task<ActionResult<ClientLogDocumentResponseDto>> LogFile()
+ {
+ if (!_serverConfigurationManager.Configuration.AllowClientLogUpload)
+ {
+ return Forbid();
+ }
+
+ if (Request.ContentLength > MaxDocumentSize)
+ {
+ // Manually validate to return proper status code.
+ return StatusCode(StatusCodes.Status413PayloadTooLarge, $"Payload must be less than {MaxDocumentSize:N0} bytes");
+ }
+
+ var (clientName, clientVersion) = GetRequestInformation();
+ var fileName = await _clientEventLogger.WriteDocumentAsync(clientName, clientVersion, Request.Body)
+ .ConfigureAwait(false);
+ return Ok(new ClientLogDocumentResponseDto(fileName));
+ }
+
+ private (string ClientName, string ClientVersion) GetRequestInformation()
+ {
+ var clientName = ClaimHelpers.GetClient(HttpContext.User) ?? "unknown-client";
+ var clientVersion = ClaimHelpers.GetIsApiKey(HttpContext.User)
+ ? "apikey"
+ : ClaimHelpers.GetVersion(HttpContext.User) ?? "unknown-version";
+
+ return (clientName, clientVersion);
+ }
+ }
+}
diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
index 2079476d0a..0b2604640b 100644
--- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
+++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
@@ -8,7 +8,7 @@ using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Dto;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -143,21 +143,24 @@ namespace Jellyfin.Api.Controllers
existingDisplayPreferences.ScrollDirection = displayPreferences.ScrollDirection;
existingDisplayPreferences.ChromecastVersion = displayPreferences.CustomPrefs.TryGetValue("chromecastVersion", out var chromecastVersion)
+ && !string.IsNullOrEmpty(chromecastVersion)
? Enum.Parse<ChromecastVersion>(chromecastVersion, true)
: ChromecastVersion.Stable;
displayPreferences.CustomPrefs.Remove("chromecastVersion");
- existingDisplayPreferences.EnableNextVideoInfoOverlay = displayPreferences.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enableNextVideoInfoOverlay)
- ? bool.Parse(enableNextVideoInfoOverlay)
- : true;
+ existingDisplayPreferences.EnableNextVideoInfoOverlay = !displayPreferences.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enableNextVideoInfoOverlay)
+ || string.IsNullOrEmpty(enableNextVideoInfoOverlay)
+ || bool.Parse(enableNextVideoInfoOverlay);
displayPreferences.CustomPrefs.Remove("enableNextVideoInfoOverlay");
existingDisplayPreferences.SkipBackwardLength = displayPreferences.CustomPrefs.TryGetValue("skipBackLength", out var skipBackLength)
+ && !string.IsNullOrEmpty(skipBackLength)
? int.Parse(skipBackLength, CultureInfo.InvariantCulture)
: 10000;
displayPreferences.CustomPrefs.Remove("skipBackLength");
existingDisplayPreferences.SkipForwardLength = displayPreferences.CustomPrefs.TryGetValue("skipForwardLength", out var skipForwardLength)
+ && !string.IsNullOrEmpty(skipForwardLength)
? int.Parse(skipForwardLength, CultureInfo.InvariantCulture)
: 30000;
displayPreferences.CustomPrefs.Remove("skipForwardLength");
@@ -196,7 +199,7 @@ namespace Jellyfin.Api.Controllers
}
var itemPrefs = _displayPreferencesManager.GetItemDisplayPreferences(existingDisplayPreferences.UserId, itemId, existingDisplayPreferences.Client);
- itemPrefs.SortBy = displayPreferences.SortBy;
+ itemPrefs.SortBy = displayPreferences.SortBy ?? "SortName";
itemPrefs.SortOrder = displayPreferences.SortOrder;
itemPrefs.RememberIndexing = displayPreferences.RememberIndexing;
itemPrefs.RememberSorting = displayPreferences.RememberSorting;
diff --git a/Jellyfin.Api/Controllers/DlnaController.cs b/Jellyfin.Api/Controllers/DlnaController.cs
index 052a6aff2e..35c3a3d922 100644
--- a/Jellyfin.Api/Controllers/DlnaController.cs
+++ b/Jellyfin.Api/Controllers/DlnaController.cs
@@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- _dlnaManager.UpdateProfile(deviceProfile);
+ _dlnaManager.UpdateProfile(profileId, deviceProfile);
return NoContent();
}
}
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 42e82dd5b1..caa3d23681 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -1391,7 +1391,7 @@ namespace Jellyfin.Api.Controllers
}
else
{
- _logger.LogError("Invalid HLS segment container: " + segmentFormat);
+ _logger.LogError("Invalid HLS segment container: {SegmentFormat}", segmentFormat);
}
var maxMuxingQueueSize = _encodingOptions.MaxMuxingQueueSize > 128
@@ -1794,7 +1794,7 @@ namespace Jellyfin.Api.Controllers
return;
}
- _logger.LogDebug("Deleting partial HLS file {path}", path);
+ _logger.LogDebug("Deleting partial HLS file {Path}", path);
try
{
@@ -1802,15 +1802,15 @@ namespace Jellyfin.Api.Controllers
}
catch (IOException ex)
{
- _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
+ _logger.LogError(ex, "Error deleting partial stream file(s) {Path}", path);
var task = Task.Delay(100);
- Task.WaitAll(task);
+ task.Wait();
DeleteFile(path, retryCount + 1);
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error deleting partial stream file(s) {path}", path);
+ _logger.LogError(ex, "Error deleting partial stream file(s) {Path}", path);
}
}
diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs
index 71caa0fe0d..7325dca0ae 100644
--- a/Jellyfin.Api/Controllers/HlsSegmentController.cs
+++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs
@@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
- if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath))
+ if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid segment.");
}
@@ -90,7 +90,7 @@ namespace Jellyfin.Api.Controllers
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
- if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath) || Path.GetExtension(file) != ".m3u8")
+ if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture) || Path.GetExtension(file) != ".m3u8")
{
return BadRequest("Invalid segment.");
}
@@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers
file = Path.GetFullPath(Path.Combine(transcodeFolderPath, file));
var fileDir = Path.GetDirectoryName(file);
- if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodeFolderPath))
+ if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodeFolderPath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid segment.");
}
diff --git a/Jellyfin.Api/Controllers/ImageByNameController.cs b/Jellyfin.Api/Controllers/ImageByNameController.cs
index 99ab7f232e..89bbf22c96 100644
--- a/Jellyfin.Api/Controllers/ImageByNameController.cs
+++ b/Jellyfin.Api/Controllers/ImageByNameController.cs
@@ -82,7 +82,7 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- if (!path.StartsWith(_applicationPaths.GeneralPath))
+ if (!path.StartsWith(_applicationPaths.GeneralPath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid image path.");
}
@@ -177,7 +177,7 @@ namespace Jellyfin.Api.Controllers
if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path))
{
- if (!path.StartsWith(basePath))
+ if (!path.StartsWith(basePath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid image path.");
}
@@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers
if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path))
{
- if (!path.StartsWith(basePath))
+ if (!path.StartsWith(basePath, StringComparison.InvariantCulture))
{
return BadRequest("Invalid image path.");
}
diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs
index 448510c06a..8a6f9b8c75 100644
--- a/Jellyfin.Api/Controllers/ItemLookupController.cs
+++ b/Jellyfin.Api/Controllers/ItemLookupController.cs
@@ -5,8 +5,6 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
@@ -30,7 +28,6 @@ namespace Jellyfin.Api.Controllers
public class ItemLookupController : BaseJellyfinApiController
{
private readonly IProviderManager _providerManager;
- private readonly IServerApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly ILogger<ItemLookupController> _logger;
@@ -39,19 +36,16 @@ namespace Jellyfin.Api.Controllers
/// Initializes a new instance of the <see cref="ItemLookupController"/> class.
/// </summary>
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
- /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{ItemLookupController}"/> interface.</param>
public ItemLookupController(
IProviderManager providerManager,
- IServerConfigurationManager serverConfigurationManager,
IFileSystem fileSystem,
ILibraryManager libraryManager,
ILogger<ItemLookupController> logger)
{
_providerManager = providerManager;
- _appPaths = serverConfigurationManager.ApplicationPaths;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_logger = logger;
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index f0d44e5cc8..45a36c8fe1 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -10,6 +10,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
@@ -33,6 +34,7 @@ namespace Jellyfin.Api.Controllers
private readonly ILocalizationManager _localization;
private readonly IDtoService _dtoService;
private readonly ILogger<ItemsController> _logger;
+ private readonly ISessionManager _sessionManager;
/// <summary>
/// Initializes a new instance of the <see cref="ItemsController"/> class.
@@ -42,18 +44,21 @@ namespace Jellyfin.Api.Controllers
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
+ /// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
public ItemsController(
IUserManager userManager,
ILibraryManager libraryManager,
ILocalizationManager localization,
IDtoService dtoService,
- ILogger<ItemsController> logger)
+ ILogger<ItemsController> logger,
+ ISessionManager sessionManager)
{
_userManager = userManager;
_libraryManager = libraryManager;
_localization = localization;
_dtoService = dtoService;
_logger = logger;
+ _sessionManager = sessionManager;
}
/// <summary>
@@ -763,6 +768,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimited.</param>
/// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
+ /// <param name="excludeActiveSessions">Optional. Whether to exclude the currently active sessions.</param>
/// <response code="200">Items returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items that are resumable.</returns>
[HttpGet("Users/{userId}/Items/Resume")]
@@ -781,7 +787,8 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery] bool enableTotalRecordCount = true,
- [FromQuery] bool? enableImages = true)
+ [FromQuery] bool? enableImages = true,
+ [FromQuery] bool excludeActiveSessions = false)
{
var user = _userManager.GetUserById(userId);
var parentIdGuid = parentId ?? Guid.Empty;
@@ -801,6 +808,15 @@ namespace Jellyfin.Api.Controllers
.ToArray();
}
+ var excludeItemIds = Array.Empty<Guid>();
+ if (excludeActiveSessions)
+ {
+ excludeItemIds = _sessionManager.Sessions
+ .Where(s => s.UserId == userId && s.NowPlayingItem != null)
+ .Select(s => s.NowPlayingItem.Id)
+ .ToArray();
+ }
+
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) },
@@ -817,7 +833,8 @@ namespace Jellyfin.Api.Controllers
AncestorIds = ancestorIds,
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
- SearchTerm = searchTerm
+ SearchTerm = searchTerm,
+ ExcludeItemIds = excludeItemIds
});
var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, dtoOptions, user);
diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs
index b98307f879..cb4894d771 100644
--- a/Jellyfin.Api/Controllers/PersonsController.cs
+++ b/Jellyfin.Api/Controllers/PersonsController.cs
@@ -26,7 +26,6 @@ namespace Jellyfin.Api.Controllers
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IUserManager _userManager;
- private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="PersonsController"/> class.
@@ -34,17 +33,14 @@ namespace Jellyfin.Api.Controllers
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
- /// <param name="userDataManager">Instance of the <see cref="IUserDataManager"/> interface.</param>
public PersonsController(
ILibraryManager libraryManager,
IDtoService dtoService,
- IUserManager userManager,
- IUserDataManager userDataManager)
+ IUserManager userManager)
{
_libraryManager = libraryManager;
_dtoService = dtoService;
_userManager = userManager;
- _userDataManager = userDataManager;
}
/// <summary>
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index 0ae6109bcc..0778ea3fc5 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -28,7 +28,6 @@ namespace Jellyfin.Api.Controllers
{
private readonly IInstallationManager _installationManager;
private readonly IPluginManager _pluginManager;
- private readonly IConfigurationManager _config;
private readonly JsonSerializerOptions _serializerOptions;
/// <summary>
@@ -36,16 +35,13 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
/// <param name="pluginManager">Instance of the <see cref="IPluginManager"/> interface.</param>
- /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
public PluginsController(
IInstallationManager installationManager,
- IPluginManager pluginManager,
- IConfigurationManager config)
+ IPluginManager pluginManager)
{
_installationManager = installationManager;
_pluginManager = pluginManager;
_serializerOptions = JsonDefaults.Options;
- _config = config;
}
/// <summary>
diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs
index 35921ede8f..773cff1ac6 100644
--- a/Jellyfin.Api/Controllers/RemoteImageController.cs
+++ b/Jellyfin.Api/Controllers/RemoteImageController.cs
@@ -30,7 +30,6 @@ namespace Jellyfin.Api.Controllers
{
private readonly IProviderManager _providerManager;
private readonly IServerApplicationPaths _applicationPaths;
- private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
/// <summary>
@@ -38,17 +37,14 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
/// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
- /// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
public RemoteImageController(
IProviderManager providerManager,
IServerApplicationPaths applicationPaths,
- IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager)
{
_providerManager = providerManager;
_applicationPaths = applicationPaths;
- _httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
}
diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs
index db8307f284..16acedcf35 100644
--- a/Jellyfin.Api/Controllers/SubtitleController.cs
+++ b/Jellyfin.Api/Controllers/SubtitleController.cs
@@ -528,7 +528,7 @@ namespace Jellyfin.Api.Controllers
if (fontFile != null && fileSize != null && fileSize > 0)
{
- _logger.LogDebug("Fallback font size is {fileSize} Bytes", fileSize);
+ _logger.LogDebug("Fallback font size is {FileSize} Bytes", fileSize);
return PhysicalFile(fontFile.FullName, MimeTypes.GetMimeType(fontFile.FullName));
}
else
diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs
index 904738bb45..2ff85fd2ae 100644
--- a/Jellyfin.Api/Controllers/SystemController.cs
+++ b/Jellyfin.Api/Controllers/SystemController.cs
@@ -212,10 +212,13 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="IEnumerable{WakeOnLanInfo}"/> with the WakeOnLan infos.</returns>
[HttpGet("WakeOnLanInfo")]
[Authorize(Policy = Policies.DefaultAuthorization)]
+ [Obsolete("This endpoint is obsolete.")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<WakeOnLanInfo>> GetWakeOnLanInfo()
{
- var result = _appHost.GetWakeOnLanInfo();
+ var result = _network.GetMacAddresses()
+ .Select(i => new WakeOnLanInfo(i))
+ .ToList();
return Ok(result);
}
}
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index e1cbc6f331..3c079a71dc 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -451,7 +451,7 @@ namespace Jellyfin.Api.Controllers
if (@static.HasValue && @static.Value && state.DirectStreamProvider != null)
{
- StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
+ StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfo(streamingRequest.LiveStreamId);
if (liveStreamInfo == null)
@@ -467,7 +467,7 @@ namespace Jellyfin.Api.Controllers
// Static remote stream
if (@static.HasValue && @static.Value && state.InputProtocol == MediaProtocol.Http)
{
- StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, startTimeTicks, Request, _dlnaManager);
+ StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, isHeadRequest, httpClient, HttpContext).ConfigureAwait(false);
@@ -484,7 +484,7 @@ namespace Jellyfin.Api.Controllers
var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
var isTranscodeCached = outputPathExists && transcodingJob != null;
- StreamingHelpers.AddDlnaHeaders(state, Response.Headers, (@static.HasValue && @static.Value) || isTranscodeCached, startTimeTicks, Request, _dlnaManager);
+ StreamingHelpers.AddDlnaHeaders(state, Response.Headers, (@static.HasValue && @static.Value) || isTranscodeCached, state.Request.StartTimeTicks, Request, _dlnaManager);
// Static stream
if (@static.HasValue && @static.Value)
diff --git a/Jellyfin.Api/Helpers/ClassMigrationHelper.cs b/Jellyfin.Api/Helpers/ClassMigrationHelper.cs
index a911a33241..76fb27bcc3 100644
--- a/Jellyfin.Api/Helpers/ClassMigrationHelper.cs
+++ b/Jellyfin.Api/Helpers/ClassMigrationHelper.cs
@@ -19,7 +19,7 @@ namespace Jellyfin.Api.Helpers
// If any this null throw an exception.
if (source == null || destination == null)
{
- throw new Exception("Source or/and Destination Objects are null");
+ throw new ArgumentException("Source or/and Destination Objects are null");
}
// Getting the Types of the objects.
diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
index 61e18220a1..3fa07720ae 100644
--- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
+++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
@@ -17,7 +17,6 @@ namespace Jellyfin.Api.Helpers
private readonly TranscodingJobDto? _job;
private readonly TranscodingJobHelper? _transcodingJobHelper;
private readonly int _timeoutMs;
- private int _bytesWritten;
private bool _disposed;
/// <summary>
@@ -71,53 +70,58 @@ namespace Jellyfin.Api.Helpers
/// <inheritdoc />
public override void Flush()
{
- _stream.Flush();
+ // Not supported
}
/// <inheritdoc />
public override int Read(byte[] buffer, int offset, int count)
+ => Read(buffer.AsSpan(offset, count));
+
+ /// <inheritdoc />
+ public override int Read(Span<byte> buffer)
{
- return _stream.Read(buffer, offset, count);
+ int totalBytesRead = 0;
+ var stopwatch = Stopwatch.StartNew();
+
+ while (KeepReading(stopwatch.ElapsedMilliseconds))
+ {
+ totalBytesRead += _stream.Read(buffer);
+ if (totalBytesRead > 0)
+ {
+ break;
+ }
+
+ Thread.Sleep(50);
+ }
+
+ UpdateBytesWritten(totalBytesRead);
+
+ return totalBytesRead;
}
/// <inheritdoc />
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ => await ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
+
+ /// <inheritdoc />
+ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
int totalBytesRead = 0;
- int remainingBytesToRead = count;
var stopwatch = Stopwatch.StartNew();
- int newOffset = offset;
- while (remainingBytesToRead > 0)
+ while (KeepReading(stopwatch.ElapsedMilliseconds))
{
- cancellationToken.ThrowIfCancellationRequested();
- int bytesRead = await _stream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
-
- remainingBytesToRead -= bytesRead;
- newOffset += bytesRead;
-
- if (bytesRead > 0)
+ totalBytesRead += await _stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
+ if (totalBytesRead > 0)
{
- _bytesWritten += bytesRead;
- totalBytesRead += bytesRead;
-
- if (_job != null)
- {
- _job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
- }
+ break;
}
- else
- {
- // If the job is null it's a live stream and will require user action to close, but don't keep it open indefinitely
- if (_job?.HasExited ?? stopwatch.ElapsedMilliseconds > _timeoutMs)
- {
- break;
- }
- await Task.Delay(50, cancellationToken).ConfigureAwait(false);
- }
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
+ UpdateBytesWritten(totalBytesRead);
+
return totalBytesRead;
}
@@ -159,5 +163,19 @@ namespace Jellyfin.Api.Helpers
base.Dispose(disposing);
}
}
+
+ private void UpdateBytesWritten(int totalBytesRead)
+ {
+ if (_job != null)
+ {
+ _job.BytesDownloaded += totalBytesRead;
+ }
+ }
+
+ private bool KeepReading(long elapsed)
+ {
+ // If the job is null it's a live stream and will require user action to close, but don't keep it open indefinitely
+ return !_job?.HasExited ?? elapsed < _timeoutMs;
+ }
}
}
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index 4fc791665e..1b8f24c27d 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -148,7 +148,7 @@ namespace Jellyfin.Api.Helpers
mediaSource = string.IsNullOrEmpty(streamingRequest.MediaSourceId)
? mediaSources[0]
- : mediaSources.Find(i => string.Equals(i.Id, streamingRequest.MediaSourceId, StringComparison.InvariantCulture));
+ : mediaSources.Find(i => string.Equals(i.Id, streamingRequest.MediaSourceId, StringComparison.Ordinal));
if (mediaSource == null && Guid.Parse(streamingRequest.MediaSourceId) == streamingRequest.Id)
{
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 07d0b55433..9d80070ebf 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -283,6 +283,7 @@ namespace Jellyfin.Api.Helpers
lock (job.ProcessLock!)
{
+ #pragma warning disable CA1849 // Can't await in lock block
job.TranscodingThrottler?.Stop().GetAwaiter().GetResult();
var process = job.Process;
@@ -308,6 +309,7 @@ namespace Jellyfin.Api.Helpers
{
}
}
+ #pragma warning restore CA1849
}
if (delete(job.Path!))
@@ -541,8 +543,7 @@ namespace Jellyfin.Api.Helpers
state,
cancellationTokenSource);
- var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
- _logger.LogInformation(commandLineLogMessage);
+ _logger.LogInformation("{Filename} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
var logFilePrefix = "FFmpeg.Transcode-";
if (state.VideoRequest != null
@@ -560,8 +561,9 @@ namespace Jellyfin.Api.Helpers
// FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
+ var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
- await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
+ await logStream.WriteAsync(commandLineLogMessageBytes, cancellationTokenSource.Token).ConfigureAwait(false);
process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index 57480b2f35..a3598edfa4 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -13,8 +13,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.2.3" />
</ItemGroup>
diff --git a/Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs b/Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs
new file mode 100644
index 0000000000..44509a9c04
--- /dev/null
+++ b/Jellyfin.Api/Models/ClientLogDtos/ClientLogDocumentResponseDto.cs
@@ -0,0 +1,22 @@
+namespace Jellyfin.Api.Models.ClientLogDtos
+{
+ /// <summary>
+ /// Client log document response dto.
+ /// </summary>
+ public class ClientLogDocumentResponseDto
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientLogDocumentResponseDto"/> class.
+ /// </summary>
+ /// <param name="fileName">The file name.</param>
+ public ClientLogDocumentResponseDto(string fileName)
+ {
+ FileName = fileName;
+ }
+
+ /// <summary>
+ /// Gets the resulting filename.
+ /// </summary>
+ public string FileName { get; }
+ }
+}
diff --git a/Jellyfin.Api/Models/DisplayPreferencesDtos/DisplayPreferencesDto.cs b/Jellyfin.Api/Models/DisplayPreferencesDtos/DisplayPreferencesDto.cs
deleted file mode 100644
index 249d828d33..0000000000
--- a/Jellyfin.Api/Models/DisplayPreferencesDtos/DisplayPreferencesDto.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using System.Collections.Generic;
-using Jellyfin.Data.Enums;
-
-namespace Jellyfin.Api.Models.DisplayPreferencesDtos
-{
- /// <summary>
- /// Defines the display preferences for any item that supports them (usually Folders).
- /// </summary>
- public class DisplayPreferencesDto
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="DisplayPreferencesDto" /> class.
- /// </summary>
- public DisplayPreferencesDto()
- {
- RememberIndexing = false;
- PrimaryImageHeight = 250;
- PrimaryImageWidth = 250;
- ShowBackdrop = true;
- CustomPrefs = new Dictionary<string, string>();
- }
-
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- /// <value>The user id.</value>
- public string? Id { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the view.
- /// </summary>
- /// <value>The type of the view.</value>
- public string? ViewType { get; set; }
-
- /// <summary>
- /// Gets or sets the sort by.
- /// </summary>
- /// <value>The sort by.</value>
- public string? SortBy { get; set; }
-
- /// <summary>
- /// Gets or sets the index by.
- /// </summary>
- /// <value>The index by.</value>
- public string? IndexBy { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [remember indexing].
- /// </summary>
- /// <value><c>true</c> if [remember indexing]; otherwise, <c>false</c>.</value>
- public bool RememberIndexing { get; set; }
-
- /// <summary>
- /// Gets or sets the height of the primary image.
- /// </summary>
- /// <value>The height of the primary image.</value>
- public int PrimaryImageHeight { get; set; }
-
- /// <summary>
- /// Gets or sets the width of the primary image.
- /// </summary>
- /// <value>The width of the primary image.</value>
- public int PrimaryImageWidth { get; set; }
-
- /// <summary>
- /// Gets the custom prefs.
- /// </summary>
- /// <value>The custom prefs.</value>
- public Dictionary<string, string> CustomPrefs { get; }
-
- /// <summary>
- /// Gets or sets the scroll direction.
- /// </summary>
- /// <value>The scroll direction.</value>
- public ScrollDirection ScrollDirection { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether to show backdrops on this item.
- /// </summary>
- /// <value><c>true</c> if showing backdrops; otherwise, <c>false</c>.</value>
- public bool ShowBackdrop { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [remember sorting].
- /// </summary>
- /// <value><c>true</c> if [remember sorting]; otherwise, <c>false</c>.</value>
- public bool RememberSorting { get; set; }
-
- /// <summary>
- /// Gets or sets the sort order.
- /// </summary>
- /// <value>The sort order.</value>
- public SortOrder SortOrder { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [show sidebar].
- /// </summary>
- /// <value><c>true</c> if [show sidebar]; otherwise, <c>false</c>.</value>
- public bool ShowSidebar { get; set; }
-
- /// <summary>
- /// Gets or sets the client.
- /// </summary>
- public string? Client { get; set; }
- }
-}
diff --git a/Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs b/Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs
index fed837b856..ab67c8732a 100644
--- a/Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs
+++ b/Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs
@@ -134,7 +134,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
/// <summary>
/// Gets or sets bytes downloaded.
/// </summary>
- public long? BytesDownloaded { get; set; }
+ public long BytesDownloaded { get; set; }
/// <summary>
/// Gets or sets bytes transcoded.
diff --git a/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs b/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs
index 7b32d76ba7..7a1ca252cf 100644
--- a/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs
+++ b/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs
@@ -141,7 +141,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
private bool IsThrottleAllowed(TranscodingJobDto job, int thresholdSeconds)
{
- var bytesDownloaded = job.BytesDownloaded ?? 0;
+ var bytesDownloaded = job.BytesDownloaded;
var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0;
var downloadPositionTicks = job.DownloadPositionTicks ?? 0;
@@ -197,7 +197,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
}
}
- _logger.LogDebug("No throttle data for " + path);
+ _logger.LogDebug("No throttle data for {Path}", path);
return false;
}