using System.Threading; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Connectivity; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; using ServiceStack.ServiceHost; using ServiceStack.Text.Controller; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace MediaBrowser.Api.UserLibrary { /// /// Class GetItem /// [Route("/Users/{UserId}/Items/{Id}", "GET")] [ServiceStack.ServiceHost.Api(Description = "Gets an item from a user's library")] public class GetItem : IReturn { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Id { get; set; } } /// /// Class GetItem /// [Route("/Users/{UserId}/Items/Root", "GET")] [ServiceStack.ServiceHost.Api(Description = "Gets the root folder from a user's library")] public class GetRootFolder : IReturn { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public Guid UserId { get; set; } } /// /// Class GetIntros /// [Route("/Users/{UserId}/Items/{Id}/Intros", "GET")] [ServiceStack.ServiceHost.Api(("Gets intros to play before the main media item plays"))] public class GetIntros : IReturn> { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public Guid UserId { get; set; } /// /// Gets or sets the item id. /// /// The item id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Id { get; set; } } /// /// Class UpdateDisplayPreferences /// [Route("/Users/{UserId}/Items/{Id}/DisplayPreferences", "POST")] [Api(("Updates a user's display preferences for an item"))] public class UpdateDisplayPreferences : DisplayPreferences, IReturnVoid { /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } } /// /// Class MarkFavoriteItem /// [Route("/Users/{UserId}/FavoriteItems/{Id}", "POST")] [Api(Description = "Marks an item as a favorite")] public class MarkFavoriteItem : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } } /// /// Class UnmarkFavoriteItem /// [Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE")] [Api(Description = "Unmarks an item as a favorite")] public class UnmarkFavoriteItem : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Id { get; set; } } /// /// Class ClearUserItemRating /// [Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE")] [ServiceStack.ServiceHost.Api(Description = "Deletes a user's saved personal rating for an item")] public class DeleteUserItemRating : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Id { get; set; } } /// /// Class UpdateUserItemRating /// [Route("/Users/{UserId}/Items/{Id}/Rating", "POST")] [ServiceStack.ServiceHost.Api(Description = "Updates a user's rating for an item")] public class UpdateUserItemRating : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } /// /// Gets or sets a value indicating whether this is likes. /// /// true if likes; otherwise, false. [ApiMember(Name = "Likes", Description = "Whether the user likes the item or not. true/false", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")] public bool Likes { get; set; } } /// /// Class MarkPlayedItem /// [Route("/Users/{UserId}/PlayedItems/{Id}", "POST")] [ServiceStack.ServiceHost.Api(Description = "Marks an item as played")] public class MarkPlayedItem : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } } /// /// Class MarkUnplayedItem /// [Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE")] [ServiceStack.ServiceHost.Api(Description = "Marks an item as unplayed")] public class MarkUnplayedItem : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Id { get; set; } } [Route("/Users/{UserId}/PlayingItems/{Id}", "POST")] [ServiceStack.ServiceHost.Api(Description = "Reports that a user has begun playing an item")] public class OnPlaybackStart : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } } [Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST")] [ServiceStack.ServiceHost.Api(Description = "Reports a user's playback progress")] public class OnPlaybackProgress : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } /// /// Gets or sets the position ticks. /// /// The position ticks. [ApiMember(Name = "PositionTicks", Description = "Optional. The current position, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] public long? PositionTicks { get; set; } } [Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE")] [ServiceStack.ServiceHost.Api(Description = "Reports that a user has stopped playing an item")] public class OnPlaybackStopped : IReturnVoid { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Id { get; set; } /// /// Gets or sets the position ticks. /// /// The position ticks. [ApiMember(Name = "PositionTicks", Description = "Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "DELETE")] public long? PositionTicks { get; set; } } /// /// Class GetLocalTrailers /// [Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET")] [ServiceStack.ServiceHost.Api(Description = "Gets local trailers for an item")] public class GetLocalTrailers : IReturn> { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Id { get; set; } } /// /// Class GetSpecialFeatures /// [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET")] [ServiceStack.ServiceHost.Api(Description = "Gets special features for a movie")] public class GetSpecialFeatures : IReturn> { /// /// Gets or sets the user id. /// /// The user id. [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public Guid UserId { get; set; } /// /// Gets or sets the id. /// /// The id. [ApiMember(Name = "Id", Description = "Movie Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Id { get; set; } } /// /// Class UserLibraryService /// public class UserLibraryService : BaseApiService { /// /// The _user manager /// private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; private readonly IJsonSerializer _jsonSerializer; /// /// Initializes a new instance of the class. /// /// jsonSerializer public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer) : base() { _userManager = userManager; _libraryManager = libraryManager; _jsonSerializer = jsonSerializer; } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetSpecialFeatures request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); var movie = (Movie)item; var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userManager); var items = movie.SpecialFeatures.Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).AsParallel().Select(t => t.Result).ToList(); return ToOptimizedResult(items); } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetLocalTrailers request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userManager); var items = item.LocalTrailers.Select(i => dtoBuilder.GetBaseItemDto(i, user, fields)).AsParallel().Select(t => t.Result).ToList(); return ToOptimizedResult(items); } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetItem request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userManager); var result = dtoBuilder.GetBaseItemDto(item, user, fields).Result; return ToOptimizedResult(result); } public object Get(GetRootFolder request) { var user = _userManager.GetUserById(request.UserId); var item = user.RootFolder; // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userManager); var result = dtoBuilder.GetBaseItemDto(item, user, fields).Result; return ToOptimizedResult(result); } /// /// Gets the specified request. /// /// The request. /// System.Object. public object Get(GetIntros request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); var result = _libraryManager.GetIntros(item, user); return ToOptimizedResult(result); } /// /// Posts the specified request. /// /// The request. public void Post(UpdateDisplayPreferences request) { // We need to parse this manually because we told service stack not to with IRequiresRequestStream // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs var pathInfo = PathInfo.Parse(RequestContext.PathInfo); var userId = new Guid(pathInfo.GetArgumentValue(1)); var itemId = pathInfo.GetArgumentValue(3); var user = _userManager.GetUserById(userId); var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : DtoBuilder.GetItemByClientId(itemId, _userManager, _libraryManager, user.Id); var folder = (Folder)item; // Serialize to json and then back so that the core doesn't see the request dto type var displayPreferences = _jsonSerializer.DeserializeFromString(_jsonSerializer.SerializeToString(request)); var task = _userManager.SaveDisplayPreferences(user.Id, folder.DisplayPreferencesId, displayPreferences, CancellationToken.None); Task.WaitAll(task); } /// /// Posts the specified request. /// /// The request. public void Post(MarkFavoriteItem request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; // Set favorite status data.IsFavorite = true; var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } /// /// Deletes the specified request. /// /// The request. public void Delete(UnmarkFavoriteItem request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; // Set favorite status data.IsFavorite = false; var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } /// /// Deletes the specified request. /// /// The request. public void Delete(DeleteUserItemRating request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; data.Rating = null; var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } /// /// Posts the specified request. /// /// The request. public void Post(UpdateUserItemRating request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; data.Likes = request.Likes; var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } /// /// Posts the specified request. /// /// The request. public void Post(MarkPlayedItem request) { var user = _userManager.GetUserById(request.UserId); var task = UpdatePlayedStatus(user, request.Id, true); Task.WaitAll(task); } /// /// Posts the specified request. /// /// The request. public void Post(OnPlaybackStart request) { var user = _userManager.GetUserById(request.UserId); var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); var auth = RequestFilterAttribute.GetAuthorization(RequestContext); if (auth != null) { _userManager.OnPlaybackStart(user, item, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty); } } /// /// Posts the specified request. /// /// The request. public void Post(OnPlaybackProgress request) { var user = _userManager.GetUserById(request.UserId); var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); var auth = RequestFilterAttribute.GetAuthorization(RequestContext); if (auth != null) { var task = _userManager.OnPlaybackProgress(user, item, request.PositionTicks, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty); Task.WaitAll(task); } } /// /// Posts the specified request. /// /// The request. public void Delete(OnPlaybackStopped request) { var user = _userManager.GetUserById(request.UserId); var item = DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); var auth = RequestFilterAttribute.GetAuthorization(RequestContext); if (auth != null) { var task = _userManager.OnPlaybackStopped(user, item, request.PositionTicks, auth["Client"], auth["DeviceId"], auth["Device"] ?? string.Empty); Task.WaitAll(task); } } /// /// Deletes the specified request. /// /// The request. public void Delete(MarkUnplayedItem request) { var user = _userManager.GetUserById(request.UserId); var task = UpdatePlayedStatus(user, request.Id, false); Task.WaitAll(task); } /// /// Updates the played status. /// /// The user. /// The item id. /// if set to true [was played]. /// Task. private Task UpdatePlayedStatus(User user, string itemId, bool wasPlayed) { var item = DtoBuilder.GetItemByClientId(itemId, _userManager, _libraryManager, user.Id); return item.SetPlayedStatus(user, wasPlayed, _userManager); } } }