diff options
| author | crobibero <cody@robibe.ro> | 2020-08-04 12:48:53 -0600 |
|---|---|---|
| committer | crobibero <cody@robibe.ro> | 2020-08-04 12:48:53 -0600 |
| commit | 858aecd409914f6a9ca6f86445ec20c8bacc0b59 (patch) | |
| tree | 249704c4c58724a87d4eeede6df4a26d11a97132 /Jellyfin.Api/Controllers | |
| parent | 2b7cefdf15cf15a3e2e9f8499af7a008ea4a9cca (diff) | |
Fix all route for base url support
Diffstat (limited to 'Jellyfin.Api/Controllers')
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, |
