aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api/Controllers
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Api/Controllers')
-rw-r--r--Jellyfin.Api/Controllers/ActivityLogController.cs1
-rw-r--r--Jellyfin.Api/Controllers/ApiKeyController.cs1
-rw-r--r--Jellyfin.Api/Controllers/ArtistsController.cs8
-rw-r--r--Jellyfin.Api/Controllers/AudioController.cs7
-rw-r--r--Jellyfin.Api/Controllers/ChannelsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/CollectionController.cs1
-rw-r--r--Jellyfin.Api/Controllers/ConfigurationController.cs1
-rw-r--r--Jellyfin.Api/Controllers/DevicesController.cs1
-rw-r--r--Jellyfin.Api/Controllers/DlnaController.cs132
-rw-r--r--Jellyfin.Api/Controllers/DlnaServerController.cs329
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs44
-rw-r--r--Jellyfin.Api/Controllers/EnvironmentController.cs3
-rw-r--r--Jellyfin.Api/Controllers/FilterController.cs2
-rw-r--r--Jellyfin.Api/Controllers/GenresController.cs6
-rw-r--r--Jellyfin.Api/Controllers/HlsSegmentController.cs2
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs19
-rw-r--r--Jellyfin.Api/Controllers/ItemLookupController.cs1
-rw-r--r--Jellyfin.Api/Controllers/ItemRefreshController.cs1
-rw-r--r--Jellyfin.Api/Controllers/ItemUpdateController.cs10
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs80
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs29
-rw-r--r--Jellyfin.Api/Controllers/LibraryStructureController.cs1
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs5
-rw-r--r--Jellyfin.Api/Controllers/LocalizationController.cs1
-rw-r--r--Jellyfin.Api/Controllers/MusicGenresController.cs4
-rw-r--r--Jellyfin.Api/Controllers/PackageController.cs1
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs3
-rw-r--r--Jellyfin.Api/Controllers/PluginsController.cs1
-rw-r--r--Jellyfin.Api/Controllers/RemoteImageController.cs1
-rw-r--r--Jellyfin.Api/Controllers/ScheduledTasksController.cs1
-rw-r--r--Jellyfin.Api/Controllers/SearchController.cs2
-rw-r--r--Jellyfin.Api/Controllers/SessionController.cs15
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs3
-rw-r--r--Jellyfin.Api/Controllers/SubtitleController.cs7
-rw-r--r--Jellyfin.Api/Controllers/SuggestionsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/SyncPlayController.cs1
-rw-r--r--Jellyfin.Api/Controllers/SystemController.cs1
-rw-r--r--Jellyfin.Api/Controllers/TrailersController.cs4
-rw-r--r--Jellyfin.Api/Controllers/TrickplayController.cs101
-rw-r--r--Jellyfin.Api/Controllers/TvShowsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs1
-rw-r--r--Jellyfin.Api/Controllers/UserViewsController.cs3
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs26
-rw-r--r--Jellyfin.Api/Controllers/YearsController.cs8
44 files changed, 281 insertions, 597 deletions
diff --git a/Jellyfin.Api/Controllers/ActivityLogController.cs b/Jellyfin.Api/Controllers/ActivityLogController.cs
index c3d02976eb..a19a203b51 100644
--- a/Jellyfin.Api/Controllers/ActivityLogController.cs
+++ b/Jellyfin.Api/Controllers/ActivityLogController.cs
@@ -2,6 +2,7 @@ using System;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Data.Queries;
+using MediaBrowser.Common.Api;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Controllers/ApiKeyController.cs b/Jellyfin.Api/Controllers/ApiKeyController.cs
index 991f8cbf20..3363d7bad2 100644
--- a/Jellyfin.Api/Controllers/ApiKeyController.cs
+++ b/Jellyfin.Api/Controllers/ApiKeyController.cs
@@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs
index c9d2f67f92..e7d3e694ab 100644
--- a/Jellyfin.Api/Controllers/ArtistsController.cs
+++ b/Jellyfin.Api/Controllers/ArtistsController.cs
@@ -95,7 +95,7 @@ public class ArtistsController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
@@ -113,7 +113,7 @@ public class ArtistsController : BaseJellyfinApiController
[FromQuery] string? nameStartsWithOrGreater,
[FromQuery] string? nameStartsWith,
[FromQuery] string? nameLessThan,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] bool? enableImages = true,
[FromQuery] bool enableTotalRecordCount = true)
@@ -299,7 +299,7 @@ public class ArtistsController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
@@ -317,7 +317,7 @@ public class ArtistsController : BaseJellyfinApiController
[FromQuery] string? nameStartsWithOrGreater,
[FromQuery] string? nameStartsWith,
[FromQuery] string? nameLessThan,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] bool? enableImages = true,
[FromQuery] bool enableTotalRecordCount = true)
diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs
index 968193a6f8..5bc5330861 100644
--- a/Jellyfin.Api/Controllers/AudioController.cs
+++ b/Jellyfin.Api/Controllers/AudioController.cs
@@ -15,7 +15,6 @@ namespace Jellyfin.Api.Controllers;
/// <summary>
/// The audio controller.
/// </summary>
-// TODO: In order to authenticate this in the future, Dlna playback will require updating
public class AudioController : BaseJellyfinApiController
{
private readonly AudioHelper _audioHelper;
@@ -95,7 +94,7 @@ public class AudioController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -147,7 +146,6 @@ public class AudioController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -260,7 +258,7 @@ public class AudioController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -312,7 +310,6 @@ public class AudioController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index 11c4ac3768..fdc16ee23f 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -122,7 +122,7 @@ public class ChannelsController : BaseJellyfinApiController
[FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
{
userId = RequestHelpers.GetUserId(User, userId);
diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs
index 2db04afb80..2d9f1ed69a 100644
--- a/Jellyfin.Api/Controllers/CollectionController.cs
+++ b/Jellyfin.Api/Controllers/CollectionController.cs
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Model.Collections;
diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs
index 9007dfc410..8db22f7ebe 100644
--- a/Jellyfin.Api/Controllers/ConfigurationController.cs
+++ b/Jellyfin.Api/Controllers/ConfigurationController.cs
@@ -6,6 +6,7 @@ using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Models.ConfigurationDtos;
using Jellyfin.Extensions.Json;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs
index aa0dff2123..aa200a7221 100644
--- a/Jellyfin.Api/Controllers/DevicesController.cs
+++ b/Jellyfin.Api/Controllers/DevicesController.cs
@@ -6,6 +6,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Data.Dtos;
using Jellyfin.Data.Entities.Security;
using Jellyfin.Data.Queries;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Devices;
diff --git a/Jellyfin.Api/Controllers/DlnaController.cs b/Jellyfin.Api/Controllers/DlnaController.cs
deleted file mode 100644
index 415385463d..0000000000
--- a/Jellyfin.Api/Controllers/DlnaController.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using Jellyfin.Api.Constants;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Dlna;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-
-namespace Jellyfin.Api.Controllers;
-
-/// <summary>
-/// Dlna Controller.
-/// </summary>
-[Authorize(Policy = Policies.RequiresElevation)]
-public class DlnaController : BaseJellyfinApiController
-{
- private readonly IDlnaManager _dlnaManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DlnaController"/> class.
- /// </summary>
- /// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
- public DlnaController(IDlnaManager dlnaManager)
- {
- _dlnaManager = dlnaManager;
- }
-
- /// <summary>
- /// Get profile infos.
- /// </summary>
- /// <response code="200">Device profile infos returned.</response>
- /// <returns>An <see cref="OkResult"/> containing the device profile infos.</returns>
- [HttpGet("ProfileInfos")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<IEnumerable<DeviceProfileInfo>> GetProfileInfos()
- {
- return Ok(_dlnaManager.GetProfileInfos());
- }
-
- /// <summary>
- /// Gets the default profile.
- /// </summary>
- /// <response code="200">Default device profile returned.</response>
- /// <returns>An <see cref="OkResult"/> containing the default profile.</returns>
- [HttpGet("Profiles/Default")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<DeviceProfile> GetDefaultProfile()
- {
- return _dlnaManager.GetDefaultProfile();
- }
-
- /// <summary>
- /// Gets a single profile.
- /// </summary>
- /// <param name="profileId">Profile Id.</param>
- /// <response code="200">Device profile returned.</response>
- /// <response code="404">Device profile not found.</response>
- /// <returns>An <see cref="OkResult"/> containing the profile on success, or a <see cref="NotFoundResult"/> if device profile not found.</returns>
- [HttpGet("Profiles/{profileId}")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult<DeviceProfile> GetProfile([FromRoute, Required] string profileId)
- {
- var profile = _dlnaManager.GetProfile(profileId);
- if (profile is null)
- {
- return NotFound();
- }
-
- return profile;
- }
-
- /// <summary>
- /// Deletes a profile.
- /// </summary>
- /// <param name="profileId">Profile id.</param>
- /// <response code="204">Device profile deleted.</response>
- /// <response code="404">Device profile not found.</response>
- /// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if profile not found.</returns>
- [HttpDelete("Profiles/{profileId}")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult DeleteProfile([FromRoute, Required] string profileId)
- {
- var existingDeviceProfile = _dlnaManager.GetProfile(profileId);
- if (existingDeviceProfile is null)
- {
- return NotFound();
- }
-
- _dlnaManager.DeleteProfile(profileId);
- return NoContent();
- }
-
- /// <summary>
- /// Creates a profile.
- /// </summary>
- /// <param name="deviceProfile">Device profile.</param>
- /// <response code="204">Device profile created.</response>
- /// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("Profiles")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult CreateProfile([FromBody] DeviceProfile deviceProfile)
- {
- _dlnaManager.CreateProfile(deviceProfile);
- return NoContent();
- }
-
- /// <summary>
- /// Updates a profile.
- /// </summary>
- /// <param name="profileId">Profile id.</param>
- /// <param name="deviceProfile">Device profile.</param>
- /// <response code="204">Device profile updated.</response>
- /// <response code="404">Device profile not found.</response>
- /// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if profile not found.</returns>
- [HttpPost("Profiles/{profileId}")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult UpdateProfile([FromRoute, Required] string profileId, [FromBody] DeviceProfile deviceProfile)
- {
- var existingDeviceProfile = _dlnaManager.GetProfile(profileId);
- if (existingDeviceProfile is null)
- {
- return NotFound();
- }
-
- _dlnaManager.UpdateProfile(profileId, deviceProfile);
- return NoContent();
- }
-}
diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs
deleted file mode 100644
index 42576934b3..0000000000
--- a/Jellyfin.Api/Controllers/DlnaServerController.cs
+++ /dev/null
@@ -1,329 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Net.Mime;
-using System.Threading.Tasks;
-using Emby.Dlna;
-using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Net;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-
-namespace Jellyfin.Api.Controllers;
-
-/// <summary>
-/// Dlna Server Controller.
-/// </summary>
-[Route("Dlna")]
-[DlnaEnabled]
-[Authorize(Policy = Policies.AnonymousLanAccessPolicy)]
-public class DlnaServerController : BaseJellyfinApiController
-{
- private readonly IDlnaManager _dlnaManager;
- private readonly IContentDirectory _contentDirectory;
- private readonly IConnectionManager _connectionManager;
- private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DlnaServerController"/> class.
- /// </summary>
- /// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
- /// <param name="contentDirectory">Instance of the <see cref="IContentDirectory"/> interface.</param>
- /// <param name="connectionManager">Instance of the <see cref="IConnectionManager"/> interface.</param>
- /// <param name="mediaReceiverRegistrar">Instance of the <see cref="IMediaReceiverRegistrar"/> interface.</param>
- public DlnaServerController(
- IDlnaManager dlnaManager,
- IContentDirectory contentDirectory,
- IConnectionManager connectionManager,
- IMediaReceiverRegistrar mediaReceiverRegistrar)
- {
- _dlnaManager = dlnaManager;
- _contentDirectory = contentDirectory;
- _connectionManager = connectionManager;
- _mediaReceiverRegistrar = mediaReceiverRegistrar;
- }
-
- /// <summary>
- /// Get Description Xml.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Description xml returned.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>An <see cref="OkResult"/> containing the description xml.</returns>
- [HttpGet("{serverId}/description")]
- [HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- public ActionResult<string> GetDescriptionXml([FromRoute, Required] string serverId)
- {
- var url = GetAbsoluteUri();
- var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
- var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress);
- return Ok(xml);
- }
-
- /// <summary>
- /// Gets Dlna content directory xml.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Dlna content directory returned.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>An <see cref="OkResult"/> containing the dlna content directory xml.</returns>
- [HttpGet("{serverId}/ContentDirectory")]
- [HttpGet("{serverId}/ContentDirectory/ContentDirectory", Name = "GetContentDirectory_2")]
- [HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_3")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
- public ActionResult<string> GetContentDirectory([FromRoute, Required] string serverId)
- {
- return Ok(_contentDirectory.GetServiceXml());
- }
-
- /// <summary>
- /// Gets Dlna media receiver registrar xml.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Dlna media receiver registrar xml returned.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Dlna media receiver registrar xml.</returns>
- [HttpGet("{serverId}/MediaReceiverRegistrar")]
- [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar", Name = "GetMediaReceiverRegistrar_2")]
- [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_3")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
- public ActionResult<string> GetMediaReceiverRegistrar([FromRoute, Required] string serverId)
- {
- return Ok(_mediaReceiverRegistrar.GetServiceXml());
- }
-
- /// <summary>
- /// Gets Dlna media receiver registrar xml.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Dlna media receiver registrar xml returned.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Dlna media receiver registrar xml.</returns>
- [HttpGet("{serverId}/ConnectionManager")]
- [HttpGet("{serverId}/ConnectionManager/ConnectionManager", Name = "GetConnectionManager_2")]
- [HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_3")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
- public ActionResult<string> GetConnectionManager([FromRoute, Required] string serverId)
- {
- return Ok(_connectionManager.GetServiceXml());
- }
-
- /// <summary>
- /// Process a content directory control request.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Request processed.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Control response.</returns>
- [HttpPost("{serverId}/ContentDirectory/Control")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- public async Task<ActionResult<ControlResponse>> ProcessContentDirectoryControlRequest([FromRoute, Required] string serverId)
- {
- return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Process a connection manager control request.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Request processed.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Control response.</returns>
- [HttpPost("{serverId}/ConnectionManager/Control")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- public async Task<ActionResult<ControlResponse>> ProcessConnectionManagerControlRequest([FromRoute, Required] string serverId)
- {
- return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Process a media receiver registrar control request.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Request processed.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Control response.</returns>
- [HttpPost("{serverId}/MediaReceiverRegistrar/Control")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- public async Task<ActionResult<ControlResponse>> ProcessMediaReceiverRegistrarControlRequest([FromRoute, Required] string serverId)
- {
- return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Processes an event subscription request.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Request processed.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Event subscription response.</returns>
- [HttpSubscribe("{serverId}/MediaReceiverRegistrar/Events")]
- [HttpUnsubscribe("{serverId}/MediaReceiverRegistrar/Events")]
- [ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- public ActionResult<EventSubscriptionResponse> ProcessMediaReceiverRegistrarEventRequest(string serverId)
- {
- return ProcessEventRequest(_mediaReceiverRegistrar);
- }
-
- /// <summary>
- /// Processes an event subscription request.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Request processed.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Event subscription response.</returns>
- [HttpSubscribe("{serverId}/ContentDirectory/Events")]
- [HttpUnsubscribe("{serverId}/ContentDirectory/Events")]
- [ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- public ActionResult<EventSubscriptionResponse> ProcessContentDirectoryEventRequest(string serverId)
- {
- return ProcessEventRequest(_contentDirectory);
- }
-
- /// <summary>
- /// Processes an event subscription request.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <response code="200">Request processed.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Event subscription response.</returns>
- [HttpSubscribe("{serverId}/ConnectionManager/Events")]
- [HttpUnsubscribe("{serverId}/ConnectionManager/Events")]
- [ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [Produces(MediaTypeNames.Text.Xml)]
- [ProducesFile(MediaTypeNames.Text.Xml)]
- public ActionResult<EventSubscriptionResponse> ProcessConnectionManagerEventRequest(string serverId)
- {
- return ProcessEventRequest(_connectionManager);
- }
-
- /// <summary>
- /// Gets a server icon.
- /// </summary>
- /// <param name="serverId">Server UUID.</param>
- /// <param name="fileName">The icon filename.</param>
- /// <response code="200">Request processed.</response>
- /// <response code="404">Not Found.</response>
- /// <response code="503">DLNA is disabled.</response>
- /// <returns>Icon stream.</returns>
- [HttpGet("{serverId}/icons/{fileName}")]
- [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [ProducesImageFile]
- public ActionResult GetIconId([FromRoute, Required] string serverId, [FromRoute, Required] string fileName)
- {
- return GetIconInternal(fileName);
- }
-
- /// <summary>
- /// Gets a server icon.
- /// </summary>
- /// <param name="fileName">The icon filename.</param>
- /// <returns>Icon stream.</returns>
- /// <response code="200">Request processed.</response>
- /// <response code="404">Not Found.</response>
- /// <response code="503">DLNA is disabled.</response>
- [HttpGet("icons/{fileName}")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
- [ProducesImageFile]
- public ActionResult GetIcon([FromRoute, Required] string fileName)
- {
- return GetIconInternal(fileName);
- }
-
- private ActionResult GetIconInternal(string fileName)
- {
- var icon = _dlnaManager.GetIcon(fileName);
- if (icon is null)
- {
- return NotFound();
- }
-
- return File(icon.Stream, MimeTypes.GetMimeType(fileName));
- }
-
- private string GetAbsoluteUri()
- {
- return $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Request.Path}";
- }
-
- private Task<ControlResponse> ProcessControlRequestInternalAsync(string id, Stream requestStream, IUpnpService service)
- {
- return service.ProcessControlRequestAsync(new ControlRequest(Request.Headers)
- {
- InputXml = requestStream,
- TargetServerUuId = id,
- RequestedUrl = GetAbsoluteUri()
- });
- }
-
- private EventSubscriptionResponse ProcessEventRequest(IDlnaEventManager dlnaEventManager)
- {
- var subscriptionId = Request.Headers["SID"];
- if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase))
- {
- var notificationType = Request.Headers["NT"];
- var callback = Request.Headers["CALLBACK"];
- var timeoutString = Request.Headers["TIMEOUT"];
-
- if (string.IsNullOrEmpty(notificationType))
- {
- return dlnaEventManager.RenewEventSubscription(
- subscriptionId,
- notificationType,
- timeoutString,
- callback);
- }
-
- return dlnaEventManager.CreateEventSubscription(notificationType, timeoutString, callback);
- }
-
- return dlnaEventManager.CancelEventSubscription(subscriptionId);
- }
-}
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 42c94c29d3..9e9c610cc5 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -17,8 +17,6 @@ using Jellyfin.Extensions;
using Jellyfin.MediaEncoding.Hls.Playlist;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Encoder;
@@ -49,12 +47,10 @@ public class DynamicHlsController : BaseJellyfinApiController
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
- private readonly IDlnaManager _dlnaManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
- private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly ILogger<DynamicHlsController> _logger;
private readonly EncodingHelper _encodingHelper;
@@ -67,12 +63,10 @@ public class DynamicHlsController : BaseJellyfinApiController
/// </summary>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
- /// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
- /// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
/// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param>
/// <param name="dynamicHlsHelper">Instance of <see cref="DynamicHlsHelper"/>.</param>
@@ -81,12 +75,10 @@ public class DynamicHlsController : BaseJellyfinApiController
public DynamicHlsController(
ILibraryManager libraryManager,
IUserManager userManager,
- IDlnaManager dlnaManager,
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
IFileSystem fileSystem,
- IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
ILogger<DynamicHlsController> logger,
DynamicHlsHelper dynamicHlsHelper,
@@ -95,12 +87,10 @@ public class DynamicHlsController : BaseJellyfinApiController
{
_libraryManager = libraryManager;
_userManager = userManager;
- _dlnaManager = dlnaManager;
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
_fileSystem = fileSystem;
- _deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_logger = logger;
_dynamicHlsHelper = dynamicHlsHelper;
@@ -176,7 +166,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -231,7 +221,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -294,8 +283,6 @@ public class DynamicHlsController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
- _dlnaManager,
- _deviceManager,
_transcodingJobHelper,
TranscodingJobType,
cancellationToken)
@@ -410,6 +397,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
+ /// <param name="enableTrickplay">Enable trickplay image playlists being added to master playlist.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
[HttpGet("Videos/{itemId}/master.m3u8")]
@@ -421,7 +409,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -467,7 +455,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary<string, string> streamOptions,
- [FromQuery] bool enableAdaptiveBitrateStreaming = true)
+ [FromQuery] bool enableAdaptiveBitrateStreaming = true,
+ [FromQuery] bool enableTrickplay = true)
{
var streamingRequest = new HlsVideoRequestDto
{
@@ -475,7 +464,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -521,7 +509,8 @@ public class DynamicHlsController : BaseJellyfinApiController
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
- EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming
+ EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming,
+ EnableTrickplay = enableTrickplay
};
return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
@@ -591,7 +580,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -644,7 +633,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -757,7 +745,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -811,7 +799,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -925,7 +912,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -978,7 +965,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -1102,7 +1088,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -1158,7 +1144,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -1283,7 +1268,7 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -1338,7 +1323,6 @@ public class DynamicHlsController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -1399,8 +1383,6 @@ public class DynamicHlsController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
- _dlnaManager,
- _deviceManager,
_transcodingJobHelper,
TranscodingJobType,
cancellationTokenSource.Token)
@@ -1439,8 +1421,6 @@ public class DynamicHlsController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
- _dlnaManager,
- _deviceManager,
_transcodingJobHelper,
TranscodingJobType,
cancellationToken)
diff --git a/Jellyfin.Api/Controllers/EnvironmentController.cs b/Jellyfin.Api/Controllers/EnvironmentController.cs
index 8c9ee1a19e..284a97621d 100644
--- a/Jellyfin.Api/Controllers/EnvironmentController.cs
+++ b/Jellyfin.Api/Controllers/EnvironmentController.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Models.EnvironmentDtos;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.IO;
using Microsoft.AspNetCore.Authorization;
@@ -168,7 +169,7 @@ public class EnvironmentController : BaseJellyfinApiController
// Check if unc share
var index = path.LastIndexOf(UncSeparator);
- if (index != -1 && path.IndexOf(UncSeparator, StringComparison.OrdinalIgnoreCase) == 0)
+ if (index != -1 && path[0] == UncSeparator)
{
parent = path.Substring(0, index);
diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs
index d51a5325f5..baeb8b81a0 100644
--- a/Jellyfin.Api/Controllers/FilterController.cs
+++ b/Jellyfin.Api/Controllers/FilterController.cs
@@ -50,7 +50,7 @@ public class FilterController : BaseJellyfinApiController
[FromQuery] Guid? userId,
[FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes)
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes)
{
userId = RequestHelpers.GetUserId(User, userId);
var user = userId.Value.Equals(default)
diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs
index da60f2c60b..6cb1993e46 100644
--- a/Jellyfin.Api/Controllers/GenresController.cs
+++ b/Jellyfin.Api/Controllers/GenresController.cs
@@ -85,7 +85,7 @@ public class GenresController : BaseJellyfinApiController
[FromQuery] string? nameStartsWithOrGreater,
[FromQuery] string? nameStartsWith,
[FromQuery] string? nameLessThan,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] bool? enableImages = true,
[FromQuery] bool enableTotalRecordCount = true)
@@ -131,8 +131,8 @@ public class GenresController : BaseJellyfinApiController
QueryResult<(BaseItem, ItemCounts)> result;
if (parentItem is ICollectionFolder parentCollectionFolder
- && (string.Equals(parentCollectionFolder.CollectionType, CollectionType.Music, StringComparison.Ordinal)
- || string.Equals(parentCollectionFolder.CollectionType, CollectionType.MusicVideos, StringComparison.Ordinal)))
+ && (parentCollectionFolder.CollectionType == CollectionType.music
+ || parentCollectionFolder.CollectionType == CollectionType.musicvideos))
{
result = _libraryManager.GetMusicGenres(query);
}
diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs
index 6eedfd8c7f..392d9955fb 100644
--- a/Jellyfin.Api/Controllers/HlsSegmentController.cs
+++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs
@@ -160,7 +160,7 @@ public class HlsSegmentController : BaseJellyfinApiController
var pathExtension = Path.GetExtension(path);
if ((string.Equals(pathExtension, segmentContainer, StringComparison.OrdinalIgnoreCase)
|| string.Equals(pathExtension, ".m3u8", StringComparison.OrdinalIgnoreCase))
- && path.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
+ && path.Contains(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase))
{
playlistPath = path;
break;
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 7b10ea170f..c031ce338d 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
@@ -79,7 +80,7 @@ public class ImageController : BaseJellyfinApiController
_appPaths = appPaths;
}
- private static Stream GetFromBase64Stream(Stream inputStream)
+ private static CryptoStream GetFromBase64Stream(Stream inputStream)
=> new CryptoStream(inputStream, new FromBase64Transform(), CryptoStreamMode.Read);
/// <summary>
@@ -2079,30 +2080,30 @@ public class ImageController : BaseJellyfinApiController
foreach (var (key, value) in headers)
{
- Response.Headers.Add(key, value);
+ Response.Headers.Append(key, value);
}
Response.ContentType = imageContentType ?? MediaTypeNames.Text.Plain;
- Response.Headers.Add(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture));
- Response.Headers.Add(HeaderNames.Vary, HeaderNames.Accept);
+ Response.Headers.Append(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture));
+ Response.Headers.Append(HeaderNames.Vary, HeaderNames.Accept);
if (disableCaching)
{
- Response.Headers.Add(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate");
- Response.Headers.Add(HeaderNames.Pragma, "no-cache, no-store, must-revalidate");
+ Response.Headers.Append(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate");
+ Response.Headers.Append(HeaderNames.Pragma, "no-cache, no-store, must-revalidate");
}
else
{
if (cacheDuration.HasValue)
{
- Response.Headers.Add(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds);
+ Response.Headers.Append(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds);
}
else
{
- Response.Headers.Add(HeaderNames.CacheControl, "public");
+ Response.Headers.Append(HeaderNames.CacheControl, "public");
}
- Response.Headers.Add(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", CultureInfo.InvariantCulture));
+ Response.Headers.Append(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", CultureInfo.InvariantCulture));
// if the image was not modified since "ifModifiedSinceHeader"-header, return a HTTP status code 304 not modified
if (!(dateImageModified > ifModifiedSinceHeader) && cacheDuration.HasValue)
diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs
index b030e74dda..e3aee1bf7a 100644
--- a/Jellyfin.Api/Controllers/ItemLookupController.cs
+++ b/Jellyfin.Api/Controllers/ItemLookupController.cs
@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs
index b8f6e91ad2..0a8522e1cf 100644
--- a/Jellyfin.Api/Controllers/ItemRefreshController.cs
+++ b/Jellyfin.Api/Controllers/ItemRefreshController.cs
@@ -2,6 +2,7 @@ using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs
index 504f2fa1d7..9800248c68 100644
--- a/Jellyfin.Api/Controllers/ItemUpdateController.cs
+++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs
@@ -5,6 +5,8 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -164,18 +166,16 @@ public class ItemUpdateController : BaseJellyfinApiController
var inheritedContentType = _libraryManager.GetInheritedContentType(item);
var configuredContentType = _libraryManager.GetConfiguredContentType(item);
- if (string.IsNullOrWhiteSpace(inheritedContentType) ||
- !string.IsNullOrWhiteSpace(configuredContentType))
+ if (inheritedContentType is null || configuredContentType is not null)
{
info.ContentTypeOptions = GetContentTypeOptions(true).ToArray();
info.ContentType = configuredContentType;
- if (string.IsNullOrWhiteSpace(inheritedContentType)
- || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+ if (inheritedContentType is null || inheritedContentType == CollectionType.tvshows)
{
info.ContentTypeOptions = info.ContentTypeOptions
.Where(i => string.IsNullOrWhiteSpace(i.Value)
- || string.Equals(i.Value, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+ || string.Equals(i.Value, "TvShows", StringComparison.OrdinalIgnoreCase))
.ToArray();
}
}
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index 80128536da..a1fc8e11b2 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -34,6 +34,7 @@ public class ItemsController : BaseJellyfinApiController
private readonly IDtoService _dtoService;
private readonly ILogger<ItemsController> _logger;
private readonly ISessionManager _sessionManager;
+ private readonly IUserDataManager _userDataRepository;
/// <summary>
/// Initializes a new instance of the <see cref="ItemsController"/> class.
@@ -44,13 +45,15 @@ public class ItemsController : BaseJellyfinApiController
/// <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>
+ /// <param name="userDataRepository">Instance of the <see cref="IUserDataManager"/> interface.</param>
public ItemsController(
IUserManager userManager,
ILibraryManager libraryManager,
ILocalizationManager localization,
IDtoService dtoService,
ILogger<ItemsController> logger,
- ISessionManager sessionManager)
+ ISessionManager sessionManager,
+ IUserDataManager userDataRepository)
{
_userManager = userManager;
_libraryManager = libraryManager;
@@ -58,6 +61,7 @@ public class ItemsController : BaseJellyfinApiController
_dtoService = dtoService;
_logger = logger;
_sessionManager = sessionManager;
+ _userDataRepository = userDataRepository;
}
/// <summary>
@@ -195,9 +199,9 @@ public class ItemsController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery] bool? isPlayed,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
@@ -269,13 +273,13 @@ public class ItemsController : BaseJellyfinApiController
folder = _libraryManager.GetUserRootFolder();
}
- string? collectionType = null;
+ CollectionType? collectionType = null;
if (folder is IHasCollectionType hasCollectionType)
{
collectionType = hasCollectionType.CollectionType;
}
- if (string.Equals(collectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+ if (collectionType == CollectionType.playlists)
{
recursive = true;
includeItemTypes = new[] { BaseItemKind.Playlist };
@@ -652,9 +656,9 @@ public class ItemsController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery] bool? isPlayed,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
@@ -812,7 +816,7 @@ public class ItemsController : BaseJellyfinApiController
[FromQuery] string? searchTerm,
[FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
[FromQuery] bool? enableUserData,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
@@ -881,4 +885,64 @@ public class ItemsController : BaseJellyfinApiController
itemsResult.TotalRecordCount,
returnItems);
}
+
+ /// <summary>
+ /// Get Item User Data.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="itemId">The item id.</param>
+ /// <response code="200">return item user data.</response>
+ /// <response code="404">Item is not found.</response>
+ /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
+ [HttpGet("Users/{userId}/Items/{itemId}/UserData")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult<UserItemDataDto> GetItemUserData(
+ [FromRoute, Required] Guid userId,
+ [FromRoute, Required] Guid itemId)
+ {
+ if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to view this item user data.");
+ }
+
+ var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException();
+ var item = _libraryManager.GetItemById(itemId);
+
+ return (item == null) ? NotFound() : _userDataRepository.GetUserDataDto(item, user);
+ }
+
+ /// <summary>
+ /// Update Item User Data.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="itemId">The item id.</param>
+ /// <param name="userDataDto">New user data object.</param>
+ /// <response code="200">return updated user item data.</response>
+ /// <response code="404">Item is not found.</response>
+ /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
+ [HttpPost("Users/{userId}/Items/{itemId}/UserData")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult<UserItemDataDto> UpdateItemUserData(
+ [FromRoute, Required] Guid userId,
+ [FromRoute, Required] Guid itemId,
+ [FromBody, Required] UpdateUserItemDataDto userDataDto)
+ {
+ if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update this item user data.");
+ }
+
+ var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException();
+ var item = _libraryManager.GetItemById(itemId);
+ if (item == null)
+ {
+ return NotFound();
+ }
+
+ _userDataRepository.SaveUserData(user, item, userDataDto, UserDataSaveReason.UpdateUserData);
+
+ return _userDataRepository.GetUserDataDto(item, user);
+ }
}
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index 46c0a8d527..de057bbab9 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -15,6 +15,7 @@ using Jellyfin.Api.Models.LibraryDtos;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Configuration;
@@ -294,8 +295,8 @@ public class LibraryController : BaseJellyfinApiController
return new AllThemeMediaResult
{
- ThemeSongsResult = themeSongs?.Value,
- ThemeVideosResult = themeVideos?.Value,
+ ThemeSongsResult = themeSongs.Value,
+ ThemeVideosResult = themeVideos.Value,
SoundtrackSongsResult = new ThemeMediaResult()
};
}
@@ -490,7 +491,7 @@ public class LibraryController : BaseJellyfinApiController
baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
- parent = parent?.GetParent();
+ parent = parent.GetParent();
}
return baseItemDtos;
@@ -788,7 +789,7 @@ public class LibraryController : BaseJellyfinApiController
[Authorize(Policy = Policies.FirstTimeSetupOrDefault)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<LibraryOptionsResultDto> GetLibraryOptionsInfo(
- [FromQuery] string? libraryContentType,
+ [FromQuery] CollectionType? libraryContentType,
[FromQuery] bool isNewLibrary = false)
{
var result = new LibraryOptionsResultDto();
@@ -922,19 +923,19 @@ public class LibraryController : BaseJellyfinApiController
}
}
- private static string[] GetRepresentativeItemTypes(string? contentType)
+ private static string[] GetRepresentativeItemTypes(CollectionType? contentType)
{
return contentType switch
{
- CollectionType.BoxSets => new[] { "BoxSet" },
- CollectionType.Playlists => new[] { "Playlist" },
- CollectionType.Movies => new[] { "Movie" },
- CollectionType.TvShows => new[] { "Series", "Season", "Episode" },
- CollectionType.Books => new[] { "Book" },
- CollectionType.Music => new[] { "MusicArtist", "MusicAlbum", "Audio", "MusicVideo" },
- CollectionType.HomeVideos => new[] { "Video", "Photo" },
- CollectionType.Photos => new[] { "Video", "Photo" },
- CollectionType.MusicVideos => new[] { "MusicVideo" },
+ CollectionType.boxsets => new[] { "BoxSet" },
+ CollectionType.playlists => new[] { "Playlist" },
+ CollectionType.movies => new[] { "Movie" },
+ CollectionType.tvshows => new[] { "Series", "Season", "Episode" },
+ CollectionType.books => new[] { "Book" },
+ CollectionType.music => new[] { "MusicArtist", "MusicAlbum", "Audio", "MusicVideo" },
+ CollectionType.homevideos => new[] { "Video", "Photo" },
+ CollectionType.photos => new[] { "Video", "Photo" },
+ CollectionType.musicvideos => new[] { "MusicVideo" },
_ => new[] { "Series", "Season", "Episode", "Movie" }
};
}
diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs
index b012ff42eb..d483ca4d2b 100644
--- a/Jellyfin.Api/Controllers/LibraryStructureController.cs
+++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.LibraryStructureDto;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 649397d68d..425086895b 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -16,6 +16,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.LiveTvDtos;
using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dto;
@@ -143,7 +144,7 @@ public class LiveTvController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? enableUserData,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery] SortOrder? sortOrder,
[FromQuery] bool enableFavoriteSorting = false,
[FromQuery] bool addCurrentProgram = true)
@@ -547,7 +548,7 @@ public class LiveTvController : BaseJellyfinApiController
[FromQuery] bool? isSports,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
diff --git a/Jellyfin.Api/Controllers/LocalizationController.cs b/Jellyfin.Api/Controllers/LocalizationController.cs
index b9772a0693..f65d95c411 100644
--- a/Jellyfin.Api/Controllers/LocalizationController.cs
+++ b/Jellyfin.Api/Controllers/LocalizationController.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs
index 435457af67..69b9042646 100644
--- a/Jellyfin.Api/Controllers/MusicGenresController.cs
+++ b/Jellyfin.Api/Controllers/MusicGenresController.cs
@@ -85,7 +85,7 @@ public class MusicGenresController : BaseJellyfinApiController
[FromQuery] string? nameStartsWithOrGreater,
[FromQuery] string? nameStartsWith,
[FromQuery] string? nameLessThan,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery] bool? enableImages = true,
[FromQuery] bool enableTotalRecordCount = true)
@@ -150,7 +150,7 @@ public class MusicGenresController : BaseJellyfinApiController
MusicGenre? item;
- if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1)
+ if (genreName.Contains(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase))
{
item = GetItemFromSlugName<MusicGenre>(_libraryManager, genreName, dtoOptions, BaseItemKind.MusicGenre);
}
diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs
index 0ba5e995fb..c5e940108c 100644
--- a/Jellyfin.Api/Controllers/PackageController.cs
+++ b/Jellyfin.Api/Controllers/PackageController.cs
@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Updates;
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index 8d2a738d4a..c4c89ccde0 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -8,6 +8,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.PlaylistDtos;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
@@ -75,7 +76,7 @@ public class PlaylistsController : BaseJellyfinApiController
[FromQuery, ParameterObsolete] string? name,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder)), ParameterObsolete] IReadOnlyList<Guid> ids,
[FromQuery, ParameterObsolete] Guid? userId,
- [FromQuery, ParameterObsolete] string? mediaType,
+ [FromQuery, ParameterObsolete] MediaType? mediaType,
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] CreatePlaylistDto? createPlaylistRequest)
{
if (ids.Count == 0)
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index 72ad14a281..f63e639276 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Extensions.Json;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Net;
diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs
index 5c77db2407..595cab2df1 100644
--- a/Jellyfin.Api/Controllers/RemoteImageController.cs
+++ b/Jellyfin.Api/Controllers/RemoteImageController.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
diff --git a/Jellyfin.Api/Controllers/ScheduledTasksController.cs b/Jellyfin.Api/Controllers/ScheduledTasksController.cs
index c8fa11ac62..065466cbca 100644
--- a/Jellyfin.Api/Controllers/ScheduledTasksController.cs
+++ b/Jellyfin.Api/Controllers/ScheduledTasksController.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Model.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs
index 387b3ea5a6..5b45941657 100644
--- a/Jellyfin.Api/Controllers/SearchController.cs
+++ b/Jellyfin.Api/Controllers/SearchController.cs
@@ -86,7 +86,7 @@ public class SearchController : BaseJellyfinApiController
[FromQuery, Required] string searchTerm,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
[FromQuery] Guid? parentId,
[FromQuery] bool? isMovie,
[FromQuery] bool? isSeries,
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index e93456de66..fdebb3d450 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -10,6 +10,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.SessionDtos;
using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
@@ -90,12 +91,6 @@ public class SessionController : BaseJellyfinApiController
result = result.Where(i => !i.UserId.Equals(default));
}
- if (activeWithinSeconds.HasValue && activeWithinSeconds.Value > 0)
- {
- var minActiveDate = DateTime.UtcNow.AddSeconds(0 - activeWithinSeconds.Value);
- result = result.Where(i => i.LastActivityDate >= minActiveDate);
- }
-
result = result.Where(i =>
{
if (!string.IsNullOrWhiteSpace(i.DeviceId))
@@ -110,6 +105,12 @@ public class SessionController : BaseJellyfinApiController
});
}
+ if (activeWithinSeconds.HasValue && activeWithinSeconds.Value > 0)
+ {
+ var minActiveDate = DateTime.UtcNow.AddSeconds(0 - activeWithinSeconds.Value);
+ result = result.Where(i => i.LastActivityDate >= minActiveDate);
+ }
+
return Ok(result);
}
@@ -393,7 +394,7 @@ public class SessionController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> PostCapabilities(
[FromQuery] string? id,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] playableMediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] playableMediaTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] GeneralCommandType[] supportedCommands,
[FromQuery] bool supportsMediaControl = false,
[FromQuery] bool supportsSync = false,
diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs
index 1098733b2c..41b0858d19 100644
--- a/Jellyfin.Api/Controllers/StartupController.cs
+++ b/Jellyfin.Api/Controllers/StartupController.cs
@@ -3,7 +3,8 @@ using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Models.StartupDtos;
-using Jellyfin.Networking.Configuration;
+using MediaBrowser.Common.Api;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;
diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs
index fb89e96108..49ca058bd4 100644
--- a/Jellyfin.Api/Controllers/SubtitleController.cs
+++ b/Jellyfin.Api/Controllers/SubtitleController.cs
@@ -14,6 +14,7 @@ using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Models.SubtitleDtos;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -115,7 +116,7 @@ public class SubtitleController : BaseJellyfinApiController
/// <response code="200">Subtitles retrieved.</response>
/// <returns>An array of <see cref="RemoteSubtitleInfo"/>.</returns>
[HttpGet("Items/{itemId}/RemoteSearch/Subtitles/{language}")]
- [Authorize]
+ [Authorize(Policy = Policies.SubtitleManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<IEnumerable<RemoteSubtitleInfo>>> SearchRemoteSubtitles(
[FromRoute, Required] Guid itemId,
@@ -135,7 +136,7 @@ public class SubtitleController : BaseJellyfinApiController
/// <response code="204">Subtitle downloaded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Items/{itemId}/RemoteSearch/Subtitles/{subtitleId}")]
- [Authorize]
+ [Authorize(Policy = Policies.SubtitleManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> DownloadRemoteSubtitles(
[FromRoute, Required] Guid itemId,
@@ -399,7 +400,7 @@ public class SubtitleController : BaseJellyfinApiController
/// <response code="204">Subtitle uploaded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Videos/{itemId}/Subtitles")]
- [Authorize(Policy = Policies.RequiresElevation)]
+ [Authorize(Policy = Policies.SubtitleManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> UploadSubtitle(
[FromRoute, Required] Guid itemId,
diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs
index 5b808f257c..675757fc51 100644
--- a/Jellyfin.Api/Controllers/SuggestionsController.cs
+++ b/Jellyfin.Api/Controllers/SuggestionsController.cs
@@ -56,7 +56,7 @@ public class SuggestionsController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetSuggestions(
[FromRoute, Required] Guid userId,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaType,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaType,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] type,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs
index 23abba7dc7..3839781971 100644
--- a/Jellyfin.Api/Controllers/SyncPlayController.cs
+++ b/Jellyfin.Api/Controllers/SyncPlayController.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.SyncPlayDtos;
+using MediaBrowser.Common.Api;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.SyncPlay;
diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs
index 11095a97f0..3d4df03869 100644
--- a/Jellyfin.Api/Controllers/SystemController.cs
+++ b/Jellyfin.Api/Controllers/SystemController.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.Mime;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index b5b6406206..4fbaafa2a0 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -160,9 +160,9 @@ public class TrailersController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery] bool? isPlayed,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
diff --git a/Jellyfin.Api/Controllers/TrickplayController.cs b/Jellyfin.Api/Controllers/TrickplayController.cs
new file mode 100644
index 0000000000..2dc960229a
--- /dev/null
+++ b/Jellyfin.Api/Controllers/TrickplayController.cs
@@ -0,0 +1,101 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Net.Mime;
+using System.Text;
+using System.Threading.Tasks;
+using Jellyfin.Api.Attributes;
+using Jellyfin.Api.Extensions;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Trickplay;
+using MediaBrowser.Model;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers;
+
+/// <summary>
+/// Trickplay controller.
+/// </summary>
+[Route("")]
+[Authorize]
+public class TrickplayController : BaseJellyfinApiController
+{
+ private readonly ILibraryManager _libraryManager;
+ private readonly ITrickplayManager _trickplayManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrickplayController"/> class.
+ /// </summary>
+ /// <param name="libraryManager">Instance of <see cref="ILibraryManager"/>.</param>
+ /// <param name="trickplayManager">Instance of <see cref="ITrickplayManager"/>.</param>
+ public TrickplayController(
+ ILibraryManager libraryManager,
+ ITrickplayManager trickplayManager)
+ {
+ _libraryManager = libraryManager;
+ _trickplayManager = trickplayManager;
+ }
+
+ /// <summary>
+ /// Gets an image tiles playlist for trickplay.
+ /// </summary>
+ /// <param name="itemId">The item id.</param>
+ /// <param name="width">The width of a single tile.</param>
+ /// <param name="mediaSourceId">The media version id, if using an alternate version.</param>
+ /// <response code="200">Tiles playlist returned.</response>
+ /// <returns>A <see cref="FileResult"/> containing the trickplay playlist file.</returns>
+ [HttpGet("Videos/{itemId}/Trickplay/{width}/tiles.m3u8")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesPlaylistFile]
+ public async Task<ActionResult> GetTrickplayHlsPlaylist(
+ [FromRoute, Required] Guid itemId,
+ [FromRoute, Required] int width,
+ [FromQuery] Guid? mediaSourceId)
+ {
+ string? playlist = await _trickplayManager.GetHlsPlaylist(mediaSourceId ?? itemId, width, User.GetToken()).ConfigureAwait(false);
+
+ if (string.IsNullOrEmpty(playlist))
+ {
+ return NotFound();
+ }
+
+ return Content(playlist, MimeTypes.GetMimeType("playlist.m3u8"), Encoding.UTF8);
+ }
+
+ /// <summary>
+ /// Gets a trickplay tile image.
+ /// </summary>
+ /// <param name="itemId">The item id.</param>
+ /// <param name="width">The width of a single tile.</param>
+ /// <param name="index">The index of the desired tile.</param>
+ /// <param name="mediaSourceId">The media version id, if using an alternate version.</param>
+ /// <response code="200">Tile image returned.</response>
+ /// <response code="200">Tile image not found at specified index.</response>
+ /// <returns>A <see cref="FileResult"/> containing the trickplay tiles image.</returns>
+ [HttpGet("Videos/{itemId}/Trickplay/{width}/{index}.jpg")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesImageFile]
+ public ActionResult GetTrickplayTileImage(
+ [FromRoute, Required] Guid itemId,
+ [FromRoute, Required] int width,
+ [FromRoute, Required] int index,
+ [FromQuery] Guid? mediaSourceId)
+ {
+ var item = _libraryManager.GetItemById(mediaSourceId ?? itemId);
+ if (item is null)
+ {
+ return NotFound();
+ }
+
+ var path = _trickplayManager.GetTrickplayTilePath(item, width, index);
+ if (System.IO.File.Exists(path))
+ {
+ return PhysicalFile(path, MediaTypeNames.Image.Jpeg);
+ }
+
+ return NotFound();
+ }
+}
diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs
index bdbbd1e0db..55a30d4692 100644
--- a/Jellyfin.Api/Controllers/TvShowsController.cs
+++ b/Jellyfin.Api/Controllers/TvShowsController.cs
@@ -135,7 +135,7 @@ public class TvShowsController : BaseJellyfinApiController
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="enableUserData">Optional. Include user data.</param>
- /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the next up episodes.</returns>
+ /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the upcoming episodes.</returns>
[HttpGet("Upcoming")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetUpcomingEpisodes(
@@ -219,7 +219,7 @@ public class TvShowsController : BaseJellyfinApiController
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery] bool? enableUserData,
- [FromQuery] string? sortBy)
+ [FromQuery] ItemSortBy? sortBy)
{
userId = RequestHelpers.GetUserId(User, userId);
var user = userId.Value.Equals(default)
@@ -289,7 +289,7 @@ public class TvShowsController : BaseJellyfinApiController
episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo.Value).ToList();
}
- if (string.Equals(sortBy, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase))
+ if (sortBy == ItemSortBy.Random)
{
episodes.Shuffle();
}
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 1be40111dd..f9f27f1480 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -8,6 +8,7 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.UserDtos;
using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs
index 838b432340..0ffa3ab1ad 100644
--- a/Jellyfin.Api/Controllers/UserViewsController.cs
+++ b/Jellyfin.Api/Controllers/UserViewsController.cs
@@ -6,6 +6,7 @@ using System.Linq;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.UserViewDtos;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -63,7 +64,7 @@ public class UserViewsController : BaseJellyfinApiController
public QueryResult<BaseItemDto> GetUserViews(
[FromRoute, Required] Guid userId,
[FromQuery] bool? includeExternalContent,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] presetViews,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] CollectionType?[] presetViews,
[FromQuery] bool includeHidden = false)
{
var query = new UserViewQuery
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index c0ec646eda..5d9868eb9f 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -12,11 +12,10 @@ using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.StreamingDtos;
+using MediaBrowser.Common.Api;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -41,11 +40,9 @@ public class VideosController : BaseJellyfinApiController
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDtoService _dtoService;
- private readonly IDlnaManager _dlnaManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IMediaEncoder _mediaEncoder;
- private readonly IDeviceManager _deviceManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
private readonly IHttpClientFactory _httpClientFactory;
private readonly EncodingHelper _encodingHelper;
@@ -58,11 +55,9 @@ public class VideosController : BaseJellyfinApiController
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
- /// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
- /// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
@@ -70,11 +65,9 @@ public class VideosController : BaseJellyfinApiController
ILibraryManager libraryManager,
IUserManager userManager,
IDtoService dtoService,
- IDlnaManager dlnaManager,
IMediaSourceManager mediaSourceManager,
IServerConfigurationManager serverConfigurationManager,
IMediaEncoder mediaEncoder,
- IDeviceManager deviceManager,
TranscodingJobHelper transcodingJobHelper,
IHttpClientFactory httpClientFactory,
EncodingHelper encodingHelper)
@@ -82,11 +75,9 @@ public class VideosController : BaseJellyfinApiController
_libraryManager = libraryManager;
_userManager = userManager;
_dtoService = dtoService;
- _dlnaManager = dlnaManager;
_mediaSourceManager = mediaSourceManager;
_serverConfigurationManager = serverConfigurationManager;
_mediaEncoder = mediaEncoder;
- _deviceManager = deviceManager;
_transcodingJobHelper = transcodingJobHelper;
_httpClientFactory = httpClientFactory;
_encodingHelper = encodingHelper;
@@ -323,7 +314,7 @@ public class VideosController : BaseJellyfinApiController
[FromQuery] bool? @static,
[FromQuery] string? @params,
[FromQuery] string? tag,
- [FromQuery] string? deviceProfileId,
+ [FromQuery, ParameterObsolete] string? deviceProfileId,
[FromQuery] string? playSessionId,
[FromQuery] string? segmentContainer,
[FromQuery] int? segmentLength,
@@ -380,7 +371,6 @@ public class VideosController : BaseJellyfinApiController
Static = @static ?? false,
Params = @params,
Tag = tag,
- DeviceProfileId = deviceProfileId,
PlaySessionId = playSessionId,
SegmentContainer = segmentContainer,
SegmentLength = segmentLength,
@@ -437,8 +427,6 @@ public class VideosController : BaseJellyfinApiController
_serverConfigurationManager,
_mediaEncoder,
_encodingHelper,
- _dlnaManager,
- _deviceManager,
_transcodingJobHelper,
_transcodingJobType,
cancellationTokenSource.Token)
@@ -446,8 +434,6 @@ public class VideosController : BaseJellyfinApiController
if (@static.HasValue && @static.Value && state.DirectStreamProvider is not null)
{
- StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
-
var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfo(streamingRequest.LiveStreamId);
if (liveStreamInfo is null)
{
@@ -462,8 +448,6 @@ public class VideosController : BaseJellyfinApiController
// Static remote stream
if (@static.HasValue && @static.Value && state.InputProtocol == MediaProtocol.Http)
{
- StreamingHelpers.AddDlnaHeaders(state, Response.Headers, true, state.Request.StartTimeTicks, Request, _dlnaManager);
-
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return await FileStreamResponseHelpers.GetStaticRemoteStreamResult(state, httpClient, HttpContext).ConfigureAwait(false);
}
@@ -474,12 +458,6 @@ public class VideosController : BaseJellyfinApiController
}
var outputPath = state.OutputFilePath;
- var outputPathExists = System.IO.File.Exists(outputPath);
-
- var transcodingJob = _transcodingJobHelper.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
- var isTranscodeCached = outputPathExists && transcodingJob is not null;
-
- 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/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs
index 74370db50b..ca46c38c59 100644
--- a/Jellyfin.Api/Controllers/YearsController.cs
+++ b/Jellyfin.Api/Controllers/YearsController.cs
@@ -76,8 +76,8 @@ public class YearsController : BaseJellyfinApiController
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
[FromQuery] bool? enableUserData,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
@@ -191,7 +191,7 @@ public class YearsController : BaseJellyfinApiController
return _dtoService.GetBaseItemDto(item, dtoOptions);
}
- private bool FilterItem(BaseItem f, IReadOnlyCollection<BaseItemKind> excludeItemTypes, IReadOnlyCollection<BaseItemKind> includeItemTypes, IReadOnlyCollection<string> mediaTypes)
+ private bool FilterItem(BaseItem f, IReadOnlyCollection<BaseItemKind> excludeItemTypes, IReadOnlyCollection<BaseItemKind> includeItemTypes, IReadOnlyCollection<MediaType> mediaTypes)
{
var baseItemKind = f.GetBaseItemKind();
// Exclude item types
@@ -207,7 +207,7 @@ public class YearsController : BaseJellyfinApiController
}
// Include MediaTypes
- if (mediaTypes.Count > 0 && !mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+ if (mediaTypes.Count > 0 && !mediaTypes.Contains(f.MediaType))
{
return false;
}