From f34f428143d5eff633503b5a623f2f68e93c3872 Mon Sep 17 00:00:00 2001 From: Steven Hildreth Date: Tue, 20 Nov 2018 08:36:07 -0600 Subject: [PATCH] Subsonic API work --- RoadieApi/Controllers/EntityControllerBase.cs | 43 +++++++ RoadieApi/Controllers/PlayController.cs | 35 +----- RoadieApi/Controllers/SubsonicController.cs | 30 ++++- RoadieApi/Services/ISubsonicService.cs | 2 + RoadieApi/Services/ReleaseService.cs | 37 ++++-- RoadieApi/Services/ServiceBase.cs | 15 +++ RoadieApi/Services/SubsonicService.cs | 91 ++++++++++++++- .../Identity/ApplicationUserPartial.cs | 13 +++ .../Models/Pagination/PagedRequest.cs | 1 + RoadieLibrary/Models/Releases/ReleaseList.cs | 2 + .../Models/ThirdPartyApi/Subsonic/ListType.cs | 21 ++++ .../Models/ThirdPartyApi/Subsonic/Request.cs | 108 ++++++++++++++++++ RoadieLibrary/Models/Users/UserRatingBase.cs | 1 + 13 files changed, 348 insertions(+), 51 deletions(-) create mode 100644 RoadieLibrary/Models/ThirdPartyApi/Subsonic/ListType.cs diff --git a/RoadieApi/Controllers/EntityControllerBase.cs b/RoadieApi/Controllers/EntityControllerBase.cs index b96c87e..66dc270 100644 --- a/RoadieApi/Controllers/EntityControllerBase.cs +++ b/RoadieApi/Controllers/EntityControllerBase.cs @@ -1,11 +1,16 @@ using Mapster; using Microsoft.AspNet.OData; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Roadie.Api.Services; using Roadie.Library.Caching; using Roadie.Library.Configuration; using Roadie.Library.Identity; +using System; +using System.IO; +using System.Net; using System.Threading.Tasks; using models = Roadie.Library.Models.Users; @@ -50,5 +55,43 @@ namespace Roadie.Api.Controllers result.IsEditor = User.IsInRole("Editor"); return result; } + + protected async Task StreamTrack(Guid id, ITrackService trackService, IPlayActivityService playActivityService) + { + var user = await this.CurrentUserModel(); + var track = await trackService.ById(user, id, null); + if (track == null || track.IsNotFoundResult) + { + Response.StatusCode = (int)HttpStatusCode.NotFound; + } + var info = await trackService.TrackStreamInfo(id, + Services.TrackService.DetermineByteStartFromHeaders(this.Request.Headers), + Services.TrackService.DetermineByteEndFromHeaders(this.Request.Headers, track.Data.FileSize)); + if (!info.IsSuccess) + { + Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + Response.Headers.Add("Content-Disposition", info.Data.ContentDisposition); + Response.Headers.Add("X-Content-Duration", info.Data.ContentDuration); + if (!info.Data.IsFullRequest) + { + Response.Headers.Add("Accept-Ranges", info.Data.AcceptRanges); + Response.Headers.Add("Content-Range", info.Data.ContentRange); + } + Response.Headers.Add("Content-Length", info.Data.ContentLength); + Response.ContentType = info.Data.ContentType; + Response.StatusCode = info.Data.IsFullRequest ? (int)HttpStatusCode.OK : (int)HttpStatusCode.PartialContent; + Response.Headers.Add("Last-Modified", info.Data.LastModified); + Response.Headers.Add("ETag", info.Data.Etag); + Response.Headers.Add("Cache-Control", info.Data.CacheControl); + Response.Headers.Add("Expires", info.Data.Expires); + var stream = new MemoryStream(info.Data.Bytes); + var playListUser = await playActivityService.CreatePlayActivity(user, info.Data); + this._logger.LogInformation($"StreamTrack PlayActivity `{ playListUser?.ToString() }`, StreamInfo `{ info.Data.ToString() }`"); + return new FileStreamResult(stream, info.Data.ContentType) + { + FileDownloadName = info.Data.FileName + }; + } } } \ No newline at end of file diff --git a/RoadieApi/Controllers/PlayController.cs b/RoadieApi/Controllers/PlayController.cs index d447196..b15a533 100644 --- a/RoadieApi/Controllers/PlayController.cs +++ b/RoadieApi/Controllers/PlayController.cs @@ -68,40 +68,7 @@ namespace Roadie.Api.Controllers [HttpGet("track/{id}.{mp3?}")] public async Task StreamTrack(Guid id) { - var user = await this.CurrentUserModel(); - var track = await this.TrackService.ById(user, id, null); - if (track == null || track.IsNotFoundResult) - { - Response.StatusCode = (int)HttpStatusCode.NotFound; - } - var info = await this.TrackService.TrackStreamInfo(id, - Services.TrackService.DetermineByteStartFromHeaders(this.Request.Headers), - Services.TrackService.DetermineByteEndFromHeaders(this.Request.Headers, track.Data.FileSize)); - if (!info.IsSuccess) - { - Response.StatusCode = (int)HttpStatusCode.InternalServerError; - } - Response.Headers.Add("Content-Disposition", info.Data.ContentDisposition); - Response.Headers.Add("X-Content-Duration", info.Data.ContentDuration); - if (!info.Data.IsFullRequest) - { - Response.Headers.Add("Accept-Ranges", info.Data.AcceptRanges); - Response.Headers.Add("Content-Range", info.Data.ContentRange); - } - Response.Headers.Add("Content-Length", info.Data.ContentLength); - Response.ContentType = info.Data.ContentType; - Response.StatusCode = info.Data.IsFullRequest ? (int)HttpStatusCode.OK : (int)HttpStatusCode.PartialContent; - Response.Headers.Add("Last-Modified", info.Data.LastModified); - Response.Headers.Add("ETag", info.Data.Etag); - Response.Headers.Add("Cache-Control", info.Data.CacheControl); - Response.Headers.Add("Expires", info.Data.Expires); - var stream = new MemoryStream(info.Data.Bytes); - var playListUser = await this.PlayActivityService.CreatePlayActivity(user, info.Data); - this._logger.LogInformation($"StreamTrack PlayActivity `{ playListUser?.ToString() }`, StreamInfo `{ info.Data.ToString() }`"); - return new FileStreamResult(stream, info.Data.ContentType) - { - FileDownloadName = info.Data.FileName - }; + return await base.StreamTrack(id, this.TrackService, this.PlayActivityService); } } } \ No newline at end of file diff --git a/RoadieApi/Controllers/SubsonicController.cs b/RoadieApi/Controllers/SubsonicController.cs index 7d5ed2b..8813b91 100644 --- a/RoadieApi/Controllers/SubsonicController.cs +++ b/RoadieApi/Controllers/SubsonicController.cs @@ -19,12 +19,16 @@ namespace Roadie.Api.Controllers public class SubsonicController : EntityControllerBase { private ISubsonicService SubsonicService { get; } + private IPlayActivityService PlayActivityService { get; } + private ITrackService TrackService { get; } - public SubsonicController(ISubsonicService subsonicService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, UserManager userManager) + public SubsonicController(ISubsonicService subsonicService, ITrackService trackService, IPlayActivityService playActivityService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, UserManager userManager) : base(cacheManager, configuration, userManager) { this._logger = logger.CreateLogger("RoadieApi.Controllers.SubsonicController"); this.SubsonicService = subsonicService; + this.TrackService = trackService; + this.PlayActivityService = playActivityService; } [HttpGet("getIndexes.view")] @@ -76,6 +80,27 @@ namespace Roadie.Api.Controllers return this.BuildResponse(request, result.Data, "podcasts"); } + [HttpGet("getAlbumList.view")] + [ProducesResponseType(200)] + public async Task GetAlbumList([FromQuery]Request request) + { + var result = await this.SubsonicService.GetAlbumList(request, null); + return this.BuildResponse(request, result.Data, "albumList"); + } + + [HttpGet("stream.view")] + [HttpPost("stream.view")] + [ProducesResponseType(200)] + public async Task StreamTrack([FromQuery]Request request) + { + var trackId = request.TrackId; + if (trackId == null) + { + Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + return await base.StreamTrack(trackId.Value, this.TrackService, this.PlayActivityService); + } + [HttpGet("getCoverArt.view")] [ProducesResponseType(200)] public async Task GetCoverArt([FromQuery]Request request, int? size) @@ -106,6 +131,9 @@ namespace Roadie.Api.Controllers return this.BuildResponse(request, result.Data); } + + + #region Response Builder Methods private string BuildJsonResult(Response response, string responseType) diff --git a/RoadieApi/Services/ISubsonicService.cs b/RoadieApi/Services/ISubsonicService.cs index 0bdd8af..1691660 100644 --- a/RoadieApi/Services/ISubsonicService.cs +++ b/RoadieApi/Services/ISubsonicService.cs @@ -18,6 +18,8 @@ namespace Roadie.Api.Services Task> GetMusicDirectory(Request request, Roadie.Library.Models.Users.User roadieUser, string id); + Task> GetAlbumList(Request request, Roadie.Library.Models.Users.User roadieUser); + Task> GetCoverArt(Request request, int? size); OperationResult Ping(Request request); diff --git a/RoadieApi/Services/ReleaseService.cs b/RoadieApi/Services/ReleaseService.cs index 0dc7872..0ad045e 100644 --- a/RoadieApi/Services/ReleaseService.cs +++ b/RoadieApi/Services/ReleaseService.cs @@ -110,16 +110,16 @@ namespace Roadie.Api.Services var sw = new Stopwatch(); sw.Start(); - if (!string.IsNullOrEmpty(request.Sort)) - { - request.Sort = request.Sort.Replace("createdDate", "createdDateTime"); - request.Sort = request.Sort.Replace("lastUpdated", "lastUpdatedDateTime"); - request.Sort = request.Sort.Replace("ReleaseDate", "ReleaseDateDateTime"); - request.Sort = request.Sort.Replace("releaseYear", "ReleaseDateDateTime"); - } - var result = (from r in this.DbContext.Releases.Include("Artist") join a in this.DbContext.Artists on r.ArtistId equals a.Id + let lastPlayed = (from ut in this.DbContext.UserTracks + join t in this.DbContext.Tracks on ut.TrackId equals t.Id + join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id + join rl in this.DbContext.Releases on rm.ReleaseId equals rl.Id + where rl.Id == r.Id + orderby ut.LastPlayed descending + select ut.LastPlayed + ).FirstOrDefault() where (request.FilterMinimumRating == null || r.Rating >= request.FilterMinimumRating.Value) where (request.FilterToArtistId == null || r.Artist.RoadieId == request.FilterToArtistId) where (request.FilterValue == "" || (r.Title.Contains(request.FilterValue) || r.AlternateNames.Contains(request.FilterValue))) @@ -146,6 +146,7 @@ namespace Roadie.Api.Services TrackCount = r.TrackCount, CreatedDate = r.CreatedDate, LastUpdated = r.LastUpdated, + LastPlayed = lastPlayed != null ? lastPlayed : null, TrackPlayedCount = (from ut in this.DbContext.UserTracks join t in this.DbContext.Tracks on ut.TrackId equals t.Id join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id @@ -161,7 +162,8 @@ namespace Roadie.Api.Services if (doRandomize ?? false) { - request.Limit = request.LimitValue > roadieUser.RandomReleaseLimit ? roadieUser.RandomReleaseLimit : request.LimitValue; + var randomLimit = roadieUser?.RandomReleaseLimit ?? 100; + request.Limit = request.LimitValue > randomLimit ? randomLimit : request.LimitValue; rows = result.OrderBy(x => Guid.NewGuid()).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); } else @@ -175,6 +177,14 @@ namespace Roadie.Api.Services { sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary { { "Release.Text", "ASC" } }) : request.OrderValue(null); } + if(request.FilterRatedOnly) + { + result = result.Where(x => x.Rating.HasValue); + } + if(request.FilterMinimumRating.HasValue) + { + result = result.Where(x => x.Rating.HasValue && x.Rating.Value >= request.FilterMinimumRating.Value); + } rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); } if (rows.Any() && roadieUser != null) @@ -184,11 +194,14 @@ namespace Roadie.Api.Services var row = rows.FirstOrDefault(x => x.DatabaseId == userReleaseRatings.ReleaseId); if (row != null) { + var isDisliked = userReleaseRatings.IsDisliked ?? false; + var isFavorite = userReleaseRatings.IsFavorite ?? false; row.UserRating = new UserRelease { - IsDisliked = userReleaseRatings.IsDisliked ?? false, - IsFavorite = userReleaseRatings.IsFavorite ?? false, - Rating = userReleaseRatings.Rating + IsDisliked = isDisliked, + IsFavorite = isFavorite, + Rating = userReleaseRatings.Rating, + RatedDate = isDisliked || isFavorite ? (DateTime?)(userReleaseRatings.LastUpdated ?? userReleaseRatings.CreatedDate) : null }; } } diff --git a/RoadieApi/Services/ServiceBase.cs b/RoadieApi/Services/ServiceBase.cs index c416df7..2f0d3c4 100644 --- a/RoadieApi/Services/ServiceBase.cs +++ b/RoadieApi/Services/ServiceBase.cs @@ -159,6 +159,21 @@ namespace Roadie.Api.Services }, data.Track.CacheRegionUrn(id)); } + protected ApplicationUser GetUser(string username) + { + if (string.IsNullOrEmpty(username)) + { + return null; + } + var userByUsername = this.CacheManager.Get(ApplicationUser.CacheUrnByUsername(username), () => + { + return this.DbContext.Users + .FirstOrDefault(x => x.UserName == username); + + }, null); + return this.GetUser(userByUsername?.RoadieId); + } + protected ApplicationUser GetUser(Guid? id) { if(!id.HasValue) diff --git a/RoadieApi/Services/SubsonicService.cs b/RoadieApi/Services/SubsonicService.cs index 0863c2c..2efd8ee 100644 --- a/RoadieApi/Services/SubsonicService.cs +++ b/RoadieApi/Services/SubsonicService.cs @@ -5,6 +5,7 @@ using Roadie.Library.Configuration; using Roadie.Library.Encoding; using Roadie.Library.Extensions; using Roadie.Library.Imaging; +using Roadie.Library.Models.Releases; using Roadie.Library.Models.Users; using Roadie.Library.Utility; using System; @@ -28,6 +29,8 @@ namespace Roadie.Api.Services { public const string SubsonicVersion = "1.16.1"; + private IReleaseService ReleaseService { get; } + public SubsonicService(IRoadieSettings configuration, IHttpEncoder httpEncoder, IHttpContext httpContext, @@ -35,9 +38,11 @@ namespace Roadie.Api.Services ICacheManager cacheManager, ILogger logger, ICollectionService collectionService, - IPlaylistService playlistService) + IPlaylistService playlistService, + IReleaseService releaseService) : base(configuration, httpEncoder, context, cacheManager, logger, httpContext) { + this.ReleaseService = releaseService; } public OperationResult Ping(subsonic.Request request) @@ -348,6 +353,7 @@ namespace Roadie.Api.Services genre = genre != null ? genre.Genre.Name : null, coverArt = subsonic.Request.ReleaseIdIdentifier + r.RoadieId.ToString(), created = collection.CreatedDate, + path = $"{ r.Artist.Name}/{ r.Title}/", playCount = playCount ?? 0 }).ToArray(); @@ -480,12 +486,12 @@ namespace Roadie.Api.Services } else if(!string.IsNullOrEmpty(request.u)) { - var userByUsername = this.DbContext.Users.FirstOrDefault(x => x.UserName == request.u); - if(userByUsername == null) + var user = this.GetUser(request.u); + if(user == null) { return new FileOperationResult(true, $"Invalid Username [{ request.u}]"); } - result.Data.Bytes = userByUsername.Avatar; + result.Data.Bytes = user.Avatar; } if (size.HasValue && result.Data.Bytes != null) @@ -508,5 +514,82 @@ namespace Roadie.Api.Services }; } + public async Task> GetAlbumList(subsonic.Request request, User roadieUser) + { + var releaseResult = new Library.Models.Pagination.PagedResult(); + + switch (request.Type) + { + case subsonic.ListType.Random: + releaseResult = await this.ReleaseService.List(roadieUser, request.PagedRequest, true); + break; + case subsonic.ListType.Highest: + case subsonic.ListType.Recent: + case subsonic.ListType.Newest: + case subsonic.ListType.Frequent: + releaseResult = await this.ReleaseService.List(roadieUser, request.PagedRequest); + break; + break; + case subsonic.ListType.AlphabeticalByName: + break; + case subsonic.ListType.AlphabeticalByArtist: + break; + case subsonic.ListType.Starred: + releaseResult = await this.ReleaseService.List(roadieUser, request.PagedRequest); + break; + case subsonic.ListType.ByYear: + break; + case subsonic.ListType.ByGenre: + break; + default: + return new OperationResult($"Unknown Album List Type [{ request.Type}]"); + } + + if(!releaseResult.IsSuccess) + { + return new OperationResult(releaseResult.Message); + } + + var albums = releaseResult.Rows.Select(r => new subsonic.Child + { + id = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + parent = subsonic.Request.ArtistIdIdentifier + r.Artist.Value, + isDir = true, + title = r.Release.Text, + album = r.Release.Text, + albumId = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + artist = r.Artist.Text, + year = SafeParser.ToNumber(r.ReleaseYear), + // genre = r.Genre.Text, + coverArt = subsonic.Request.ReleaseIdIdentifier + r.Id.ToString(), + averageRating = r.Rating ?? 0, + averageRatingSpecified = true, + created = r.CreatedDate.Value, + createdSpecified = true, + path = $"{ r.Artist.Text}/{ r.Release.Text}/", + playCount = r.TrackPlayedCount ?? 0, + playCountSpecified = true, + starred = r.UserRating != null ? (r.UserRating.IsFavorite ? r.UserRating.RatedDate : null) : null, + userRating = r.UserRating != null ? r.UserRating.Rating ?? 0 : 0, + userRatingSpecified = r.UserRating != null && r.UserRating.Rating != null + }).ToArray(); + + return new OperationResult + { + IsSuccess = true, + Data = new subsonic.Response + { + version = SubsonicService.SubsonicVersion, + status = subsonic.ResponseStatus.ok, + ItemElementName = subsonic.ItemChoiceType.albumList, + Item = new subsonic.AlbumList + { + album = albums + } + } + }; + + } + } } \ No newline at end of file diff --git a/RoadieLibrary/Identity/ApplicationUserPartial.cs b/RoadieLibrary/Identity/ApplicationUserPartial.cs index c56fa19..5a36903 100644 --- a/RoadieLibrary/Identity/ApplicationUserPartial.cs +++ b/RoadieLibrary/Identity/ApplicationUserPartial.cs @@ -16,6 +16,11 @@ namespace Roadie.Library.Identity return $"urn:user_by_id:{ Id }"; } + public static string CacheUrnByUsername(string Username) + { + return $"urn:user_by_username:{ Username }"; + } + public string CacheRegion { get @@ -24,6 +29,14 @@ namespace Roadie.Library.Identity } } + public string CacheKeyByUsername + { + get + { + return ApplicationUser.CacheUrnByUsername(this.UserName); + } + } + public string CacheKey { get diff --git a/RoadieLibrary/Models/Pagination/PagedRequest.cs b/RoadieLibrary/Models/Pagination/PagedRequest.cs index 6eceb7d..2acb029 100644 --- a/RoadieLibrary/Models/Pagination/PagedRequest.cs +++ b/RoadieLibrary/Models/Pagination/PagedRequest.cs @@ -89,6 +89,7 @@ namespace Roadie.Library.Models.Pagination public Guid? FilterToArtistId { get; set; } public int? FilterMinimumRating { get; set; } + public bool FilterRatedOnly { get; internal set; } public PagedRequest() { } diff --git a/RoadieLibrary/Models/Releases/ReleaseList.cs b/RoadieLibrary/Models/Releases/ReleaseList.cs index 1546fa0..80090ad 100644 --- a/RoadieLibrary/Models/Releases/ReleaseList.cs +++ b/RoadieLibrary/Models/Releases/ReleaseList.cs @@ -44,5 +44,7 @@ namespace Roadie.Library.Models.Releases public int? TrackPlayedCount { get; set; } public UserRelease UserRating { get; set; } public Statuses? Status { get; set; } + public DataToken Genre { get; set; } + public DateTime? LastPlayed { get; set; } } } diff --git a/RoadieLibrary/Models/ThirdPartyApi/Subsonic/ListType.cs b/RoadieLibrary/Models/ThirdPartyApi/Subsonic/ListType.cs new file mode 100644 index 0000000..63c7159 --- /dev/null +++ b/RoadieLibrary/Models/ThirdPartyApi/Subsonic/ListType.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Roadie.Library.Models.ThirdPartyApi.Subsonic +{ + public enum ListType : short + { + Unknown = 0, + Random, + Newest, + Highest, + Frequent, + Recent, + AlphabeticalByName, + AlphabeticalByArtist, + Starred, + ByYear, + ByGenre + } +} diff --git a/RoadieLibrary/Models/ThirdPartyApi/Subsonic/Request.cs b/RoadieLibrary/Models/ThirdPartyApi/Subsonic/Request.cs index 2ca0957..94a66d4 100644 --- a/RoadieLibrary/Models/ThirdPartyApi/Subsonic/Request.cs +++ b/RoadieLibrary/Models/ThirdPartyApi/Subsonic/Request.cs @@ -7,6 +7,8 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic [Serializable] public class Request { + public const int MaxPageSize = 500; + public const string ArtistIdIdentifier = "A:"; public const string CollectionIdentifier = "C:"; public const string ReleaseIdIdentifier = "R:"; @@ -159,6 +161,112 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic } } + public Guid? TrackId + { + get + { + if (string.IsNullOrEmpty(this.id)) + { + return null; + } + if (this.id.StartsWith(Request.TrackIdIdentifier)) + { + return SafeParser.ToGuid(this.id.Replace(Request.TrackIdIdentifier, "")); + } + return null; + } + } + + #region Paging and List Related + + /// + /// The number of albums to return. Max 500. + /// + public int? Size { get; set; } + + /// + /// The list offset. Useful if you for example want to page through the list of newest albums. + /// + public int? Offset { get; set; } + + /// + /// The first year in the range. If fromYear > toYear a reverse chronological list is returned. + /// + public int? FromYear { get; set; } + + /// + /// The last year in the range. + /// + public int? ToYear { get; set; } + + /// + /// The name of the genre, e.g., "Rock". + /// + public string Genre { get; set; } + + /// + /// Only return albums in the music folder with the given ID. See getMusicFolders. + /// + public int? MusicFolderId { get; set; } + + public ListType Type { get; set; } + + //var pagedRequest = new Library.Models.Pagination.PagedRequest + //{ + + //}; + + private Library.Models.Pagination.PagedRequest _pagedRequest; + + public Library.Models.Pagination.PagedRequest PagedRequest + { + get + { + if(this._pagedRequest == null) + { + var limit = this.Size ?? Request.MaxPageSize; + var page = this.Offset > 0 ? (int)Math.Ceiling((decimal)this.Offset.Value / (decimal)limit) : 1; + var pagedRequest = new Pagination.PagedRequest(); + switch (this.Type) + { + case ListType.Newest: + pagedRequest.Sort = "CreatedDate"; + pagedRequest.Order = "DESC"; + break; + case ListType.Highest: + pagedRequest.Sort = "Rating"; + pagedRequest.Order = "DESC"; + pagedRequest.FilterRatedOnly = true; + break; + case ListType.Frequent: + pagedRequest.Sort = "TrackPlayedCount"; + pagedRequest.Order = "DESC"; + break; + case ListType.Recent: + pagedRequest.Sort = "LastPlayed"; + pagedRequest.Order = "DESC"; + break; + case ListType.AlphabeticalByName: + break; + case ListType.AlphabeticalByArtist: + break; + case ListType.Starred: + pagedRequest.FilterRatedOnly = true; + break; + case ListType.ByGenre: + break; + default: + break; + } + pagedRequest.Limit = limit; + pagedRequest.Page = page; + this._pagedRequest = pagedRequest; + } + return this._pagedRequest; + } + } + + #endregion //public user CheckPasswordGetUser(ICacheManager cacheManager, RoadieDbContext context) diff --git a/RoadieLibrary/Models/Users/UserRatingBase.cs b/RoadieLibrary/Models/Users/UserRatingBase.cs index 0b658fb..7cbd175 100644 --- a/RoadieLibrary/Models/Users/UserRatingBase.cs +++ b/RoadieLibrary/Models/Users/UserRatingBase.cs @@ -8,5 +8,6 @@ namespace Roadie.Library.Models.Users public bool IsDisliked { get; set; } public bool IsFavorite { get; set; } public short? Rating { get; set; } + public DateTime? RatedDate { get; set; } } } \ No newline at end of file