diff --git a/Jellyfin.Api/Controllers/AlbumsController.cs b/Jellyfin.Api/Controllers/AlbumsController.cs
index 01ba7fc326..190d4bd07c 100644
--- a/Jellyfin.Api/Controllers/AlbumsController.cs
+++ b/Jellyfin.Api/Controllers/AlbumsController.cs
@@ -17,6 +17,7 @@ namespace Jellyfin.Api.Controllers
///
/// The albums controller.
///
+ [Route("")]
public class AlbumsController : BaseJellyfinApiController
{
private readonly IUserManager _userManager;
@@ -48,7 +49,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The maximum number of records to return.
/// Similar albums returned.
/// A with similar albums.
- [HttpGet("/Albums/{albumId}/Similar")]
+ [HttpGet("Albums/{albumId}/Similar")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetSimilarAlbums(
[FromRoute] string albumId,
@@ -80,7 +81,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The maximum number of records to return.
/// Similar artists returned.
/// A with similar artists.
- [HttpGet("/Artists/{artistId}/Similar")]
+ [HttpGet("Artists/{artistId}/Similar")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetSimilarArtists(
[FromRoute] string artistId,
diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs
index e033c50d5c..a7bdb24f6d 100644
--- a/Jellyfin.Api/Controllers/DashboardController.cs
+++ b/Jellyfin.Api/Controllers/DashboardController.cs
@@ -20,6 +20,7 @@ namespace Jellyfin.Api.Controllers
///
/// The dashboard controller.
///
+ [Route("")]
public class DashboardController : BaseJellyfinApiController
{
private readonly ILogger _logger;
@@ -64,7 +65,7 @@ namespace Jellyfin.Api.Controllers
/// ConfigurationPages returned.
/// Server still loading.
/// An with infos about the plugins.
- [HttpGet("/web/ConfigurationPages")]
+ [HttpGet("web/ConfigurationPages")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult> GetConfigurationPages(
@@ -118,7 +119,7 @@ namespace Jellyfin.Api.Controllers
/// ConfigurationPage returned.
/// Plugin configuration page not found.
/// The configuration page.
- [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
///
/// Robots.txt returned.
/// The robots.txt.
- [HttpGet("/robots.txt")]
+ [HttpGet("robots.txt")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ApiExplorerSettings(IgnoreApi = true)]
public ActionResult GetRobotsTxt()
@@ -187,7 +188,7 @@ namespace Jellyfin.Api.Controllers
/// Web client returned.
/// Server does not host a web client.
/// The resource.
- [HttpGet("/web/{*resourceName}")]
+ [HttpGet("web/{*resourceName}")]
[ApiExplorerSettings(IgnoreApi = true)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -218,7 +219,7 @@ namespace Jellyfin.Api.Controllers
///
/// Favicon.ico returned.
/// The favicon.
- [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 c4f79ce950..ec65cb95a2 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -37,6 +37,7 @@ namespace Jellyfin.Api.Controllers
///
/// Dynamic hls controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class DynamicHlsController : BaseJellyfinApiController
{
@@ -164,8 +165,8 @@ namespace Jellyfin.Api.Controllers
/// Enable adaptive bitrate streaming.
/// Video stream returned.
/// A containing the playlist file.
- [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 GetMasterHlsVideoPlaylist(
[FromRoute] Guid itemId,
@@ -334,8 +335,8 @@ namespace Jellyfin.Api.Controllers
/// Enable adaptive bitrate streaming.
/// Audio stream returned.
/// A containing the playlist file.
- [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 GetMasterHlsAudioPlaylist(
[FromRoute] Guid itemId,
@@ -503,7 +504,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The streaming options.
/// Video stream returned.
/// A containing the audio file.
- [HttpGet("/Videos/{itemId}/main.m3u8")]
+ [HttpGet("Videos/{itemId}/main.m3u8")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task GetVariantHlsVideoPlaylist(
[FromRoute] Guid itemId,
@@ -668,7 +669,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The streaming options.
/// Audio stream returned.
/// A containing the audio file.
- [HttpGet("/Audio/{itemId}/main.m3u8")]
+ [HttpGet("Audio/{itemId}/main.m3u8")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task GetVariantHlsAudioPlaylist(
[FromRoute] Guid itemId,
@@ -835,7 +836,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The streaming options.
/// Video stream returned.
/// A containing the audio file.
- [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 GetHlsVideoSegment(
@@ -1004,7 +1005,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The streaming options.
/// Video stream returned.
/// A containing the audio file.
- [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 GetHlsAudioSegment(
diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs
index 9ba5e11618..2a567c8461 100644
--- a/Jellyfin.Api/Controllers/FilterController.cs
+++ b/Jellyfin.Api/Controllers/FilterController.cs
@@ -18,6 +18,7 @@ namespace Jellyfin.Api.Controllers
///
/// Filters controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class FilterController : BaseJellyfinApiController
{
@@ -44,7 +45,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Filter by MediaType. Allows multiple, comma delimited.
/// Legacy filters retrieved.
/// Legacy query filters.
- [HttpGet("/Items/Filters")]
+ [HttpGet("Items/Filters")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetQueryFiltersLegacy(
[FromQuery] Guid? userId,
@@ -133,7 +134,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Search recursive.
/// Filters retrieved.
/// Query filters.
- [HttpGet("/Items/Filters2")]
+ [HttpGet("Items/Filters2")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetQueryFilters(
[FromQuery] Guid? userId,
diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs
index 7bf9326a71..e4a6842bce 100644
--- a/Jellyfin.Api/Controllers/HlsSegmentController.cs
+++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
///
/// The hls segment controller.
///
+ [Route("")]
public class HlsSegmentController : BaseJellyfinApiController
{
private readonly IFileSystem _fileSystem;
@@ -50,8 +51,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the audio stream.
// 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
/// The playlist id.
/// Hls video playlist returned.
/// A containing the playlist.
- [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
/// The play session id.
/// Encoding stopped successfully.
/// A indicating success.
- [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
/// A containing the video segment.
// 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 3a445b1b3c..360164ad4f 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
///
/// Image controller.
///
+ [Route("")]
public class ImageController : BaseJellyfinApiController
{
private readonly IUserManager _userManager;
@@ -81,8 +82,8 @@ namespace Jellyfin.Api.Controllers
/// Image updated.
/// User does not have permission to delete the image.
/// A .
- [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
/// Image deleted.
/// User does not have permission to delete the image.
/// A .
- [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
/// Image deleted.
/// Item not found.
/// A on success, or a if item not found.
- [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
/// Image saved.
/// Item not found.
/// A on success, or a if item not found.
- [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
/// Image index updated.
/// Item not found.
/// A on success, or a if item not found.
- [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
/// Item images returned.
/// Item not found.
/// The list of image infos on success, or if item not found.
- [HttpGet("/Items/{itemId}/Images")]
+ [HttpGet("Items/{itemId}/Images")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult> GetItemImageInfos([FromRoute] Guid itemId)
@@ -341,10 +342,10 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetItemImage(
@@ -421,8 +422,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetItemImage2(
@@ -499,8 +500,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetArtistImage(
@@ -577,8 +578,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetGenreImage(
@@ -655,8 +656,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetMusicGenreImage(
@@ -733,8 +734,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetPersonImage(
@@ -811,8 +812,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetStudioImage(
@@ -889,8 +890,8 @@ namespace Jellyfin.Api.Controllers
/// A containing the file stream on success,
/// or a if item not found.
///
- [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 GetUserImage(
diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs
index bb980af3e7..8ca232ceff 100644
--- a/Jellyfin.Api/Controllers/InstantMixController.cs
+++ b/Jellyfin.Api/Controllers/InstantMixController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
///
/// The instant mix controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class InstantMixController : BaseJellyfinApiController
{
@@ -59,7 +60,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The image types to include in the output.
/// Instant playlist returned.
/// A with the playlist items.
- [HttpGet("/Songs/{id}/InstantMix")]
+ [HttpGet("Songs/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetInstantMixFromSong(
[FromRoute] Guid id,
@@ -96,7 +97,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The image types to include in the output.
/// Instant playlist returned.
/// A with the playlist items.
- [HttpGet("/Albums/{id}/InstantMix")]
+ [HttpGet("Albums/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetInstantMixFromAlbum(
[FromRoute] Guid id,
@@ -133,7 +134,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The image types to include in the output.
/// Instant playlist returned.
/// A with the playlist items.
- [HttpGet("/Playlists/{id}/InstantMix")]
+ [HttpGet("Playlists/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetInstantMixFromPlaylist(
[FromRoute] Guid id,
@@ -170,7 +171,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The image types to include in the output.
/// Instant playlist returned.
/// A with the playlist items.
- [HttpGet("/MusicGenres/{name}/InstantMix")]
+ [HttpGet("MusicGenres/{name}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetInstantMixFromMusicGenre(
[FromRoute] string? name,
@@ -206,7 +207,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The image types to include in the output.
/// Instant playlist returned.
/// A with the playlist items.
- [HttpGet("/Artists/InstantMix")]
+ [HttpGet("Artists/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetInstantMixFromArtists(
[FromRoute] Guid id,
@@ -243,7 +244,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The image types to include in the output.
/// Instant playlist returned.
/// A with the playlist items.
- [HttpGet("/MusicGenres/InstantMix")]
+ [HttpGet("MusicGenres/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetInstantMixFromMusicGenres(
[FromRoute] Guid id,
@@ -280,7 +281,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The image types to include in the output.
/// Instant playlist returned.
/// A with the playlist items.
- [HttpGet("/Items/{id}/InstantMix")]
+ [HttpGet("Items/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetInstantMixFromItem(
[FromRoute] Guid id,
diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs
index 44709d0ee6..0d9dffbfe6 100644
--- a/Jellyfin.Api/Controllers/ItemLookupController.cs
+++ b/Jellyfin.Api/Controllers/ItemLookupController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
///
/// Item lookup controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class ItemLookupController : BaseJellyfinApiController
{
@@ -68,7 +69,7 @@ namespace Jellyfin.Api.Controllers
/// External id info retrieved.
/// Item not found.
/// List of external id info.
- [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 that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/Movie")]
+ [HttpPost("Items/RemoteSearch/Movie")]
public async Task>> GetMovieRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -109,7 +110,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/Trailer")]
+ [HttpPost("Items/RemoteSearch/Trailer")]
public async Task>> GetTrailerRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -126,7 +127,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/MusicVideo")]
+ [HttpPost("Items/RemoteSearch/MusicVideo")]
public async Task>> GetMusicVideoRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -143,7 +144,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/Series")]
+ [HttpPost("Items/RemoteSearch/Series")]
public async Task>> GetSeriesRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -160,7 +161,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/BoxSet")]
+ [HttpPost("Items/RemoteSearch/BoxSet")]
public async Task>> GetBoxSetRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -177,7 +178,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/MusicArtist")]
+ [HttpPost("Items/RemoteSearch/MusicArtist")]
public async Task>> GetMusicArtistRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -194,7 +195,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/MusicAlbum")]
+ [HttpPost("Items/RemoteSearch/MusicAlbum")]
public async Task>> GetMusicAlbumRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -211,7 +212,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/Person")]
+ [HttpPost("Items/RemoteSearch/Person")]
[Authorize(Policy = Policies.RequiresElevation)]
public async Task>> GetPersonRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
@@ -229,7 +230,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the list of remote search results.
///
- [HttpPost("/Items/RemoteSearch/Book")]
+ [HttpPost("Items/RemoteSearch/Book")]
public async Task>> GetBookRemoteSearchResults([FromBody, BindRequired] RemoteSearchQuery query)
{
var results = await _providerManager.GetRemoteSearchResults(query, CancellationToken.None)
@@ -247,7 +248,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an containing the images file stream.
///
- [HttpGet("/Items/RemoteSearch/Image")]
+ [HttpGet("Items/RemoteSearch/Image")]
public async Task GetRemoteSearchImage(
[FromQuery, Required] string imageUrl,
[FromQuery, Required] string providerName)
@@ -291,7 +292,7 @@ namespace Jellyfin.Api.Controllers
/// A that represents the asynchronous operation to get the remote search results.
/// The task result contains an .
///
- [HttpPost("/Items/RemoteSearch/Apply/{id}")]
+ [HttpPost("Items/RemoteSearch/Apply/{id}")]
[Authorize(Policy = Policies.RequiresElevation)]
public async Task ApplySearchCriteria(
[FromRoute] Guid itemId,
diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs
index c9b2aafcca..a5d9d36a33 100644
--- a/Jellyfin.Api/Controllers/ItemUpdateController.cs
+++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs
@@ -24,6 +24,7 @@ namespace Jellyfin.Api.Controllers
///
/// Item update controller.
///
+ [Route("")]
[Authorize(Policy = Policies.RequiresElevation)]
public class ItemUpdateController : BaseJellyfinApiController
{
@@ -63,7 +64,7 @@ namespace Jellyfin.Api.Controllers
/// Item updated.
/// Item not found.
/// An on success, or a if the item could not be found.
- [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
/// Item metadata editor returned.
/// Item not found.
/// An on success containing the metadata editor, or a if the item could not be found.
- [HttpGet("/Items/{itemId}/MetadataEditor")]
+ [HttpGet("Items/{itemId}/MetadataEditor")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetMetadataEditorInfo([FromRoute] Guid itemId)
@@ -190,7 +191,7 @@ namespace Jellyfin.Api.Controllers
/// Item content type updated.
/// Item not found.
/// An on success, or a if the item could not be found.
- [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 354741ced1..1b8b68313b 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -23,6 +23,7 @@ namespace Jellyfin.Api.Controllers
///
/// The items controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class ItemsController : BaseJellyfinApiController
{
@@ -139,8 +140,8 @@ namespace Jellyfin.Api.Controllers
/// Optional. Enable the total record count.
/// Optional, include image information in output.
/// A with the items.
- [HttpGet("/Items")]
- [HttpGet("/Users/{uId}/Items", Name = "GetItems_2")]
+ [HttpGet("Items")]
+ [HttpGet("Users/{uId}/Items", Name = "GetItems_2")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetItems(
[FromRoute] Guid? uId,
@@ -523,7 +524,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Include image information in output.
/// Items returned.
/// A with the items that are resumable.
- [HttpGet("/Users/{userId}/Items/Resume")]
+ [HttpGet("Users/{userId}/Items/Resume")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetResumeItems(
[FromRoute] Guid userId,
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index 0ec7e2b8c0..a4637752d1 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -43,6 +43,7 @@ namespace Jellyfin.Api.Controllers
///
/// Library Controller.
///
+ [Route("")]
public class LibraryController : BaseJellyfinApiController
{
private readonly IProviderManager _providerManager;
@@ -100,7 +101,7 @@ namespace Jellyfin.Api.Controllers
/// File stream returned.
/// Item not found.
/// A with the original file.
- [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
///
/// Critic reviews returned.
/// The list of critic reviews.
- [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
/// Theme songs returned.
/// Item not found.
/// The item theme songs.
- [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
/// Theme videos returned.
/// Item not found.
/// The item theme videos.
- [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
/// Theme songs and videos returned.
/// Item not found.
/// The item theme videos.
- [HttpGet("/Items/{itemId}/ThemeMedia")]
+ [HttpGet("Items/{itemId}/ThemeMedia")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetThemeMedia(
@@ -302,7 +303,7 @@ namespace Jellyfin.Api.Controllers
///
/// Library scan started.
/// A .
- [HttpGet("/Library/Refresh")]
+ [HttpGet("Library/Refresh")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task RefreshLibrary()
@@ -326,7 +327,7 @@ namespace Jellyfin.Api.Controllers
/// Item deleted.
/// Unauthorized access.
/// A .
- [HttpDelete("/Items/{itemId}")]
+ [HttpDelete("Items/{itemId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
@@ -356,7 +357,7 @@ namespace Jellyfin.Api.Controllers
/// Items deleted.
/// Unauthorized access.
/// A .
- [HttpDelete("/Items")]
+ [HttpDelete("Items")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
@@ -400,7 +401,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Get counts of favorite items.
/// Item counts returned.
/// Item counts.
- [HttpGet("/Items/Counts")]
+ [HttpGet("Items/Counts")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetItemCounts(
@@ -434,7 +435,7 @@ namespace Jellyfin.Api.Controllers
/// Item parents returned.
/// Item not found.
/// Item parents.
- [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
///
/// Physical paths returned.
/// List of physical paths.
- [HttpGet("/Library/PhysicalPaths")]
+ [HttpGet("Library/PhysicalPaths")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetPhysicalPaths()
@@ -491,7 +492,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Filter by folders that are marked hidden, or not.
/// Media folders returned.
/// List of user media folders.
- [HttpGet("/Library/MediaFolders")]
+ [HttpGet("Library/MediaFolders")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetMediaFolders([FromQuery] bool? isHidden)
@@ -521,8 +522,8 @@ namespace Jellyfin.Api.Controllers
/// The tvdbId.
/// Report success.
/// A .
- [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
/// The imdbId.
/// Report success.
/// A .
- [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
/// A list of updated media paths.
/// Report success.
/// A .
- [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
/// Item not found.
/// A containing the media stream.
/// User can't download or item can't be downloaded.
- [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
/// 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.
/// Similar items returned.
/// A containing the similar items.
- [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> GetSimilarItems(
[FromRoute] Guid itemId,
@@ -735,7 +736,7 @@ namespace Jellyfin.Api.Controllers
/// Whether this is a new library.
/// Library options info returned.
/// Library options info.
- [HttpGet("/Libraries/AvailableOptions")]
+ [HttpGet("Libraries/AvailableOptions")]
[Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetLibraryOptionsInfo(
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index 5b0f46b02e..242cbf1918 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -34,6 +34,7 @@ namespace Jellyfin.Api.Controllers
///
/// The media info controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class MediaInfoController : BaseJellyfinApiController
{
@@ -88,7 +89,7 @@ namespace Jellyfin.Api.Controllers
/// The user id.
/// Playback info returned.
/// A containing a with the playback information.
- [HttpGet("/Items/{itemId}/PlaybackInfo")]
+ [HttpGet("Items/{itemId}/PlaybackInfo")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetPlaybackInfo([FromRoute] Guid itemId, [FromQuery] Guid? userId)
{
@@ -116,7 +117,7 @@ namespace Jellyfin.Api.Controllers
/// Whether to allow to copy the audio stream. Default: true.
/// Playback info returned.
/// A containing a with the playback info.
- [HttpPost("/Items/{itemId}/PlaybackInfo")]
+ [HttpPost("Items/{itemId}/PlaybackInfo")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetPostedPlaybackInfo(
[FromRoute] Guid itemId,
@@ -237,7 +238,7 @@ namespace Jellyfin.Api.Controllers
/// Whether to enable direct stream. Default: true.
/// Media source opened.
/// A containing a .
- [HttpPost("/LiveStreams/Open")]
+ [HttpPost("LiveStreams/Open")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> OpenLiveStream(
[FromQuery] string? openToken,
@@ -278,7 +279,7 @@ namespace Jellyfin.Api.Controllers
/// The livestream id.
/// Livestream closed.
/// A indicating success.
- [HttpPost("/LiveStreams/Close")]
+ [HttpPost("LiveStreams/Close")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult CloseLiveStream([FromQuery] string? liveStreamId)
{
@@ -293,7 +294,7 @@ namespace Jellyfin.Api.Controllers
/// Test buffer returned.
/// Size has to be a numer between 0 and 10,000,000.
/// A with specified bitrate.
- [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 a6c552790e..06c4213fb0 100644
--- a/Jellyfin.Api/Controllers/PackageController.cs
+++ b/Jellyfin.Api/Controllers/PackageController.cs
@@ -16,7 +16,7 @@ namespace Jellyfin.Api.Controllers
///
/// Package Controller.
///
- [Route("Packages")]
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class PackageController : BaseJellyfinApiController
{
@@ -41,7 +41,7 @@ namespace Jellyfin.Api.Controllers
/// The GUID of the associated assembly.
/// Package retrieved.
/// A containing package information.
- [HttpGet("/{name}")]
+ [HttpGet("Packages/{name}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetPackageInfo(
[FromRoute] [Required] string? name,
@@ -61,7 +61,7 @@ namespace Jellyfin.Api.Controllers
///
/// Available packages returned.
/// An containing available packages information.
- [HttpGet]
+ [HttpGet("Packages")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetPackages()
{
@@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers
/// Package found.
/// Package not found.
/// A on success, or a if the package could not be found.
- [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
/// Installation Id.
/// Installation cancelled.
/// A on successfully cancelling a package installation.
- [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
///
/// Package repositories returned.
/// An containing the list of package repositories.
- [HttpGet("/Repositories")]
+ [HttpGet("Repositories")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetRepositories()
@@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers
/// The list of package repositories.
/// Package repositories saved.
/// A .
- [HttpOptions("/Repositories")]
+ [HttpOptions("Repositories")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SetRepositories([FromBody] List repositoryInfos)
diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs
index 3ebd003f1a..0422bfe727 100644
--- a/Jellyfin.Api/Controllers/PlaystateController.cs
+++ b/Jellyfin.Api/Controllers/PlaystateController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
///
/// Playstate controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class PlaystateController : BaseJellyfinApiController
{
@@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The date the item was played.
/// Item marked as played.
/// An containing the .
- [HttpPost("/Users/{userId}/PlayedItems/{itemId}")]
+ [HttpPost("Users/{userId}/PlayedItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult MarkPlayedItem(
[FromRoute] Guid userId,
@@ -93,7 +94,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// Item marked as unplayed.
/// A containing the .
- [HttpDelete("/Users/{userId}/PlayedItem/{itemId}")]
+ [HttpDelete("Users/{userId}/PlayedItem/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult MarkUnplayedItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -115,7 +116,7 @@ namespace Jellyfin.Api.Controllers
/// The playback start info.
/// Playback start recorded.
/// A .
- [HttpPost("/Sessions/Playing")]
+ [HttpPost("Sessions/Playing")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task ReportPlaybackStart([FromBody] PlaybackStartInfo playbackStartInfo)
{
@@ -131,7 +132,7 @@ namespace Jellyfin.Api.Controllers
/// The playback progress info.
/// Playback progress recorded.
/// A .
- [HttpPost("/Sessions/Playing/Progress")]
+ [HttpPost("Sessions/Playing/Progress")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task ReportPlaybackProgress([FromBody] PlaybackProgressInfo playbackProgressInfo)
{
@@ -147,7 +148,7 @@ namespace Jellyfin.Api.Controllers
/// Playback session id.
/// Playback session pinged.
/// A .
- [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
/// The playback stop info.
/// Playback stop recorded.
/// A .
- [HttpPost("/Sessions/Playing/Stopped")]
+ [HttpPost("Sessions/Playing/Stopped")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task ReportPlaybackStopped([FromBody] PlaybackStopInfo playbackStopInfo)
{
@@ -190,7 +191,7 @@ namespace Jellyfin.Api.Controllers
/// Indicates if the client can seek.
/// Play start recorded.
/// A .
- [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 OnPlaybackStart(
@@ -240,7 +241,7 @@ namespace Jellyfin.Api.Controllers
/// Indicates if the player is muted.
/// Play progress recorded.
/// A .
- [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 OnPlaybackProgress(
@@ -292,7 +293,7 @@ namespace Jellyfin.Api.Controllers
/// The play session id.
/// Playback stop recorded.
/// A .
- [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 OnPlaybackStopped(
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index 770d74838d..fe10f0f1bf 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -188,7 +188,7 @@ namespace Jellyfin.Api.Controllers
/// Not Implemented.
/// This endpoint is not implemented.
[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 1b300e0d8a..3e6f577f13 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -23,6 +23,7 @@ namespace Jellyfin.Api.Controllers
///
/// The session controller.
///
+ [Route("")]
public class SessionController : BaseJellyfinApiController
{
private readonly ISessionManager _sessionManager;
@@ -57,7 +58,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Filter by sessions that were active in the last n seconds.
/// List of sessions returned.
/// An with the available sessions.
- [HttpGet("/Sessions")]
+ [HttpGet("Sessions")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetSessions(
@@ -120,7 +121,7 @@ namespace Jellyfin.Api.Controllers
/// The name of the item.
/// Instruction sent to session.
/// A .
- [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
/// The .
/// Instruction sent to session.
/// A .
- [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
/// The .
/// Playstate command sent to session.
/// A .
- [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
/// The command to send.
/// System command sent to session.
/// A .
- [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
/// The command to send.
/// General command sent to session.
/// A .
- [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
/// The .
/// Full general command sent to session.
/// A .
- [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
/// The message timeout. If omitted the user will have to confirm viewing the message.
/// Message sent.
/// A .
- [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
/// The user id.
/// User added to session.
/// A .
- [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
/// The user id.
/// User removed from session.
/// A .
- [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
/// Determines whether the device supports a unique identifier.
/// Capabilities posted.
/// A .
- [HttpPost("/Sessions/Capabilities")]
+ [HttpPost("Sessions/Capabilities")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult PostCapabilities(
[FromQuery] string? id,
@@ -398,7 +399,7 @@ namespace Jellyfin.Api.Controllers
/// The .
/// Capabilities updated.
/// A .
- [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
/// The item id.
/// Session reported to server.
/// A .
- [HttpPost("/Sessions/Viewing")]
+ [HttpPost("Sessions/Viewing")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult ReportViewing(
[FromQuery] string? sessionId,
@@ -438,7 +439,7 @@ namespace Jellyfin.Api.Controllers
///
/// Session end reported to server.
/// A .
- [HttpPost("/Sessions/Logout")]
+ [HttpPost("Sessions/Logout")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult ReportSessionEnded()
{
@@ -453,7 +454,7 @@ namespace Jellyfin.Api.Controllers
///
/// Auth providers retrieved.
/// An with the auth providers.
- [HttpGet("/Auth/Providers")]
+ [HttpGet("Auth/Providers")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetAuthProviders()
{
@@ -465,7 +466,7 @@ namespace Jellyfin.Api.Controllers
///
/// Password reset providers retrieved.
/// An with the password reset providers.
- [HttpGet("/Auto/PasswordResetProviders")]
+ [HttpGet("Auto/PasswordResetProviders")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetPasswordResetProviders()
{
diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs
index f8c19d15c4..22144ab1af 100644
--- a/Jellyfin.Api/Controllers/SubtitleController.cs
+++ b/Jellyfin.Api/Controllers/SubtitleController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
///
/// Subtitle controller.
///
+ [Route("")]
public class SubtitleController : BaseJellyfinApiController
{
private readonly ILibraryManager _libraryManager;
@@ -80,7 +81,7 @@ namespace Jellyfin.Api.Controllers
/// Subtitle deleted.
/// Item not found.
/// A .
- [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
/// Optional. Only show subtitles which are a perfect match.
/// Subtitles retrieved.
/// An array of .
- [HttpGet("/Items/{itemId}/RemoteSearch/Subtitles/{language}")]
+ [HttpGet("Items/{itemId}/RemoteSearch/Subtitles/{language}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task>> SearchRemoteSubtitles(
@@ -127,7 +128,7 @@ namespace Jellyfin.Api.Controllers
/// The subtitle id.
/// Subtitle downloaded.
/// A .
- [HttpPost("/Items/{itemId}/RemoteSearch/Subtitles/{subtitleId}")]
+ [HttpPost("Items/{itemId}/RemoteSearch/Subtitles/{subtitleId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task DownloadRemoteSubtitles(
@@ -157,7 +158,7 @@ namespace Jellyfin.Api.Controllers
/// The item id.
/// File returned.
/// A with the subtitle file.
- [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
/// Optional. The start position of the subtitle in ticks.
/// File returned.
/// A with the subtitle file.
- [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 GetSubtitle(
[FromRoute, Required] Guid itemId,
@@ -247,7 +248,7 @@ namespace Jellyfin.Api.Controllers
/// The subtitle segment length.
/// Subtitle playlist retrieved.
/// A with the HLS subtitle playlist.
- [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 55759f316f..42db6b6a11 100644
--- a/Jellyfin.Api/Controllers/SuggestionsController.cs
+++ b/Jellyfin.Api/Controllers/SuggestionsController.cs
@@ -16,6 +16,7 @@ namespace Jellyfin.Api.Controllers
///
/// The suggestions controller.
///
+ [Route("")]
public class SuggestionsController : BaseJellyfinApiController
{
private readonly IDtoService _dtoService;
@@ -49,7 +50,7 @@ namespace Jellyfin.Api.Controllers
/// Whether to enable the total record count.
/// Suggestions returned.
/// A with the suggestions.
- [HttpGet("/Users/{userId}/Suggestions")]
+ [HttpGet("Users/{userId}/Suggestions")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetSuggestions(
[FromRoute] Guid userId,
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index fbab7948ff..5157b08ae5 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Enable the total record count.
/// Optional, include image information in output.
/// A with the trailers.
- [HttpGet("/Trailers")]
+ [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetTrailers(
[FromQuery] Guid? userId,
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index 5a9bec2b05..75df16aa7b 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -19,6 +19,7 @@ namespace Jellyfin.Api.Controllers
///
/// The universal audio controller.
///
+ [Route("")]
public class UniversalAudioController : BaseJellyfinApiController
{
private readonly IAuthorizationContext _authorizationContext;
@@ -68,10 +69,10 @@ namespace Jellyfin.Api.Controllers
/// Audio stream returned.
/// Redirected to remote audio stream.
/// A containing the audio file.
- [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 482baf6416..2ce5c7e569 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -450,7 +450,7 @@ namespace Jellyfin.Api.Controllers
/// The create user by name request body.
/// User created.
/// An of the new user.
- [HttpPost("/Users/New")]
+ [HttpPost("New")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> CreateUserByName([FromBody] CreateUserByName request)
diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index cedda3b9d2..f55ff6f3d5 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -25,6 +25,7 @@ namespace Jellyfin.Api.Controllers
///
/// User library controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class UserLibraryController : BaseJellyfinApiController
{
@@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// Item returned.
/// An containing the d item.
- [HttpGet("/Users/{userId}/Items/{itemId}")]
+ [HttpGet("Users/{userId}/Items/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task> GetItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -90,7 +91,7 @@ namespace Jellyfin.Api.Controllers
/// User id.
/// Root folder returned.
/// An containing the user's root folder.
- [HttpGet("/Users/{userId}/Items/Root")]
+ [HttpGet("Users/{userId}/Items/Root")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetRootFolder([FromRoute] Guid userId)
{
@@ -107,7 +108,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// Intros returned.
/// An containing the intros to play.
- [HttpGet("/Users/{userId}/Items/{itemId}/Intros")]
+ [HttpGet("Users/{userId}/Items/{itemId}/Intros")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task>> GetIntros([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -135,7 +136,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// Item marked as favorite.
/// An containing the .
- [HttpPost("/Users/{userId}/FavoriteItems/{itemId}")]
+ [HttpPost("Users/{userId}/FavoriteItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult MarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -149,7 +150,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// Item unmarked as favorite.
/// An containing the .
- [HttpDelete("/Users/{userId}/FavoriteItems/{itemId}")]
+ [HttpDelete("Users/{userId}/FavoriteItems/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult UnmarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -163,7 +164,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// Personal rating removed.
/// An containing the .
- [HttpDelete("/Users/{userId}/Items/{itemId}/Rating")]
+ [HttpDelete("Users/{userId}/Items/{itemId}/Rating")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult DeleteUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -178,7 +179,7 @@ namespace Jellyfin.Api.Controllers
/// Whether this is likes.
/// Item rating updated.
/// An containing the .
- [HttpPost("/Users/{userId}/Items/{itemId}/Rating")]
+ [HttpPost("Users/{userId}/Items/{itemId}/Rating")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult UpdateUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId, [FromQuery] bool? likes)
{
@@ -192,7 +193,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// An containing the item's local trailers.
/// The items local trailers.
- [HttpGet("/Users/{userId}/Items/{itemId}/LocalTrailers")]
+ [HttpGet("Users/{userId}/Items/{itemId}/LocalTrailers")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetLocalTrailers([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -227,7 +228,7 @@ namespace Jellyfin.Api.Controllers
/// Item id.
/// Special features returned.
/// An containing the special features.
- [HttpGet("/Users/{userId}/Items/{itemId}/SpecialFeatures")]
+ [HttpGet("Users/{userId}/Items/{itemId}/SpecialFeatures")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetSpecialFeatures([FromRoute] Guid userId, [FromRoute] Guid itemId)
{
@@ -260,7 +261,7 @@ namespace Jellyfin.Api.Controllers
/// Whether or not to group items into a parent container.
/// Latest media returned.
/// An containing the latest media.
- [HttpGet("/Users/{userId}/Items/Latest")]
+ [HttpGet("Users/{userId}/Items/Latest")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetLatestMedia(
[FromRoute] Guid userId,
diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs
index f4bd451efe..6df7cc779f 100644
--- a/Jellyfin.Api/Controllers/UserViewsController.cs
+++ b/Jellyfin.Api/Controllers/UserViewsController.cs
@@ -21,6 +21,7 @@ namespace Jellyfin.Api.Controllers
///
/// User views controller.
///
+ [Route("")]
public class UserViewsController : BaseJellyfinApiController
{
private readonly IUserManager _userManager;
@@ -60,7 +61,7 @@ namespace Jellyfin.Api.Controllers
/// Whether or not to include hidden content.
/// User views returned.
/// An containing the user views.
- [HttpGet("/Users/{userId}/Views")]
+ [HttpGet("Users/{userId}/Views")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetUserViews(
[FromRoute] Guid userId,
@@ -122,7 +123,7 @@ namespace Jellyfin.Api.Controllers
/// An containing the user view grouping options
/// or a if user not found.
///
- [HttpGet("/Users/{userId}/GroupingOptions")]
+ [HttpGet("Users/{userId}/GroupingOptions")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult> GetGroupingOptions([FromRoute] Guid userId)
diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs
index 8520dd1638..76188f46d6 100644
--- a/Jellyfin.Api/Controllers/VideoHlsController.cs
+++ b/Jellyfin.Api/Controllers/VideoHlsController.cs
@@ -30,6 +30,7 @@ namespace Jellyfin.Api.Controllers
///
/// The video hls controller.
///
+ [Route("")]
[Authorize(Policy = Policies.DefaultAuthorization)]
public class VideoHlsController : BaseJellyfinApiController
{
@@ -158,7 +159,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. Whether to enable subtitles in the manifest.
/// Hls live stream retrieved.
/// A containing the hls file.
- [HttpGet("/Videos/{itemId}/live.m3u8")]
+ [HttpGet("Videos/{itemId}/live.m3u8")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task 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,