aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs47
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs66
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs9
-rw-r--r--MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs76
-rw-r--r--MediaBrowser.Model/Entities/UserDataSaveReason.cs7
5 files changed, 203 insertions, 2 deletions
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index a0a90b129..8beeb8041 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -81,6 +81,53 @@ namespace Emby.Server.Implementations.Library
});
}
+ public void SaveUserData(User user, BaseItem item, UpdateUserItemDataDto userDataDto, UserDataSaveReason reason)
+ {
+ ArgumentNullException.ThrowIfNull(user);
+ ArgumentNullException.ThrowIfNull(item);
+ ArgumentNullException.ThrowIfNull(reason);
+ ArgumentNullException.ThrowIfNull(userDataDto);
+
+ var userData = GetUserData(user, item);
+
+ if (userDataDto.PlaybackPositionTicks.HasValue)
+ {
+ userData.PlaybackPositionTicks = userDataDto.PlaybackPositionTicks.Value;
+ }
+
+ if (userDataDto.PlayCount.HasValue)
+ {
+ userData.PlayCount = userDataDto.PlayCount.Value;
+ }
+
+ if (userDataDto.IsFavorite.HasValue)
+ {
+ userData.IsFavorite = userDataDto.IsFavorite.Value;
+ }
+
+ if (userDataDto.Likes.HasValue)
+ {
+ userData.Likes = userDataDto.Likes.Value;
+ }
+
+ if (userDataDto.Played.HasValue)
+ {
+ userData.Played = userDataDto.Played.Value;
+ }
+
+ if (userDataDto.LastPlayedDate.HasValue)
+ {
+ userData.LastPlayedDate = userDataDto.LastPlayedDate.Value;
+ }
+
+ if (userDataDto.Rating.HasValue)
+ {
+ userData.Rating = userDataDto.Rating.Value;
+ }
+
+ 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 4e46e808a..ae80d15e6 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -34,6 +34,7 @@ public class ItemsController : BaseJellyfinApiController
private readonly IDtoService _dtoService;
private readonly ILogger<ItemsController> _logger;
private readonly ISessionManager _sessionManager;
+ private readonly IUserDataManager _userDataRepository;
/// <summary>
/// Initializes a new instance of the <see cref="ItemsController"/> class.
@@ -44,13 +45,15 @@ public class ItemsController : BaseJellyfinApiController
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
+ /// <param name="userDataRepository">Instance of the <see cref="IUserDataManager"/> interface.</param>
public ItemsController(
IUserManager userManager,
ILibraryManager libraryManager,
ILocalizationManager localization,
IDtoService dtoService,
ILogger<ItemsController> logger,
- ISessionManager sessionManager)
+ ISessionManager sessionManager,
+ IUserDataManager userDataRepository)
{
_userManager = userManager;
_libraryManager = libraryManager;
@@ -58,6 +61,7 @@ public class ItemsController : BaseJellyfinApiController
_dtoService = dtoService;
_logger = logger;
_sessionManager = sessionManager;
+ _userDataRepository = userDataRepository;
}
/// <summary>
@@ -881,4 +885,64 @@ public class ItemsController : BaseJellyfinApiController
itemsResult.TotalRecordCount,
returnItems);
}
+
+ /// <summary>
+ /// Get Item User Data.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="itemId">The item id.</param>
+ /// <response code="200">return item user data.</response>
+ /// <response code="404">Item is not found.</response>
+ /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
+ [HttpGet("Users/{userId}/Items/{itemId}/UserData")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult<UserItemDataDto> GetItemUserData(
+ [FromRoute, Required] Guid userId,
+ [FromRoute, Required] Guid itemId)
+ {
+ if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true))
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to view this item user data.");
+ }
+
+ var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException();
+ var item = _libraryManager.GetItemById(itemId);
+
+ return (item == null) ? NotFound() : _userDataRepository.GetUserDataDto(item, user);
+ }
+
+ /// <summary>
+ /// Update Item User Data.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="itemId">The item id.</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>
+ [HttpPost("Users/{userId}/Items/{itemId}/UserData")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult<UserItemDataDto> UpdateItemUserData(
+ [FromRoute, Required] Guid userId,
+ [FromRoute, Required] Guid itemId,
+ [FromBody, Required] UpdateUserItemDataDto 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)
+ {
+ return NotFound();
+ }
+
+ _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..43cccfc65 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, UpdateUserItemDataDto userDataDto, UserDataSaveReason reason);
+
UserItemData GetUserData(User user, BaseItem item);
UserItemData GetUserData(Guid userId, BaseItem item);
diff --git a/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs
new file mode 100644
index 000000000..7bfedf973
--- /dev/null
+++ b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs
@@ -0,0 +1,76 @@
+using System;
+
+namespace MediaBrowser.Model.Dto
+{
+ /// <summary>
+ /// This is used by the api to get information about a item user data.
+ /// </summary>
+ public class UpdateUserItemDataDto
+ {
+ /// <summary>
+ /// Gets or sets the rating.
+ /// </summary>
+ /// <value>The rating.</value>
+ public double? Rating { get; set; }
+
+ /// <summary>
+ /// Gets or sets the played percentage.
+ /// </summary>
+ /// <value>The played percentage.</value>
+ public double? PlayedPercentage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the unplayed item count.
+ /// </summary>
+ /// <value>The unplayed item count.</value>
+ public int? UnplayedItemCount { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playback position ticks.
+ /// </summary>
+ /// <value>The playback position ticks.</value>
+ public long? PlaybackPositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the play count.
+ /// </summary>
+ /// <value>The play count.</value>
+ public 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 bool? IsFavorite { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this <see cref="UpdateUserItemDataDto" /> is likes.
+ /// </summary>
+ /// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value>
+ public bool? Likes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the last played date.
+ /// </summary>
+ /// <value>The last played date.</value>
+ public DateTime? LastPlayedDate { 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 bool? Played { get; set; }
+
+ /// <summary>
+ /// Gets or sets the key.
+ /// </summary>
+ /// <value>The key.</value>
+ public string? Key { get; set; }
+
+ /// <summary>
+ /// Gets or sets the item identifier.
+ /// </summary>
+ /// <value>The item identifier.</value>
+ public string? ItemId { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Entities/UserDataSaveReason.cs b/MediaBrowser.Model/Entities/UserDataSaveReason.cs
index 20404e6f4..b8e73a98c 100644
--- a/MediaBrowser.Model/Entities/UserDataSaveReason.cs
+++ b/MediaBrowser.Model/Entities/UserDataSaveReason.cs
@@ -33,6 +33,11 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// The import.
/// </summary>
- Import = 6
+ Import = 6,
+
+ /// <summary>
+ /// API call updated item user data.
+ /// </summary>
+ UpdateUserData = 7,
}
}