aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api/Controllers
diff options
context:
space:
mode:
authorcrobibero <cody@robibe.ro>2020-08-04 12:48:53 -0600
committercrobibero <cody@robibe.ro>2020-08-04 12:48:53 -0600
commit858aecd409914f6a9ca6f86445ec20c8bacc0b59 (patch)
tree249704c4c58724a87d4eeede6df4a26d11a97132 /Jellyfin.Api/Controllers
parent2b7cefdf15cf15a3e2e9f8499af7a008ea4a9cca (diff)
Fix all route for base url support
Diffstat (limited to 'Jellyfin.Api/Controllers')
-rw-r--r--Jellyfin.Api/Controllers/AlbumsController.cs5
-rw-r--r--Jellyfin.Api/Controllers/DashboardController.cs11
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs17
-rw-r--r--Jellyfin.Api/Controllers/FilterController.cs5
-rw-r--r--Jellyfin.Api/Controllers/HlsSegmentController.cs11
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs57
-rw-r--r--Jellyfin.Api/Controllers/InstantMixController.cs15
-rw-r--r--Jellyfin.Api/Controllers/ItemLookupController.cs25
-rw-r--r--Jellyfin.Api/Controllers/ItemUpdateController.cs7
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs7
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs51
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs11
-rw-r--r--Jellyfin.Api/Controllers/PackageController.cs14
-rw-r--r--Jellyfin.Api/Controllers/PlaystateController.cs19
-rw-r--r--Jellyfin.Api/Controllers/PluginsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/SessionController.cs33
-rw-r--r--Jellyfin.Api/Controllers/SubtitleController.cs15
-rw-r--r--Jellyfin.Api/Controllers/SuggestionsController.cs3
-rw-r--r--Jellyfin.Api/Controllers/TrailersController.cs2
-rw-r--r--Jellyfin.Api/Controllers/UniversalAudioController.cs9
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs2
-rw-r--r--Jellyfin.Api/Controllers/UserLibraryController.cs21
-rw-r--r--Jellyfin.Api/Controllers/UserViewsController.cs5
-rw-r--r--Jellyfin.Api/Controllers/VideoHlsController.cs5
24 files changed, 186 insertions, 166 deletions
diff --git a/Jellyfin.Api/Controllers/AlbumsController.cs b/Jellyfin.Api/Controllers/AlbumsController.cs
index 01ba7fc32..190d4bd07 100644
--- a/Jellyfin.Api/Controllers/AlbumsController.cs
+++ b/Jellyfin.Api/Controllers/AlbumsController.cs
@@ -17,6 +17,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The albums controller.
/// </summary>
+ [Route("")]
public class AlbumsController : BaseJellyfinApiController
{
private readonly IUserManager _userManager;
@@ -48,7 +49,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <response code="200">Similar albums returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with similar albums.</returns>
- [HttpGet("/Albums/{albumId}/Similar")]
+ [HttpGet("Albums/{albumId}/Similar")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetSimilarAlbums(
[FromRoute] string albumId,
@@ -80,7 +81,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <response code="200">Similar artists returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with similar artists.</returns>
- [HttpGet("/Artists/{artistId}/Similar")]
+ [HttpGet("Artists/{artistId}/Similar")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetSimilarArtists(
[FromRoute] string artistId,
diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs
index e033c50d5..a7bdb24f6 100644
--- a/Jellyfin.Api/Controllers/DashboardController.cs
+++ b/Jellyfin.Api/Controllers/DashboardController.cs
@@ -20,6 +20,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The dashboard controller.
/// </summary>
+ [Route("")]
public class DashboardController : BaseJellyfinApiController
{
private readonly ILogger<DashboardController> _logger;
@@ -64,7 +65,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">ConfigurationPages returned.</response>
/// <response code="404">Server still loading.</response>
/// <returns>An <see cref="IEnumerable{ConfigurationPageInfo}"/> with infos about the plugins.</returns>
- [HttpGet("/web/ConfigurationPages")]
+ [HttpGet("web/ConfigurationPages")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<IEnumerable<ConfigurationPageInfo?>> GetConfigurationPages(
@@ -118,7 +119,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">ConfigurationPage returned.</response>
/// <response code="404">Plugin configuration page not found.</response>
/// <returns>The configuration page.</returns>
- [HttpGet("/web/ConfigurationPage")]
+ [HttpGet("web/ConfigurationPage")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetDashboardConfigurationPage([FromQuery] string? name)
@@ -172,7 +173,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Robots.txt returned.</response>
/// <returns>The robots.txt.</returns>
- [HttpGet("/robots.txt")]
+ [HttpGet("robots.txt")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ApiExplorerSettings(IgnoreApi = true)]
public ActionResult GetRobotsTxt()
@@ -187,7 +188,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Web client returned.</response>
/// <response code="404">Server does not host a web client.</response>
/// <returns>The resource.</returns>
- [HttpGet("/web/{*resourceName}")]
+ [HttpGet("web/{*resourceName}")]
[ApiExplorerSettings(IgnoreApi = true)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -218,7 +219,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Favicon.ico returned.</response>
/// <returns>The favicon.</returns>
- [HttpGet("/favicon.ico")]
+ [HttpGet("favicon.ico")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ApiExplorerSettings(IgnoreApi = true)]
public ActionResult GetFavIcon()
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index c4f79ce95..ec65cb95a 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -37,6 +37,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Dynamic hls controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class DynamicHlsController : BaseJellyfinApiController
{
@@ -164,8 +165,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
- [HttpGet("/Videos/{itemId}/master.m3u8")]
- [HttpHead("/Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")]
+ [HttpGet("Videos/{itemId}/master.m3u8")]
+ [HttpHead("Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> GetMasterHlsVideoPlaylist(
[FromRoute] Guid itemId,
@@ -334,8 +335,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
/// <response code="200">Audio stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
- [HttpGet("/Audio/{itemId}/master.m3u8")]
- [HttpHead("/Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")]
+ [HttpGet("Audio/{itemId}/master.m3u8")]
+ [HttpHead("Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> GetMasterHlsAudioPlaylist(
[FromRoute] Guid itemId,
@@ -503,7 +504,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
- [HttpGet("/Videos/{itemId}/main.m3u8")]
+ [HttpGet("Videos/{itemId}/main.m3u8")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> GetVariantHlsVideoPlaylist(
[FromRoute] Guid itemId,
@@ -668,7 +669,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <response code="200">Audio stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
- [HttpGet("/Audio/{itemId}/main.m3u8")]
+ [HttpGet("Audio/{itemId}/main.m3u8")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> GetVariantHlsAudioPlaylist(
[FromRoute] Guid itemId,
@@ -835,7 +836,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
- [HttpGet("/Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
+ [HttpGet("Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")]
public async Task<ActionResult> GetHlsVideoSegment(
@@ -1004,7 +1005,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
- [HttpGet("/Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
+ [HttpGet("Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")]
public async Task<ActionResult> GetHlsAudioSegment(
diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs
index 9ba5e1161..2a567c846 100644
--- a/Jellyfin.Api/Controllers/FilterController.cs
+++ b/Jellyfin.Api/Controllers/FilterController.cs
@@ -18,6 +18,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Filters controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class FilterController : BaseJellyfinApiController
{
@@ -44,7 +45,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="mediaTypes">Optional. Filter by MediaType. Allows multiple, comma delimited.</param>
/// <response code="200">Legacy filters retrieved.</response>
/// <returns>Legacy query filters.</returns>
- [HttpGet("/Items/Filters")]
+ [HttpGet("Items/Filters")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryFiltersLegacy> GetQueryFiltersLegacy(
[FromQuery] Guid? userId,
@@ -133,7 +134,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="recursive">Optional. Search recursive.</param>
/// <response code="200">Filters retrieved.</response>
/// <returns>Query filters.</returns>
- [HttpGet("/Items/Filters2")]
+ [HttpGet("Items/Filters2")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryFilters> GetQueryFilters(
[FromQuery] Guid? userId,
diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs
index 7bf9326a7..e4a6842bc 100644
--- a/Jellyfin.Api/Controllers/HlsSegmentController.cs
+++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The hls segment controller.
/// </summary>
+ [Route("")]
public class HlsSegmentController : BaseJellyfinApiController
{
private readonly IFileSystem _fileSystem;
@@ -50,8 +51,8 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="FileStreamResult"/> containing the audio stream.</returns>
// Can't require authentication just yet due to seeing some requests come from Chrome without full query string
// [Authenticated]
- [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.mp3", Name = "GetHlsAudioSegmentLegacyMp3")]
- [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.aac", Name = "GetHlsAudioSegmentLegacyAac")]
+ [HttpGet("Audio/{itemId}/hls/{segmentId}/stream.mp3", Name = "GetHlsAudioSegmentLegacyMp3")]
+ [HttpGet("Audio/{itemId}/hls/{segmentId}/stream.aac", Name = "GetHlsAudioSegmentLegacyAac")]
[ProducesResponseType(StatusCodes.Status200OK)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
public ActionResult GetHlsAudioSegmentLegacy([FromRoute] string itemId, [FromRoute] string segmentId)
@@ -70,7 +71,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playlistId">The playlist id.</param>
/// <response code="200">Hls video playlist returned.</response>
/// <returns>A <see cref="FileStreamResult"/> containing the playlist.</returns>
- [HttpGet("/Videos/{itemId}/hls/{playlistId}/stream.m3u8")]
+ [HttpGet("Videos/{itemId}/hls/{playlistId}/stream.m3u8")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
@@ -89,7 +90,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playSessionId">The play session id.</param>
/// <response code="204">Encoding stopped successfully.</response>
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
- [HttpDelete("/Videos/ActiveEncodings")]
+ [HttpDelete("Videos/ActiveEncodings")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult StopEncodingProcess([FromQuery] string deviceId, [FromQuery] string playSessionId)
@@ -109,7 +110,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="FileStreamResult"/> containing the video segment.</returns>
// Can't require authentication just yet due to seeing some requests come from Chrome without full query string
// [Authenticated]
- [HttpGet("/Videos/{itemId}/hls/{playlistId}/{segmentId}.{segmentContainer}")]
+ [HttpGet("Videos/{itemId}/hls/{playlistId}/{segmentId}.{segmentContainer}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
public ActionResult GetHlsVideoSegmentLegacy(
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 3a445b1b3..360164ad4 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Image controller.
/// </summary>
+ [Route("")]
public class ImageController : BaseJellyfinApiController
{
private readonly IUserManager _userManager;
@@ -81,8 +82,8 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Image updated.</response>
/// <response code="403">User does not have permission to delete the image.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Users/{userId}/Images/{imageType}")]
- [HttpPost("/Users/{userId}/Images/{imageType}/{index?}", Name = "PostUserImage_2")]
+ [HttpPost("Users/{userId}/Images/{imageType}")]
+ [HttpPost("Users/{userId}/Images/{imageType}/{index?}", Name = "PostUserImage_2")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
@@ -127,8 +128,8 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Image deleted.</response>
/// <response code="403">User does not have permission to delete the image.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpDelete("/Users/{userId}/Images/{itemType}")]
- [HttpDelete("/Users/{userId}/Images/{itemType}/{index?}", Name = "DeleteUserImage_2")]
+ [HttpDelete("Users/{userId}/Images/{itemType}")]
+ [HttpDelete("Users/{userId}/Images/{itemType}/{index?}", Name = "DeleteUserImage_2")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
@@ -166,8 +167,8 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Image deleted.</response>
/// <response code="404">Item not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
- [HttpDelete("/Items/{itemId}/Images/{imageType}")]
- [HttpDelete("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "DeleteItemImage_2")]
+ [HttpDelete("Items/{itemId}/Images/{imageType}")]
+ [HttpDelete("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "DeleteItemImage_2")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -195,8 +196,8 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Image saved.</response>
/// <response code="404">Item not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
- [HttpPost("/Items/{itemId}/Images/{imageType}")]
- [HttpPost("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "SetItemImage_2")]
+ [HttpPost("Items/{itemId}/Images/{imageType}")]
+ [HttpPost("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "SetItemImage_2")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -230,7 +231,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Image index updated.</response>
/// <response code="404">Item not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
- [HttpPost("/Items/{itemId}/Images/{imageType}/{imageIndex}/Index")]
+ [HttpPost("Items/{itemId}/Images/{imageType}/{imageIndex}/Index")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -257,7 +258,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Item images returned.</response>
/// <response code="404">Item not found.</response>
/// <returns>The list of image infos on success, or <see cref="NotFoundResult"/> if item not found.</returns>
- [HttpGet("/Items/{itemId}/Images")]
+ [HttpGet("Items/{itemId}/Images")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<IEnumerable<ImageInfo>> GetItemImageInfos([FromRoute] Guid itemId)
@@ -341,10 +342,10 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/Items/{itemId}/Images/{imageType}")]
- [HttpHead("/Items/{itemId}/Images/{imageType}", Name = "HeadItemImage")]
- [HttpGet("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "GetItemImage_2")]
- [HttpHead("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "HeadItemImage_2")]
+ [HttpGet("Items/{itemId}/Images/{imageType}")]
+ [HttpHead("Items/{itemId}/Images/{imageType}", Name = "HeadItemImage")]
+ [HttpGet("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "GetItemImage_2")]
+ [HttpHead("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "HeadItemImage_2")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetItemImage(
@@ -421,8 +422,8 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/Items/{itemId}/Images/{imageType}/{imageIndex}/{tag}/{format}/{maxWidth}/{maxHeight}/{percentPlayed}/{unplayedCount}")]
- [HttpHead("/Items/{itemId}/Images/{imageType}/{imageIndex}/{tag}/{format}/{maxWidth}/{maxHeight}/{percentPlayed}/{unplayedCount}", Name = "HeadItemImage2")]
+ [HttpGet("Items/{itemId}/Images/{imageType}/{imageIndex}/{tag}/{format}/{maxWidth}/{maxHeight}/{percentPlayed}/{unplayedCount}")]
+ [HttpHead("Items/{itemId}/Images/{imageType}/{imageIndex}/{tag}/{format}/{maxWidth}/{maxHeight}/{percentPlayed}/{unplayedCount}", Name = "HeadItemImage2")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetItemImage2(
@@ -499,8 +500,8 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/Artists/{name}/Images/{imageType}/{imageIndex?}")]
- [HttpHead("/Artists/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadArtistImage")]
+ [HttpGet("Artists/{name}/Images/{imageType}/{imageIndex?}")]
+ [HttpHead("Artists/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadArtistImage")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetArtistImage(
@@ -577,8 +578,8 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/Genres/{name}/Images/{imageType}/{imageIndex?}")]
- [HttpHead("/Genres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadGenreImage")]
+ [HttpGet("Genres/{name}/Images/{imageType}/{imageIndex?}")]
+ [HttpHead("Genres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadGenreImage")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetGenreImage(
@@ -655,8 +656,8 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/MusicGenres/{name}/Images/{imageType}/{imageIndex?}")]
- [HttpHead("/MusicGenres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadMusicGenreImage")]
+ [HttpGet("MusicGenres/{name}/Images/{imageType}/{imageIndex?}")]
+ [HttpHead("MusicGenres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadMusicGenreImage")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetMusicGenreImage(
@@ -733,8 +734,8 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/Persons/{name}/Images/{imageType}/{imageIndex?}")]
- [HttpHead("/Persons/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadPersonImage")]
+ [HttpGet("Persons/{name}/Images/{imageType}/{imageIndex?}")]
+ [HttpHead("Persons/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadPersonImage")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetPersonImage(
@@ -811,8 +812,8 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/Studios/{name}/Images/{imageType}/{imageIndex?}")]
- [HttpHead("/Studios/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadStudioImage")]
+ [HttpGet("Studios/{name}/Images/{imageType}/{imageIndex?}")]
+ [HttpHead("Studios/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadStudioImage")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetStudioImage(
@@ -889,8 +890,8 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="FileStreamResult"/> containing the file stream on success,
/// or a <see cref="NotFoundResult"/> if item not found.
/// </returns>
- [HttpGet("/Users/{userId}/Images/{imageType}/{imageIndex?}")]
- [HttpHead("/Users/{userId}/Images/{imageType}/{imageIndex?}", Name = "HeadUserImage")]
+ [HttpGet("Users/{userId}/Images/{imageType}/{imageIndex?}")]
+ [HttpHead("Users/{userId}/Images/{imageType}/{imageIndex?}", Name = "HeadUserImage")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetUserImage(
diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs
index bb980af3e..8ca232cef 100644
--- a/Jellyfin.Api/Controllers/InstantMixController.cs
+++ b/Jellyfin.Api/Controllers/InstantMixController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The instant mix controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class InstantMixController : BaseJellyfinApiController
{
@@ -59,7 +60,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("/Songs/{id}/InstantMix")]
+ [HttpGet("Songs/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromSong(
[FromRoute] Guid id,
@@ -96,7 +97,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("/Albums/{id}/InstantMix")]
+ [HttpGet("Albums/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromAlbum(
[FromRoute] Guid id,
@@ -133,7 +134,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("/Playlists/{id}/InstantMix")]
+ [HttpGet("Playlists/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromPlaylist(
[FromRoute] Guid id,
@@ -170,7 +171,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("/MusicGenres/{name}/InstantMix")]
+ [HttpGet("MusicGenres/{name}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenre(
[FromRoute] string? name,
@@ -206,7 +207,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("/Artists/InstantMix")]
+ [HttpGet("Artists/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromArtists(
[FromRoute] Guid id,
@@ -243,7 +244,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("/MusicGenres/InstantMix")]
+ [HttpGet("MusicGenres/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenres(
[FromRoute] Guid id,
@@ -280,7 +281,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
- [HttpGet("/Items/{id}/InstantMix")]
+ [HttpGet("Items/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromItem(
[FromRoute] Guid id,
diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs
index 44709d0ee..0d9dffbfe 100644
--- a/Jellyfin.Api/Controllers/ItemLookupController.cs
+++ b/Jellyfin.Api/Controllers/ItemLookupController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Item lookup controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class ItemLookupController : BaseJellyfinApiController
{
@@ -68,7 +69,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">External id info retrieved.</response>
/// <response code="404">Item not found.</response>
/// <returns>List of external id info.</returns>
- [HttpGet("/Items/{itemId}/ExternalIdInfos")]
+ [HttpGet("Items/{itemId}/ExternalIdInfos")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -92,7 +93,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/Movie")]
+ [HttpPost("Items/RemoteSearch/Movie")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetMovieRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<MovieInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<Movie, MovieInfo>(query, CancellationToken.None)
@@ -109,7 +110,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/Trailer")]
+ [HttpPost("Items/RemoteSearch/Trailer")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetTrailerRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<TrailerInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<Trailer, TrailerInfo>(query, CancellationToken.None)
@@ -126,7 +127,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/MusicVideo")]
+ [HttpPost("Items/RemoteSearch/MusicVideo")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetMusicVideoRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<MusicVideoInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<MusicVideo, MusicVideoInfo>(query, CancellationToken.None)
@@ -143,7 +144,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/Series")]
+ [HttpPost("Items/RemoteSearch/Series")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetSeriesRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<SeriesInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<Series, SeriesInfo>(query, CancellationToken.None)
@@ -160,7 +161,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/BoxSet")]
+ [HttpPost("Items/RemoteSearch/BoxSet")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetBoxSetRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<BoxSetInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<BoxSet, BoxSetInfo>(query, CancellationToken.None)
@@ -177,7 +178,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/MusicArtist")]
+ [HttpPost("Items/RemoteSearch/MusicArtist")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetMusicArtistRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<ArtistInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<MusicArtist, ArtistInfo>(query, CancellationToken.None)
@@ -194,7 +195,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/MusicAlbum")]
+ [HttpPost("Items/RemoteSearch/MusicAlbum")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetMusicAlbumRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<AlbumInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<MusicAlbum, AlbumInfo>(query, CancellationToken.None)
@@ -211,7 +212,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/Person")]
+ [HttpPost("Items/RemoteSearch/Person")]
[Authorize(Policy = Policies.RequiresElevation)]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetPersonRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<PersonLookupInfo> query)
{
@@ -229,7 +230,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="OkResult"/> containing the list of remote search results.
/// </returns>
- [HttpPost("/Items/RemoteSearch/Book")]
+ [HttpPost("Items/RemoteSearch/Book")]
public async Task<ActionResult<IEnumerable<RemoteSearchResult>>> GetBookRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery<BookInfo> query)
{
var results = await _providerManager.GetRemoteSearchResults<Book, BookInfo>(query, CancellationToken.None)
@@ -247,7 +248,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="FileStreamResult"/> containing the images file stream.
/// </returns>
- [HttpGet("/Items/RemoteSearch/Image")]
+ [HttpGet("Items/RemoteSearch/Image")]
public async Task<ActionResult> GetRemoteSearchImage(
[FromQuery, Required] string imageUrl,
[FromQuery, Required] string providerName)
@@ -291,7 +292,7 @@ namespace Jellyfin.Api.Controllers
/// A <see cref="Task" /> that represents the asynchronous operation to get the remote search results.
/// The task result contains an <see cref="NoContentResult"/>.
/// </returns>
- [HttpPost("/Items/RemoteSearch/Apply/{id}")]
+ [HttpPost("Items/RemoteSearch/Apply/{id}")]
[Authorize(Policy = Policies.RequiresElevation)]
public async Task<ActionResult> ApplySearchCriteria(
[FromRoute] Guid itemId,
diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs
index c9b2aafcc..a5d9d36a3 100644
--- a/Jellyfin.Api/Controllers/ItemUpdateController.cs
+++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs
@@ -24,6 +24,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Item update controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.RequiresElevation)]
public class ItemUpdateController : BaseJellyfinApiController
{
@@ -63,7 +64,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Item updated.</response>
/// <response code="404">Item not found.</response>
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the item could not be found.</returns>
- [HttpPost("/Items/{itemId}")]
+ [HttpPost("Items/{itemId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateItem([FromRoute] Guid itemId, [FromBody, BindRequired] BaseItemDto request)
@@ -136,7 +137,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Item metadata editor returned.</response>
/// <response code="404">Item not found.</response>
/// <returns>An <see cref="OkResult"/> on success containing the metadata editor, or a <see cref="NotFoundResult"/> if the item could not be found.</returns>
- [HttpGet("/Items/{itemId}/MetadataEditor")]
+ [HttpGet("Items/{itemId}/MetadataEditor")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<MetadataEditorInfo> GetMetadataEditorInfo([FromRoute] Guid itemId)
@@ -190,7 +191,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Item content type updated.</response>
/// <response code="404">Item not found.</response>
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the item could not be found.</returns>
- [HttpPost("/Items/{itemId}/ContentType")]
+ [HttpPost("Items/{itemId}/ContentType")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateItemContentType([FromRoute] Guid itemId, [FromQuery, BindRequired] string? contentType)
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index 354741ced..1b8b68313 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -23,6 +23,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The items controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class ItemsController : BaseJellyfinApiController
{
@@ -139,8 +140,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
/// <param name="enableImages">Optional, include image information in output.</param>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
- [HttpGet("/Items")]
- [HttpGet("/Users/{uId}/Items", Name = "GetItems_2")]
+ [HttpGet("Items")]
+ [HttpGet("Users/{uId}/Items", Name = "GetItems_2")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetItems(
[FromRoute] Guid? uId,
@@ -523,7 +524,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableImages">Optional. Include image information in output.</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")]
+ [HttpGet("Users/{userId}/Items/Resume")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetResumeItems(
[FromRoute] Guid userId,
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index 0ec7e2b8c..a4637752d 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -43,6 +43,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Library Controller.
/// </summary>
+ [Route("")]
public class LibraryController : BaseJellyfinApiController
{
private readonly IProviderManager _providerManager;
@@ -100,7 +101,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">File stream returned.</response>
/// <response code="404">Item not found.</response>
/// <returns>A <see cref="FileStreamResult"/> with the original file.</returns>
- [HttpGet("/Items/{itemId}/File")]
+ [HttpGet("Items/{itemId}/File")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -121,7 +122,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Critic reviews returned.</response>
/// <returns>The list of critic reviews.</returns>
- [HttpGet("/Items/{itemId}/CriticReviews")]
+ [HttpGet("Items/{itemId}/CriticReviews")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[Obsolete("This endpoint is obsolete.")]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -139,7 +140,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Theme songs returned.</response>
/// <response code="404">Item not found.</response>
/// <returns>The item theme songs.</returns>
- [HttpGet("/Items/{itemId}/ThemeSongs")]
+ [HttpGet("Items/{itemId}/ThemeSongs")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -205,7 +206,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Theme videos returned.</response>
/// <response code="404">Item not found.</response>
/// <returns>The item theme videos.</returns>
- [HttpGet("/Items/{itemId}/ThemeVideos")]
+ [HttpGet("Items/{itemId}/ThemeVideos")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -271,7 +272,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Theme songs and videos returned.</response>
/// <response code="404">Item not found.</response>
/// <returns>The item theme videos.</returns>
- [HttpGet("/Items/{itemId}/ThemeMedia")]
+ [HttpGet("Items/{itemId}/ThemeMedia")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<AllThemeMediaResult> GetThemeMedia(
@@ -302,7 +303,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="204">Library scan started.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpGet("/Library/Refresh")]
+ [HttpGet("Library/Refresh")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> RefreshLibrary()
@@ -326,7 +327,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Item deleted.</response>
/// <response code="401">Unauthorized access.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpDelete("/Items/{itemId}")]
+ [HttpDelete("Items/{itemId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
@@ -356,7 +357,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Items deleted.</response>
/// <response code="401">Unauthorized access.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpDelete("/Items")]
+ [HttpDelete("Items")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
@@ -400,7 +401,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="isFavorite">Optional. Get counts of favorite items.</param>
/// <response code="200">Item counts returned.</response>
/// <returns>Item counts.</returns>
- [HttpGet("/Items/Counts")]
+ [HttpGet("Items/Counts")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<ItemCounts> GetItemCounts(
@@ -434,7 +435,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Item parents returned.</response>
/// <response code="404">Item not found.</response>
/// <returns>Item parents.</returns>
- [HttpGet("/Items/{itemId}/Ancestors")]
+ [HttpGet("Items/{itemId}/Ancestors")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -476,7 +477,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Physical paths returned.</response>
/// <returns>List of physical paths.</returns>
- [HttpGet("/Library/PhysicalPaths")]
+ [HttpGet("Library/PhysicalPaths")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<string>> GetPhysicalPaths()
@@ -491,7 +492,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="isHidden">Optional. Filter by folders that are marked hidden, or not.</param>
/// <response code="200">Media folders returned.</response>
/// <returns>List of user media folders.</returns>
- [HttpGet("/Library/MediaFolders")]
+ [HttpGet("Library/MediaFolders")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetMediaFolders([FromQuery] bool? isHidden)
@@ -521,8 +522,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="tvdbId">The tvdbId.</param>
/// <response code="204">Report success.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Library/Series/Added", Name = "PostAddedSeries")]
- [HttpPost("/Library/Series/Updated")]
+ [HttpPost("Library/Series/Added", Name = "PostAddedSeries")]
+ [HttpPost("Library/Series/Updated")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult PostUpdatedSeries([FromQuery] string? tvdbId)
@@ -551,8 +552,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="imdbId">The imdbId.</param>
/// <response code="204">Report success.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Library/Movies/Added", Name = "PostAddedMovies")]
- [HttpPost("/Library/Movies/Updated")]
+ [HttpPost("Library/Movies/Added", Name = "PostAddedMovies")]
+ [HttpPost("Library/Movies/Updated")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult PostUpdatedMovies([FromRoute] string? tmdbId, [FromRoute] string? imdbId)
@@ -593,7 +594,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="updates">A list of updated media paths.</param>
/// <response code="204">Report success.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Library/Media/Updated")]
+ [HttpPost("Library/Media/Updated")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult PostUpdatedMedia([FromBody, BindRequired] MediaUpdateInfoDto[] updates)
@@ -614,7 +615,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="404">Item not found.</response>
/// <returns>A <see cref="FileResult"/> containing the media stream.</returns>
/// <exception cref="ArgumentException">User can't download or item can't be downloaded.</exception>
- [HttpGet("/Items/{itemId}/Download")]
+ [HttpGet("Items/{itemId}/Download")]
[Authorize(Policy = Policies.Download)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -679,12 +680,12 @@ namespace Jellyfin.Api.Controllers
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls.</param>
/// <response code="200">Similar items returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> containing the similar items.</returns>
- [HttpGet("/Artists/{itemId}/Similar", Name = "GetSimilarArtists2")]
- [HttpGet("/Items/{itemId}/Similar")]
- [HttpGet("/Albums/{itemId}/Similar", Name = "GetSimilarAlbums2")]
- [HttpGet("/Shows/{itemId}/Similar", Name = "GetSimilarShows2")]
- [HttpGet("/Movies/{itemId}/Similar", Name = "GetSimilarMovies2")]
- [HttpGet("/Trailers/{itemId}/Similar", Name = "GetSimilarTrailers2")]
+ [HttpGet("Artists/{itemId}/Similar", Name = "GetSimilarArtists2")]
+ [HttpGet("Items/{itemId}/Similar")]
+ [HttpGet("Albums/{itemId}/Similar", Name = "GetSimilarAlbums2")]
+ [HttpGet("Shows/{itemId}/Similar", Name = "GetSimilarShows2")]
+ [HttpGet("Movies/{itemId}/Similar", Name = "GetSimilarMovies2")]
+ [HttpGet("Trailers/{itemId}/Similar", Name = "GetSimilarTrailers2")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetSimilarItems(
[FromRoute] Guid itemId,
@@ -735,7 +736,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="isNewLibrary">Whether this is a new library.</param>
/// <response code="200">Library options info returned.</response>
/// <returns>Library options info.</returns>
- [HttpGet("/Libraries/AvailableOptions")]
+ [HttpGet("Libraries/AvailableOptions")]
[Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<LibraryOptionsResultDto> GetLibraryOptionsInfo(
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index 5b0f46b02..242cbf191 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -34,6 +34,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The media info controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class MediaInfoController : BaseJellyfinApiController
{
@@ -88,7 +89,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="userId">The user id.</param>
/// <response code="200">Playback info returned.</response>
/// <returns>A <see cref="Task"/> containing a <see cref="PlaybackInfoResponse"/> with the playback information.</returns>
- [HttpGet("/Items/{itemId}/PlaybackInfo")]
+ [HttpGet("Items/{itemId}/PlaybackInfo")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PlaybackInfoResponse>> GetPlaybackInfo([FromRoute] Guid itemId, [FromQuery] Guid? userId)
{
@@ -116,7 +117,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="allowAudioStreamCopy">Whether to allow to copy the audio stream. Default: true.</param>
/// <response code="200">Playback info returned.</response>
/// <returns>A <see cref="Task"/> containing a <see cref="PlaybackInfoResponse"/> with the playback info.</returns>
- [HttpPost("/Items/{itemId}/PlaybackInfo")]
+ [HttpPost("Items/{itemId}/PlaybackInfo")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PlaybackInfoResponse>> GetPostedPlaybackInfo(
[FromRoute] Guid itemId,
@@ -237,7 +238,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableDirectStream">Whether to enable direct stream. Default: true.</param>
/// <response code="200">Media source opened.</response>
/// <returns>A <see cref="Task"/> containing a <see cref="LiveStreamResponse"/>.</returns>
- [HttpPost("/LiveStreams/Open")]
+ [HttpPost("LiveStreams/Open")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<LiveStreamResponse>> OpenLiveStream(
[FromQuery] string? openToken,
@@ -278,7 +279,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="liveStreamId">The livestream id.</param>
/// <response code="204">Livestream closed.</response>
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
- [HttpPost("/LiveStreams/Close")]
+ [HttpPost("LiveStreams/Close")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult CloseLiveStream([FromQuery] string? liveStreamId)
{
@@ -293,7 +294,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Test buffer returned.</response>
/// <response code="400">Size has to be a numer between 0 and 10,000,000.</response>
/// <returns>A <see cref="FileResult"/> with specified bitrate.</returns>
- [HttpGet("/Playback/BitrateTest")]
+ [HttpGet("Playback/BitrateTest")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Produces(MediaTypeNames.Application.Octet)]
diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs
index a6c552790..06c4213fb 100644
--- a/Jellyfin.Api/Controllers/PackageController.cs
+++ b/Jellyfin.Api/Controllers/PackageController.cs
@@ -16,7 +16,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Package Controller.
/// </summary>
- [Route("Packages")]
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class PackageController : BaseJellyfinApiController
{
@@ -41,7 +41,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="assemblyGuid">The GUID of the associated assembly.</param>
/// <response code="200">Package retrieved.</response>
/// <returns>A <see cref="PackageInfo"/> containing package information.</returns>
- [HttpGet("/{name}")]
+ [HttpGet("Packages/{name}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PackageInfo>> GetPackageInfo(
[FromRoute] [Required] string? name,
@@ -61,7 +61,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Available packages returned.</response>
/// <returns>An <see cref="PackageInfo"/> containing available packages information.</returns>
- [HttpGet]
+ [HttpGet("Packages")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IEnumerable<PackageInfo>> GetPackages()
{
@@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Package found.</response>
/// <response code="404">Package not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the package could not be found.</returns>
- [HttpPost("/Installed/{name}")]
+ [HttpPost("Packages/Installed/{name}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Policy = Policies.RequiresElevation)]
@@ -111,7 +111,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="packageId">Installation Id.</param>
/// <response code="204">Installation cancelled.</response>
/// <returns>A <see cref="NoContentResult"/> on successfully cancelling a package installation.</returns>
- [HttpDelete("/Installing/{packageId}")]
+ [HttpDelete("Packages/Installing/{packageId}")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult CancelPackageInstallation(
@@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Package repositories returned.</response>
/// <returns>An <see cref="OkResult"/> containing the list of package repositories.</returns>
- [HttpGet("/Repositories")]
+ [HttpGet("Repositories")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<RepositoryInfo>> GetRepositories()
@@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="repositoryInfos">The list of package repositories.</param>
/// <response code="204">Package repositories saved.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpOptions("/Repositories")]
+ [HttpOptions("Repositories")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SetRepositories([FromBody] List<RepositoryInfo> repositoryInfos)
diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs
index 3ebd003f1..0422bfe72 100644
--- a/Jellyfin.Api/Controllers/PlaystateController.cs
+++ b/Jellyfin.Api/Controllers/PlaystateController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Playstate controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class PlaystateController : BaseJellyfinApiController
{
@@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="datePlayed">Optional. The date the item was played.</param>
/// <response code="200">Item marked as played.</response>
/// <returns>An <see cref="OkResult"/> containing the <see cref="UserItemDataDto"/>.</returns>
- [HttpPost("/Users/{userId}/PlayedItems/{itemId}")]
+ [HttpPost("Users/{userId}/PlayedItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<UserItemDataDto> MarkPlayedItem(
[FromRoute] Guid userId,
@@ -93,7 +94,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">Item marked as unplayed.</response>
/// <returns>A <see cref="OkResult"/> containing the <see cref="UserItemDataDto"/>.</returns>
- [HttpDelete("/Users/{userId}/PlayedItem/{itemId}")]
+ [HttpDelete("Users/{userId}/PlayedItem/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<UserItemDataDto> MarkUnplayedItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -115,7 +116,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playbackStartInfo">The playback start info.</param>
/// <response code="204">Playback start recorded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Playing")]
+ [HttpPost("Sessions/Playing")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> ReportPlaybackStart([FromBody] PlaybackStartInfo playbackStartInfo)
{
@@ -131,7 +132,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playbackProgressInfo">The playback progress info.</param>
/// <response code="204">Playback progress recorded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Playing/Progress")]
+ [HttpPost("Sessions/Playing/Progress")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> ReportPlaybackProgress([FromBody] PlaybackProgressInfo playbackProgressInfo)
{
@@ -147,7 +148,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playSessionId">Playback session id.</param>
/// <response code="204">Playback session pinged.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Playing/Ping")]
+ [HttpPost("Sessions/Playing/Ping")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult PingPlaybackSession([FromQuery] string playSessionId)
{
@@ -161,7 +162,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playbackStopInfo">The playback stop info.</param>
/// <response code="204">Playback stop recorded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Playing/Stopped")]
+ [HttpPost("Sessions/Playing/Stopped")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> ReportPlaybackStopped([FromBody] PlaybackStopInfo playbackStopInfo)
{
@@ -190,7 +191,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="canSeek">Indicates if the client can seek.</param>
/// <response code="204">Play start recorded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Users/{userId}/PlayingItems/{itemId}")]
+ [HttpPost("Users/{userId}/PlayingItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")]
public async Task<ActionResult> OnPlaybackStart(
@@ -240,7 +241,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="isMuted">Indicates if the player is muted.</param>
/// <response code="204">Play progress recorded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Users/{userId}/PlayingItems/{itemId}/Progress")]
+ [HttpPost("Users/{userId}/PlayingItems/{itemId}/Progress")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")]
public async Task<ActionResult> OnPlaybackProgress(
@@ -292,7 +293,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playSessionId">The play session id.</param>
/// <response code="204">Playback stop recorded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpDelete("/Users/{userId}/PlayingItems/{itemId}")]
+ [HttpDelete("Users/{userId}/PlayingItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")]
public async Task<ActionResult> OnPlaybackStopped(
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index 770d74838..fe10f0f1b 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -188,7 +188,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>Not Implemented.</returns>
/// <exception cref="NotImplementedException">This endpoint is not implemented.</exception>
[Obsolete("Paid plugins are not supported")]
- [HttpGet("/Registrations/{name}")]
+ [HttpGet("Registrations/{name}")]
[ProducesResponseType(StatusCodes.Status501NotImplemented)]
public ActionResult GetRegistration([FromRoute] string? name)
{
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index 1b300e0d8..3e6f577f1 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -23,6 +23,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The session controller.
/// </summary>
+ [Route("")]
public class SessionController : BaseJellyfinApiController
{
private readonly ISessionManager _sessionManager;
@@ -57,7 +58,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="activeWithinSeconds">Optional. Filter by sessions that were active in the last n seconds.</param>
/// <response code="200">List of sessions returned.</response>
/// <returns>An <see cref="IEnumerable{SessionInfo}"/> with the available sessions.</returns>
- [HttpGet("/Sessions")]
+ [HttpGet("Sessions")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<SessionInfo>> GetSessions(
@@ -120,7 +121,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemName">The name of the item.</param>
/// <response code="204">Instruction sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/Viewing")]
+ [HttpPost("Sessions/{sessionId}/Viewing")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult DisplayContent(
[FromRoute] string? sessionId,
@@ -154,7 +155,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playRequest">The <see cref="PlayRequest"/>.</param>
/// <response code="204">Instruction sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/Playing")]
+ [HttpPost("Sessions/{sessionId}/Playing")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult Play(
[FromRoute] string? sessionId,
@@ -188,7 +189,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="playstateRequest">The <see cref="PlaystateRequest"/>.</param>
/// <response code="204">Playstate command sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/Playing/{command}")]
+ [HttpPost("Sessions/{sessionId}/Playing/{command}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SendPlaystateCommand(
[FromRoute] string? sessionId,
@@ -210,7 +211,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="command">The command to send.</param>
/// <response code="204">System command sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/System/{command}")]
+ [HttpPost("Sessions/{sessionId}/System/{command}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SendSystemCommand(
[FromRoute] string? sessionId,
@@ -241,7 +242,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="command">The command to send.</param>
/// <response code="204">General command sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/Command/{command}")]
+ [HttpPost("Sessions/{sessionId}/Command/{command}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SendGeneralCommand(
[FromRoute] string? sessionId,
@@ -267,7 +268,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="command">The <see cref="GeneralCommand"/>.</param>
/// <response code="204">Full general command sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/Command")]
+ [HttpPost("Sessions/{sessionId}/Command")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SendFullGeneralCommand(
[FromRoute] string? sessionId,
@@ -300,7 +301,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="timeoutMs">The message timeout. If omitted the user will have to confirm viewing the message.</param>
/// <response code="204">Message sent.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/Message")]
+ [HttpPost("Sessions/{sessionId}/Message")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SendMessageCommand(
[FromRoute] string? sessionId,
@@ -327,7 +328,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="userId">The user id.</param>
/// <response code="204">User added to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/{sessionId}/User/{userId}")]
+ [HttpPost("Sessions/{sessionId}/User/{userId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult AddUserToSession(
[FromRoute] string? sessionId,
@@ -344,7 +345,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="userId">The user id.</param>
/// <response code="204">User removed from session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpDelete("/Sessions/{sessionId}/User/{userId}")]
+ [HttpDelete("Sessions/{sessionId}/User/{userId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult RemoveUserFromSession(
[FromRoute] string? sessionId,
@@ -365,7 +366,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="supportsPersistentIdentifier">Determines whether the device supports a unique identifier.</param>
/// <response code="204">Capabilities posted.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Capabilities")]
+ [HttpPost("Sessions/Capabilities")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult PostCapabilities(
[FromQuery] string? id,
@@ -398,7 +399,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="capabilities">The <see cref="ClientCapabilities"/>.</param>
/// <response code="204">Capabilities updated.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Capabilities/Full")]
+ [HttpPost("Sessions/Capabilities/Full")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult PostFullCapabilities(
[FromQuery] string? id,
@@ -421,7 +422,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">The item id.</param>
/// <response code="204">Session reported to server.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Viewing")]
+ [HttpPost("Sessions/Viewing")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult ReportViewing(
[FromQuery] string? sessionId,
@@ -438,7 +439,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="204">Session end reported to server.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Sessions/Logout")]
+ [HttpPost("Sessions/Logout")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult ReportSessionEnded()
{
@@ -453,7 +454,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Auth providers retrieved.</response>
/// <returns>An <see cref="IEnumerable{NameIdPair}"/> with the auth providers.</returns>
- [HttpGet("/Auth/Providers")]
+ [HttpGet("Auth/Providers")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<NameIdPair>> GetAuthProviders()
{
@@ -465,7 +466,7 @@ namespace Jellyfin.Api.Controllers
/// </summary>
/// <response code="200">Password reset providers retrieved.</response>
/// <returns>An <see cref="IEnumerable{NameIdPair}"/> with the password reset providers.</returns>
- [HttpGet("/Auto/PasswordResetProviders")]
+ [HttpGet("Auto/PasswordResetProviders")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<NameIdPair>> GetPasswordResetProviders()
{
diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs
index f8c19d15c..22144ab1a 100644
--- a/Jellyfin.Api/Controllers/SubtitleController.cs
+++ b/Jellyfin.Api/Controllers/SubtitleController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Subtitle controller.
/// </summary>
+ [Route("")]
public class SubtitleController : BaseJellyfinApiController
{
private readonly ILibraryManager _libraryManager;
@@ -80,7 +81,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Subtitle deleted.</response>
/// <response code="404">Item not found.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpDelete("/Videos/{itemId}/Subtitles/{index}")]
+ [HttpDelete("Videos/{itemId}/Subtitles/{index}")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -107,7 +108,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="isPerfectMatch">Optional. Only show subtitles which are a perfect match.</param>
/// <response code="200">Subtitles retrieved.</response>
/// <returns>An array of <see cref="RemoteSubtitleInfo"/>.</returns>
- [HttpGet("/Items/{itemId}/RemoteSearch/Subtitles/{language}")]
+ [HttpGet("Items/{itemId}/RemoteSearch/Subtitles/{language}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<IEnumerable<RemoteSubtitleInfo>>> SearchRemoteSubtitles(
@@ -127,7 +128,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="subtitleId">The subtitle id.</param>
/// <response code="204">Subtitle downloaded.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("/Items/{itemId}/RemoteSearch/Subtitles/{subtitleId}")]
+ [HttpPost("Items/{itemId}/RemoteSearch/Subtitles/{subtitleId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> DownloadRemoteSubtitles(
@@ -157,7 +158,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="id">The item id.</param>
/// <response code="200">File returned.</response>
/// <returns>A <see cref="FileStreamResult"/> with the subtitle file.</returns>
- [HttpGet("/Providers/Subtitles/Subtitles/{id}")]
+ [HttpGet("Providers/Subtitles/Subtitles/{id}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Octet)]
@@ -181,8 +182,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="startPositionTicks">Optional. The start position of the subtitle in ticks.</param>
/// <response code="200">File returned.</response>
/// <returns>A <see cref="FileContentResult"/> with the subtitle file.</returns>
- [HttpGet("/Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{format}")]
- [HttpGet("/Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks?}/Stream.{format}", Name = "GetSubtitle_2")]
+ [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{format}")]
+ [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks?}/Stream.{format}", Name = "GetSubtitle_2")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> GetSubtitle(
[FromRoute, Required] Guid itemId,
@@ -247,7 +248,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="segmentLength">The subtitle segment length.</param>
/// <response code="200">Subtitle playlist retrieved.</response>
/// <returns>A <see cref="FileContentResult"/> with the HLS subtitle playlist.</returns>
- [HttpGet("/Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/subtitles.m3u8")]
+ [HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/subtitles.m3u8")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs
index 55759f316..42db6b6a1 100644
--- a/Jellyfin.Api/Controllers/SuggestionsController.cs
+++ b/Jellyfin.Api/Controllers/SuggestionsController.cs
@@ -16,6 +16,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The suggestions controller.
/// </summary>
+ [Route("")]
public class SuggestionsController : BaseJellyfinApiController
{
private readonly IDtoService _dtoService;
@@ -49,7 +50,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableTotalRecordCount">Whether to enable the total record count.</param>
/// <response code="200">Suggestions returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the suggestions.</returns>
- [HttpGet("/Users/{userId}/Suggestions")]
+ [HttpGet("Users/{userId}/Suggestions")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetSuggestions(
[FromRoute] Guid userId,
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index fbab7948f..5157b08ae 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
/// <param name="enableImages">Optional, include image information in output.</param>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the trailers.</returns>
- [HttpGet("/Trailers")]
+ [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetTrailers(
[FromQuery] Guid? userId,
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index 5a9bec2b0..75df16aa7 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The universal audio controller.
/// </summary>
+ [Route("")]
public class UniversalAudioController : BaseJellyfinApiController
{
private readonly IAuthorizationContext _authorizationContext;
@@ -68,10 +69,10 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Audio stream returned.</response>
/// <response code="302">Redirected to remote audio stream.</response>
/// <returns>A <see cref="Task"/> containing the audio file.</returns>
- [HttpGet("/Audio/{itemId}/universal")]
- [HttpGet("/Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")]
- [HttpHead("/Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")]
- [HttpHead("/Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")]
+ [HttpGet("Audio/{itemId}/universal")]
+ [HttpGet("Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")]
+ [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")]
+ [HttpHead("Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status302Found)]
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 482baf641..2ce5c7e56 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -450,7 +450,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="request">The create user by name request body.</param>
/// <response code="200">User created.</response>
/// <returns>An <see cref="UserDto"/> of the new user.</returns>
- [HttpPost("/Users/New")]
+ [HttpPost("New")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<UserDto>> CreateUserByName([FromBody] CreateUserByName request)
diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index cedda3b9d..f55ff6f3d 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -25,6 +25,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// User library controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class UserLibraryController : BaseJellyfinApiController
{
@@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">Item returned.</response>
/// <returns>An <see cref="OkResult"/> containing the d item.</returns>
- [HttpGet("/Users/{userId}/Items/{itemId}")]
+ [HttpGet("Users/{userId}/Items/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<BaseItemDto>> GetItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -90,7 +91,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="userId">User id.</param>
/// <response code="200">Root folder returned.</response>
/// <returns>An <see cref="OkResult"/> containing the user's root folder.</returns>
- [HttpGet("/Users/{userId}/Items/Root")]
+ [HttpGet("Users/{userId}/Items/Root")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<BaseItemDto> GetRootFolder([FromRoute] Guid userId)
{
@@ -107,7 +108,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">Intros returned.</response>
/// <returns>An <see cref="OkResult"/> containing the intros to play.</returns>
- [HttpGet("/Users/{userId}/Items/{itemId}/Intros")]
+ [HttpGet("Users/{userId}/Items/{itemId}/Intros")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetIntros([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -135,7 +136,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">Item marked as favorite.</response>
/// <returns>An <see cref="OkResult"/> containing the <see cref="UserItemDataDto"/>.</returns>
- [HttpPost("/Users/{userId}/FavoriteItems/{itemId}")]
+ [HttpPost("Users/{userId}/FavoriteItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<UserItemDataDto> MarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -149,7 +150,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">Item unmarked as favorite.</response>
/// <returns>An <see cref="OkResult"/> containing the <see cref="UserItemDataDto"/>.</returns>
- [HttpDelete("/Users/{userId}/FavoriteItems/{itemId}")]
+ [HttpDelete("Users/{userId}/FavoriteItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<UserItemDataDto> UnmarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -163,7 +164,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">Personal rating removed.</response>
/// <returns>An <see cref="OkResult"/> containing the <see cref="UserItemDataDto"/>.</returns>
- [HttpDelete("/Users/{userId}/Items/{itemId}/Rating")]
+ [HttpDelete("Users/{userId}/Items/{itemId}/Rating")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<UserItemDataDto> DeleteUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -178,7 +179,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="likes">Whether this <see cref="UpdateUserItemRating" /> is likes.</param>
/// <response code="200">Item rating updated.</response>
/// <returns>An <see cref="OkResult"/> containing the <see cref="UserItemDataDto"/>.</returns>
- [HttpPost("/Users/{userId}/Items/{itemId}/Rating")]
+ [HttpPost("Users/{userId}/Items/{itemId}/Rating")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<UserItemDataDto> UpdateUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId, [FromQuery] bool? likes)
{
@@ -192,7 +193,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">An <see cref="OkResult"/> containing the item's local trailers.</response>
/// <returns>The items local trailers.</returns>
- [HttpGet("/Users/{userId}/Items/{itemId}/LocalTrailers")]
+ [HttpGet("Users/{userId}/Items/{itemId}/LocalTrailers")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<BaseItemDto>> GetLocalTrailers([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -227,7 +228,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemId">Item id.</param>
/// <response code="200">Special features returned.</response>
/// <returns>An <see cref="OkResult"/> containing the special features.</returns>
- [HttpGet("/Users/{userId}/Items/{itemId}/SpecialFeatures")]
+ [HttpGet("Users/{userId}/Items/{itemId}/SpecialFeatures")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<BaseItemDto>> GetSpecialFeatures([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -260,7 +261,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="groupItems">Whether or not to group items into a parent container.</param>
/// <response code="200">Latest media returned.</response>
/// <returns>An <see cref="OkResult"/> containing the latest media.</returns>
- [HttpGet("/Users/{userId}/Items/Latest")]
+ [HttpGet("Users/{userId}/Items/Latest")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<BaseItemDto>> GetLatestMedia(
[FromRoute] Guid userId,
diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs
index f4bd451ef..6df7cc779 100644
--- a/Jellyfin.Api/Controllers/UserViewsController.cs
+++ b/Jellyfin.Api/Controllers/UserViewsController.cs
@@ -21,6 +21,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// User views controller.
/// </summary>
+ [Route("")]
public class UserViewsController : BaseJellyfinApiController
{
private readonly IUserManager _userManager;
@@ -60,7 +61,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="includeHidden">Whether or not to include hidden content.</param>
/// <response code="200">User views returned.</response>
/// <returns>An <see cref="OkResult"/> containing the user views.</returns>
- [HttpGet("/Users/{userId}/Views")]
+ [HttpGet("Users/{userId}/Views")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetUserViews(
[FromRoute] Guid userId,
@@ -122,7 +123,7 @@ namespace Jellyfin.Api.Controllers
/// An <see cref="OkResult"/> containing the user view grouping options
/// or a <see cref="NotFoundResult"/> if user not found.
/// </returns>
- [HttpGet("/Users/{userId}/GroupingOptions")]
+ [HttpGet("Users/{userId}/GroupingOptions")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<IEnumerable<SpecialViewOptionDto>> GetGroupingOptions([FromRoute] Guid userId)
diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs
index 8520dd163..76188f46d 100644
--- a/Jellyfin.Api/Controllers/VideoHlsController.cs
+++ b/Jellyfin.Api/Controllers/VideoHlsController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The video hls controller.
/// </summary>
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class VideoHlsController : BaseJellyfinApiController
{
@@ -158,7 +159,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="enableSubtitlesInManifest">Optional. Whether to enable subtitles in the manifest.</param>
/// <response code="200">Hls live stream retrieved.</response>
/// <returns>A <see cref="FileResult"/> containing the hls file.</returns>
- [HttpGet("/Videos/{itemId}/live.m3u8")]
+ [HttpGet("Videos/{itemId}/live.m3u8")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> GetLiveHlsStream(
[FromRoute] Guid itemId,
@@ -271,7 +272,7 @@ namespace Jellyfin.Api.Controllers
};
var cancellationTokenSource = new CancellationTokenSource();
- var state = await StreamingHelpers.GetStreamingState(
+ using var state = await StreamingHelpers.GetStreamingState(
streamingRequest,
Request,
_authContext,