From 6e5ec99ea10557c141ed8d755e672cef628d35f0 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 3 Mar 2024 13:51:31 -0700 Subject: Move userId in API from route to optional query parameter (#11074) * Move userId in API from route to optional query parameter * Standardize UserViewsController * Move userId to query in ImageController * Move userId to query in ItemsController * Move userId to query in PlaystateController * Move userId to query in SuggestionsController * Move userId from route to query in UserLibraryController * Clean up routes * Move userId to query in UserController * fix bad merge --------- Co-authored-by: Niels van Velzen --- Jellyfin.Api/Controllers/PlaystateController.cs | 168 +++++++++++++++++++++--- 1 file changed, 149 insertions(+), 19 deletions(-) (limited to 'Jellyfin.Api/Controllers/PlaystateController.cs') diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index bde2f4d1a..949d101dc 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -68,15 +68,16 @@ public class PlaystateController : BaseJellyfinApiController /// Item marked as played. /// Item not found. /// An containing the , or a if item was not found. - [HttpPost("Users/{userId}/PlayedItems/{itemId}")] + [HttpPost("UserPlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> MarkPlayedItem( - [FromRoute, Required] Guid userId, + [FromQuery] Guid? userId, [FromRoute, Required] Guid itemId, [FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? datePlayed) { - var user = _userManager.GetUserById(userId); + var requestUserId = RequestHelpers.GetUserId(User, userId); + var user = _userManager.GetUserById(requestUserId); if (user is null) { return NotFound(); @@ -105,6 +106,26 @@ public class PlaystateController : BaseJellyfinApiController return dto; } + /// + /// Marks an item as played for user. + /// + /// User id. + /// Item id. + /// Optional. The date the item was played. + /// Item marked as played. + /// Item not found. + /// An containing the , or a if item was not found. + [HttpPost("Users/{userId}/PlayedItems/{itemId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [Obsolete("Kept for backwards compatibility")] + [ApiExplorerSettings(IgnoreApi = true)] + public Task> MarkPlayedItemLegacy( + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, + [FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? datePlayed) + => MarkPlayedItem(userId, itemId, datePlayed); + /// /// Marks an item as unplayed for user. /// @@ -113,12 +134,15 @@ public class PlaystateController : BaseJellyfinApiController /// Item marked as unplayed. /// Item not found. /// A containing the , or a if item was not found. - [HttpDelete("Users/{userId}/PlayedItems/{itemId}")] + [HttpDelete("UserPlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public async Task> MarkUnplayedItem( + [FromQuery] Guid? userId, + [FromRoute, Required] Guid itemId) { - var user = _userManager.GetUserById(userId); + var requestUserId = RequestHelpers.GetUserId(User, userId); + var user = _userManager.GetUserById(requestUserId); if (user is null) { return NotFound(); @@ -147,6 +171,24 @@ public class PlaystateController : BaseJellyfinApiController return dto; } + /// + /// Marks an item as unplayed for user. + /// + /// User id. + /// Item id. + /// Item marked as unplayed. + /// Item not found. + /// A containing the , or a if item was not found. + [HttpDelete("Users/{userId}/PlayedItems/{itemId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [Obsolete("Kept for backwards compatibility")] + [ApiExplorerSettings(IgnoreApi = true)] + public Task> MarkUnplayedItemLegacy( + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId) + => MarkUnplayedItem(userId, itemId); + /// /// Reports playback has started within a session. /// @@ -215,9 +257,8 @@ public class PlaystateController : BaseJellyfinApiController } /// - /// Reports that a user has begun playing an item. + /// Reports that a session has begun playing an item. /// - /// User id. /// Item id. /// The id of the MediaSource. /// The audio stream index. @@ -228,11 +269,9 @@ public class PlaystateController : BaseJellyfinApiController /// Indicates if the client can seek. /// Play start recorded. /// A . - [HttpPost("Users/{userId}/PlayingItems/{itemId}")] + [HttpPost("PlayingItems/{itemId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStart( - [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] int? audioStreamIndex, @@ -261,11 +300,41 @@ public class PlaystateController : BaseJellyfinApiController } /// - /// Reports a user's playback progress. + /// Reports that a user has begun playing an item. /// /// User id. /// Item id. /// The id of the MediaSource. + /// The audio stream index. + /// The subtitle stream index. + /// The play method. + /// The live stream id. + /// The play session id. + /// Indicates if the client can seek. + /// Play start recorded. + /// A . + [HttpPost("Users/{userId}/PlayingItems/{itemId}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [Obsolete("Kept for backwards compatibility")] + [ApiExplorerSettings(IgnoreApi = true)] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] + public Task OnPlaybackStartLegacy( + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, + [FromQuery] string? mediaSourceId, + [FromQuery] int? audioStreamIndex, + [FromQuery] int? subtitleStreamIndex, + [FromQuery] PlayMethod? playMethod, + [FromQuery] string? liveStreamId, + [FromQuery] string? playSessionId, + [FromQuery] bool canSeek = false) + => OnPlaybackStart(itemId, mediaSourceId, audioStreamIndex, subtitleStreamIndex, playMethod, liveStreamId, playSessionId, canSeek); + + /// + /// Reports a session's playback progress. + /// + /// Item id. + /// The id of the MediaSource. /// Optional. The current position, in ticks. 1 tick = 10000 ms. /// The audio stream index. /// The subtitle stream index. @@ -278,11 +347,9 @@ public class PlaystateController : BaseJellyfinApiController /// Indicates if the player is muted. /// Play progress recorded. /// A . - [HttpPost("Users/{userId}/PlayingItems/{itemId}/Progress")] + [HttpPost("PlayingItems/{itemId}/Progress")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackProgress( - [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] long? positionTicks, @@ -319,22 +386,58 @@ public class PlaystateController : BaseJellyfinApiController } /// - /// Reports that a user has stopped playing an item. + /// Reports a user's playback progress. /// /// User id. /// Item id. /// The id of the MediaSource. + /// Optional. The current position, in ticks. 1 tick = 10000 ms. + /// The audio stream index. + /// The subtitle stream index. + /// Scale of 0-100. + /// The play method. + /// The live stream id. + /// The play session id. + /// The repeat mode. + /// Indicates if the player is paused. + /// Indicates if the player is muted. + /// Play progress recorded. + /// A . + [HttpPost("Users/{userId}/PlayingItems/{itemId}/Progress")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [Obsolete("Kept for backwards compatibility")] + [ApiExplorerSettings(IgnoreApi = true)] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] + public Task OnPlaybackProgressLegacy( + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, + [FromQuery] string? mediaSourceId, + [FromQuery] long? positionTicks, + [FromQuery] int? audioStreamIndex, + [FromQuery] int? subtitleStreamIndex, + [FromQuery] int? volumeLevel, + [FromQuery] PlayMethod? playMethod, + [FromQuery] string? liveStreamId, + [FromQuery] string? playSessionId, + [FromQuery] RepeatMode? repeatMode, + [FromQuery] bool isPaused = false, + [FromQuery] bool isMuted = false) + => OnPlaybackProgress(itemId, mediaSourceId, positionTicks, audioStreamIndex, subtitleStreamIndex, volumeLevel, playMethod, liveStreamId, playSessionId, repeatMode, isPaused, isMuted); + + /// + /// Reports that a session has stopped playing an item. + /// + /// Item id. + /// The id of the MediaSource. /// The next media type that will play. /// Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms. /// The live stream id. /// The play session id. /// Playback stop recorded. /// A . - [HttpDelete("Users/{userId}/PlayingItems/{itemId}")] + [HttpDelete("PlayingItems/{itemId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStopped( - [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] string? nextMediaType, @@ -363,6 +466,33 @@ public class PlaystateController : BaseJellyfinApiController return NoContent(); } + /// + /// Reports that a user has stopped playing an item. + /// + /// User id. + /// Item id. + /// The id of the MediaSource. + /// The next media type that will play. + /// Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms. + /// The live stream id. + /// The play session id. + /// Playback stop recorded. + /// A . + [HttpDelete("Users/{userId}/PlayingItems/{itemId}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [Obsolete("Kept for backwards compatibility")] + [ApiExplorerSettings(IgnoreApi = true)] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] + public Task OnPlaybackStoppedLegacy( + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, + [FromQuery] string? mediaSourceId, + [FromQuery] string? nextMediaType, + [FromQuery] long? positionTicks, + [FromQuery] string? liveStreamId, + [FromQuery] string? playSessionId) + => OnPlaybackStopped(itemId, mediaSourceId, nextMediaType, positionTicks, liveStreamId, playSessionId); + /// /// Updates the played status. /// -- cgit v1.2.3