diff options
| -rw-r--r-- | Emby.Server.Implementations/Library/UserDataManager.cs | 37 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/ItemsController.cs | 60 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Library/IUserDataManager.cs | 9 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dto/UserDataDto.cs | 43 |
4 files changed, 97 insertions, 52 deletions
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index a0a90b129..0d67f2cda 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; +using System.Reflection; using System.Threading; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; @@ -81,6 +82,42 @@ namespace Emby.Server.Implementations.Library }); } + public void SaveUserData(User user, BaseItem item, UserDataDto userDataDto, UserDataSaveReason reason) + { + ArgumentNullException.ThrowIfNull(user); + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(reason); + ArgumentNullException.ThrowIfNull(userDataDto); + + var userData = GetUserData(user, item); + + var parentProperties = userDataDto.GetType().GetProperties(); + var childProperties = userData.GetType().GetProperties(); + + foreach (var parentProperty in parentProperties) + { + foreach (var childProperty in childProperties) + { + if (parentProperty.Name != childProperty.Name) + { + continue; + } + + var value = parentProperty.GetValue(userDataDto, null); + + if (value is null) + { + continue; + } + + childProperty.SetValue(userData, value, null); + break; + } + } + + SaveUserData(user, item, userData, reason, CancellationToken.None); + } + /// <summary> /// Save the provided user data for the given user. Batch operation. Does not fire any events or update the cache. /// </summary> diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 8c816f802..2a346be68 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -913,13 +913,7 @@ public class ItemsController : BaseJellyfinApiController /// </summary> /// <param name="userId">The user id.</param> /// <param name="itemId">The item id.</param> - /// <param name="played">Optional. Whether to mark the item as played.</param> - /// <param name="favorite">Optional. Whether to mark the item as favorite.</param> - /// <param name="likes">Optional. Whether to mark the item as liked.</param> - /// <param name="rating">Optional. User item rating.</param> - /// <param name="playbackPositionTicks">Optional. Item playback position ticks. 1 tick = 10000 ms.</param> - /// <param name="playCount">Optional. How many times the user played the item.</param> - /// <param name="lastPlayedDate">Optional. The date the item was played.</param> + /// <param name="userDataDto">New user data object.</param> /// <response code="200">return updated user item data.</response> /// <response code="404">Item is not found.</response> /// <returns>Return <see cref="UserItemDataDto"/>.</returns> @@ -929,14 +923,13 @@ public class ItemsController : BaseJellyfinApiController public ActionResult<UserItemDataDto> UpdateItemUserData( [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, - [FromQuery] bool? played, - [FromQuery] bool? favorite, - [FromQuery] bool? likes, - [FromQuery] double? rating, - [FromQuery] long? playbackPositionTicks, - [FromQuery] int? playCount, - [FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? lastPlayedDate) + [FromBody, Required] UserDataDto userDataDto) { + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) + { + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update this item user data."); + } + var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException(); var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -944,44 +937,7 @@ public class ItemsController : BaseJellyfinApiController return NotFound(); } - var userData = _userDataRepository.GetUserData(user, item); - - if (played.HasValue) - { - userData.Played = played.Value; - } - - if (favorite.HasValue) - { - userData.IsFavorite = favorite.Value; - } - - if (likes.HasValue) - { - userData.Likes = likes.Value; - } - - if (rating.HasValue) - { - userData.Rating = rating.Value; - } - - if (playbackPositionTicks.HasValue) - { - userData.PlaybackPositionTicks = playbackPositionTicks.Value; - } - - if (playCount.HasValue) - { - userData.PlayCount = playCount.Value; - } - - if (lastPlayedDate.HasValue) - { - userData.LastPlayedDate = lastPlayedDate.Value; - } - - _userDataRepository.SaveUserData(user.Id, item, userData, UserDataSaveReason.UpdateUserData, CancellationToken.None); + _userDataRepository.SaveUserData(user, item, userDataDto, UserDataSaveReason.UpdateUserData); return _userDataRepository.GetUserDataDto(item, user); } diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 034c40591..8849c098f 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -35,6 +35,15 @@ namespace MediaBrowser.Controller.Library void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken); + /// <summary> + /// Save the provided user data for the given user. + /// </summary> + /// <param name="user">The user.</param> + /// <param name="item">The item.</param> + /// <param name="userDataDto">The reason for updating the user data.</param> + /// <param name="reason">The reason.</param> + void SaveUserData(User user, BaseItem item, UserDataDto userDataDto, UserDataSaveReason reason); + UserItemData GetUserData(User user, BaseItem item); UserItemData GetUserData(Guid userId, BaseItem item); diff --git a/MediaBrowser.Model/Dto/UserDataDto.cs b/MediaBrowser.Model/Dto/UserDataDto.cs new file mode 100644 index 000000000..3012916f8 --- /dev/null +++ b/MediaBrowser.Model/Dto/UserDataDto.cs @@ -0,0 +1,43 @@ +#nullable disable +using System; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class UserDataDto extends UserItemDataDto to allow nullable members. + /// This change allow us to implement the new /Users/{UserId}/Items/{ItemId}/UserData endpoint. + /// This object allows the requestor to update all or specific user data fields without altering the non-nullable members state. + /// </summary> + public class UserDataDto : UserItemDataDto + { + /// <summary> + /// Gets or sets the playback position ticks. + /// </summary> + /// <value>The playback position ticks.</value> + public new long? PlaybackPositionTicks { get; set; } + + /// <summary> + /// Gets or sets the play count. + /// </summary> + /// <value>The play count.</value> + public new int? PlayCount { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is favorite. + /// </summary> + /// <value><c>true</c> if this instance is favorite; otherwise, <c>false</c>.</value> + public new bool? IsFavorite { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="UserItemDataDto" /> is likes. + /// </summary> + /// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value> + public new bool? Likes { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="UserItemDataDto" /> is played. + /// </summary> + /// <value><c>true</c> if played; otherwise, <c>false</c>.</value> + public new bool? Played { get; set; } + } +} |
