Subsonic API bookmark management implemented.

This commit is contained in:
Steven Hildreth 2018-11-24 11:52:15 -06:00
parent 62ad0cee29
commit d8b096b543
12 changed files with 710 additions and 548 deletions

View file

@ -58,6 +58,49 @@ namespace Roadie.Api.Controllers
return this.BuildResponse(request, result, "album");
}
[HttpGet("createBookmark.view")]
[HttpPost("createBookmark.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> CreateBookmark(SubsonicRequest request, int position, string comment)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.CreateBookmark(request, this.SubsonicUser, position, comment);
return this.BuildResponse(request, result);
}
[HttpGet("deleteBookmark.view")]
[HttpPost("deleteBookmark.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteBookmark(SubsonicRequest request)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.DeleteBookmark(request, this.SubsonicUser);
return this.BuildResponse(request, result);
}
[HttpGet("getBookmarks.view")]
[HttpPost("getBookmarks.view")]
[ProducesResponseType(200)]
public async Task<IActionResult> GetBookmarks(SubsonicRequest request)
{
var authResult = await this.AuthenticateUser(request);
if (authResult != null)
{
return authResult;
}
var result = await this.SubsonicService.GetBookmarks(request, this.SubsonicUser);
return this.BuildResponse(request, result, "bookmarks");
}
[HttpGet("star.view")]
[HttpPost("star.view")]
[ProducesResponseType(200)]

View file

@ -29,6 +29,7 @@ namespace Roadie.Api.Services
{
private ICollectionService CollectionService { get; } = null;
private IPlaylistService PlaylistService { get; } = null;
private IBookmarkService BookmarkService { get; } = null;
public ArtistService(IRoadieSettings configuration,
IHttpEncoder httpEncoder,
@ -37,11 +38,14 @@ namespace Roadie.Api.Services
ICacheManager cacheManager,
ILogger<ArtistService> logger,
ICollectionService collectionService,
IPlaylistService playlistService)
IPlaylistService playlistService,
IBookmarkService bookmarkService
)
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
{
this.CollectionService = collectionService;
this.PlaylistService = playlistService;
this.BookmarkService = bookmarkService;
}
public async Task<OperationResult<Artist>> ById(User roadieUser, Guid id, IEnumerable<string> includes)
@ -56,7 +60,11 @@ namespace Roadie.Api.Services
if (result?.Data != null && roadieUser != null)
{
var artist = this.GetArtist(id);
result.Data.UserBookmark = this.GetUserBookmarks(roadieUser).FirstOrDefault(x => x.Type == BookmarkType.Artist && x.Bookmark.Value == artist.RoadieId.ToString());
var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Artist);
if(userBookmarkResult.IsSuccess)
{
result.Data.UserBookmark = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == artist.RoadieId.ToString());
}
var userArtist = this.DbContext.UserArtists.FirstOrDefault(x => x.ArtistId == artist.Id && x.UserId == roadieUser.Id);
if (userArtist != null)
{

View file

@ -33,31 +33,33 @@ namespace Roadie.Api.Services
public async Task<Library.Models.Pagination.PagedResult<BookmarkList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, BookmarkType? filterType = null)
{
var sw = new Stopwatch();
sw.Start();
if (!string.IsNullOrEmpty(request.Sort))
{
request.Sort = request.Sort.Replace("bookmarkType", "bookmarkTypeValue");
request.Sort = request.Sort.Replace("createdDate", "createdDateTime");
request.Sort = request.Sort.Replace("lastUpdated", "lastUpdatedDateTime");
}
var result = (from b in this.DbContext.Bookmarks
join u in this.DbContext.Users on b.UserId equals u.Id
where b.UserId == roadieUser.Id
where (filterType == null || b.BookmarkType == filterType)
select new BookmarkList
{
Comment = b.Comment,
Position = b.Position,
User = new DataToken
{
Text = u.UserName,
Value = u.RoadieId.ToString()
},
DatabaseId = b.Id,
Id = b.RoadieId,
CreatedDate = b.CreatedDate,
LastUpdated = b.LastUpdated,
Type = b.BookmarkType,
BookmarkTargetId = b.BookmarkTargetId
BookmarkTargetId = b.BookmarkTargetId
});
var sortBy = string.IsNullOrEmpty(request.Sort) ? request.OrderValue(new Dictionary<string, string> { { "CreatedDate", "DESC" } }) : request.OrderValue(null);
var rowCount = result.Count();
BookmarkList[] rows = rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
BookmarkList[] rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
var datas = (from b in rows
join a in this.DbContext.Artists on b.BookmarkTargetId equals a.Id into aa
@ -65,7 +67,7 @@ namespace Roadie.Api.Services
join r in this.DbContext.Releases on b.BookmarkTargetId equals r.Id into rr
from r in rr.DefaultIfEmpty()
join t in this.DbContext.Tracks on b.BookmarkTargetId equals t.Id into tt
from t in rr.DefaultIfEmpty()
from t in tt.DefaultIfEmpty()
join p in this.DbContext.Playlists on b.BookmarkTargetId equals p.Id into pp
from p in pp.DefaultIfEmpty()
join c in this.DbContext.Collections on b.BookmarkTargetId equals c.Id into cc
@ -87,6 +89,7 @@ namespace Roadie.Api.Services
Value = d.a.RoadieId.ToString()
};
row.Thumbnail = this.MakeArtistThumbnailImage(d.a.RoadieId);
row.SortName = d.a.SortName ?? d.a.Name;
break;
case BookmarkType.Release:
@ -96,6 +99,7 @@ namespace Roadie.Api.Services
Value = d.r.RoadieId.ToString()
};
row.Thumbnail = this.MakeReleaseThumbnailImage(d.r.RoadieId);
row.SortName = d.r.Title;
break;
case BookmarkType.Track:
@ -105,6 +109,7 @@ namespace Roadie.Api.Services
Value = d.t.RoadieId.ToString()
};
row.Thumbnail = this.MakeTrackThumbnailImage(d.t.RoadieId);
row.SortName = d.t.Title;
break;
case BookmarkType.Playlist:
@ -114,6 +119,7 @@ namespace Roadie.Api.Services
Value = d.p.RoadieId.ToString()
};
row.Thumbnail = this.MakePlaylistThumbnailImage(d.p.RoadieId);
row.SortName = d.p.Name;
break;
case BookmarkType.Collection:
@ -123,6 +129,7 @@ namespace Roadie.Api.Services
Value = d.c.RoadieId.ToString()
};
row.Thumbnail = this.MakeCollectionThumbnailImage(d.c.RoadieId);
row.SortName = d.c.SortName ?? d.c.Name;
break;
case BookmarkType.Label:
@ -132,6 +139,7 @@ namespace Roadie.Api.Services
Value = d.l.RoadieId.ToString()
};
row.Thumbnail = this.MakeLabelThumbnailImage(d.l.RoadieId);
row.SortName = d.l.SortName ?? d.l.Name;
break;
}
};
@ -139,7 +147,7 @@ namespace Roadie.Api.Services
sw.Stop();
return new Library.Models.Pagination.PagedResult<BookmarkList>
{
{
TotalCount = rowCount,
CurrentPage = request.PageValue,
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),

View file

@ -8,6 +8,10 @@ namespace Roadie.Api.Services
{
Task<SubsonicOperationResult<SubsonicAuthenticateResponse>> Authenticate(Request request);
Task<SubsonicOperationResult<Response>> CreateBookmark(Request request, Roadie.Library.Models.Users.User roadieUser, int position, string comment);
Task<SubsonicOperationResult<Response>> DeleteBookmark(Request request, Roadie.Library.Models.Users.User roadieUser);
Task<SubsonicOperationResult<Response>> GetAlbum(Request request, Roadie.Library.Models.Users.User roadieUser);
Task<SubsonicOperationResult<Response>> GetAlbumInfo(Request request, Roadie.Library.Models.Users.User roadieUser, AlbumInfoVersion version);
@ -20,6 +24,8 @@ namespace Roadie.Api.Services
Task<SubsonicOperationResult<Response>> GetArtists(Request request, Roadie.Library.Models.Users.User roadieUser);
Task<SubsonicOperationResult<Response>> GetBookmarks(Request request, Roadie.Library.Models.Users.User roadieUser);
Task<SubsonicFileOperationResult<Roadie.Library.Models.Image>> GetCoverArt(Request request, int? size);
Task<SubsonicOperationResult<Response>> GetGenres(Request request);

View file

@ -27,14 +27,18 @@ namespace Roadie.Api.Services
{
public class ReleaseService : ServiceBase, IReleaseService
{
private IBookmarkService BookmarkService { get; } = null;
public ReleaseService(IRoadieSettings configuration,
IHttpEncoder httpEncoder,
IHttpContext httpContext,
data.IRoadieDbContext dbContext,
ICacheManager cacheManager,
ILogger<ReleaseService> logger)
ILogger<ReleaseService> logger,
IBookmarkService bookmarkService)
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
{
this.BookmarkService = bookmarkService;
}
public async Task<OperationResult<Release>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
@ -49,8 +53,11 @@ namespace Roadie.Api.Services
if (result?.Data != null && roadieUser != null)
{
var release = this.GetRelease(id);
result.Data.UserBookmark = this.GetUserBookmarks(roadieUser).FirstOrDefault(x => x.Type == BookmarkType.Release && x.Bookmark.Value == release.RoadieId.ToString());
var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Release);
if (userBookmarkResult.IsSuccess)
{
result.Data.UserBookmark = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == release.RoadieId.ToString());
}
if (result.Data.Medias != null)
{
var releaseTrackIds = result.Data.Medias.SelectMany(x => x.Tracks).Select(x => x.Id);

View file

@ -93,9 +93,8 @@ namespace Roadie.Api.Services
{
return this.DbContext.Artists
.FirstOrDefault(x => x.Name == artistName);
}, null);
if(artistByName == null)
if (artistByName == null)
{
return null;
}
@ -156,10 +155,13 @@ namespace Roadie.Api.Services
}, data.Release.CacheRegionUrn(id));
}
/// <summary>
/// Get Track by Subsonic Id ("T:guid")
/// </summary>
protected data.Track GetTrack(string id)
{
Guid trackId = Guid.Empty;
if(Guid.TryParse(id, out trackId))
if (Guid.TryParse(id, out trackId))
{
return this.GetTrack(trackId);
}
@ -188,15 +190,14 @@ namespace Roadie.Api.Services
var userByUsername = this.CacheManager.Get(ApplicationUser.CacheUrnByUsername(username), () =>
{
return this.DbContext.Users
.FirstOrDefault(x => x.UserName == username);
.FirstOrDefault(x => x.UserName == username);
}, null);
return this.GetUser(userByUsername?.RoadieId);
}
protected ApplicationUser GetUser(Guid? id)
{
if(!id.HasValue)
if (!id.HasValue)
{
return null;
}
@ -211,113 +212,9 @@ namespace Roadie.Api.Services
.Include("UserRoles.Role.RoleClaims")
.Include(x => x.Claims)
.FirstOrDefault(x => x.RoadieId == id);
}, ApplicationUser.CacheRegionUrn(id.Value));
}
protected List<BookmarkList> GetUserBookmarks(User roadieUser)
{
if(roadieUser == null)
{
return null;
}
return this.CacheManager.Get($"urn:user_bookmarks:{ roadieUser.Id }", () =>
{
var bookmarks = from b in this.DbContext.Bookmarks
join a in this.DbContext.Artists on b.BookmarkTargetId equals a.Id into aa
from a in aa.DefaultIfEmpty()
join r in this.DbContext.Releases on b.BookmarkTargetId equals r.Id into rr
from r in rr.DefaultIfEmpty()
join t in this.DbContext.Tracks on b.BookmarkTargetId equals t.Id into tt
from t in tt.DefaultIfEmpty()
join p in this.DbContext.Playlists on b.BookmarkTargetId equals p.Id into pp
from p in pp.DefaultIfEmpty()
join c in this.DbContext.Collections on b.BookmarkTargetId equals c.Id into cc
from c in cc.DefaultIfEmpty()
join l in this.DbContext.Labels on b.BookmarkTargetId equals l.Id into ll
from l in ll.DefaultIfEmpty()
where b.UserId == roadieUser.Id
select new
{
b,
a,
r,
t,
p,
c,
l
};
var result = new List<BookmarkList>();
foreach (var bookmark in bookmarks)
{
var b = bookmark.b.Adapt<BookmarkList>();
if (bookmark.a != null)
{
b.Bookmark = new DataToken
{
Text = bookmark.a.Name,
Value = bookmark.a.RoadieId.ToString()
};
b.Thumbnail = this.MakeArtistThumbnailImage(bookmark.a.RoadieId);
continue;
}
if (bookmark.r != null)
{
b.Bookmark = new DataToken
{
Text = bookmark.r.Title,
Value = bookmark.r.RoadieId.ToString()
};
b.Thumbnail = this.MakeReleaseThumbnailImage(bookmark.r.RoadieId);
continue;
}
if (bookmark.t != null)
{
b.Bookmark = new DataToken
{
Text = bookmark.t.Title,
Value = bookmark.t.RoadieId.ToString()
};
b.Thumbnail = this.MakeTrackThumbnailImage(bookmark.t.RoadieId);
continue;
}
if (bookmark.p != null)
{
b.Bookmark = new DataToken
{
Text = bookmark.p.Name,
Value = bookmark.p.RoadieId.ToString()
};
b.Thumbnail = this.MakePlaylistThumbnailImage(bookmark.p.RoadieId);
continue;
}
if (bookmark.c != null)
{
b.Bookmark = new DataToken
{
Text = bookmark.c.Name,
Value = bookmark.c.RoadieId.ToString()
};
b.Thumbnail = this.MakeCollectionThumbnailImage(bookmark.c.RoadieId);
continue;
}
if (bookmark.l != null)
{
b.Bookmark = new DataToken
{
Text = bookmark.l.Name,
Value = bookmark.l.RoadieId.ToString()
};
b.Thumbnail = this.MakeLabelThumbnailImage(bookmark.l.RoadieId);
continue;
}
result.Add(b);
}
return result;
}, ApplicationUser.CacheRegionUrn(roadieUser.UserId));
}
protected Image MakeArtistThumbnailImage(Guid id)
{
return MakeThumbnailImage(id, "artist");
@ -328,17 +225,26 @@ namespace Roadie.Api.Services
return MakeThumbnailImage(id, "collection");
}
protected Image MakeImage(Guid id, int width = 200, int height = 200)
{
return new Image($"{this.HttpContext.ImageBaseUrl }/{id}/{ width }/{ height }");
}
protected Image MakeImage(Guid id, string type, ImageSize imageSize)
{
return this.MakeImage(id, type, imageSize.Width, imageSize.Height);
}
protected Image MakeLabelThumbnailImage(Guid id)
{
return MakeThumbnailImage(id, "label");
}
protected string MakeLastFmUrl(string artistName, string releaseTitle)
{
return "http://www.last.fm/music/" + this.HttpEncoder.UrlEncode($"{ artistName }/{ releaseTitle }");
}
protected Image MakePlaylistThumbnailImage(Guid id)
{
return MakeThumbnailImage(id, "playlist");
@ -359,17 +265,6 @@ namespace Roadie.Api.Services
return MakeThumbnailImage(id, "user");
}
private Image MakeThumbnailImage(Guid id, string type)
{
return this.MakeImage(id, type, this.Configuration.ThumbnailImageSize.Width, this.Configuration.ThumbnailImageSize.Height);
}
protected Image MakeImage(Guid id, string type, ImageSize imageSize)
{
return this.MakeImage(id, type, imageSize.Width, imageSize.Height);
}
private Image MakeImage(Guid id, string type, int? width, int? height)
{
if (width.HasValue && height.HasValue)
@ -379,10 +274,9 @@ namespace Roadie.Api.Services
return new Image($"{this.HttpContext.ImageBaseUrl }/{type}/{id}");
}
protected string MakeLastFmUrl(string artistName, string releaseTitle)
private Image MakeThumbnailImage(Guid id, string type)
{
return "http://www.last.fm/music/" + this.HttpEncoder.UrlEncode($"{ artistName }/{ releaseTitle }");
return this.MakeImage(id, type, this.Configuration.ThumbnailImageSize.Width, this.Configuration.ThumbnailImageSize.Height);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -270,6 +270,26 @@ namespace Roadie.Api.Services
randomTrackIds = this.DbContext.Tracks.FromSql(sql).Select(x => x.Id).ToArray();
}
Guid?[] filterToTrackIds = null;
if(request.FilterToTrackId.HasValue || request.FilterToTrackIds != null)
{
var f = new List<Guid?>();
if(request.FilterToTrackId.HasValue)
{
f.Add(request.FilterToTrackId);
}
if (request.FilterToTrackIds != null)
{
foreach (var ft in request.FilterToTrackIds)
{
if (!f.Contains(ft))
{
f.Add(ft);
}
}
}
filterToTrackIds = f.ToArray();
}
var resultQuery = (from t in this.DbContext.Tracks
join rm in this.DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in this.DbContext.Releases on rm.ReleaseId equals r.Id
@ -279,7 +299,7 @@ namespace Roadie.Api.Services
from releaseArtist in aa.DefaultIfEmpty()
where (t.Hash != null)
where (releaseId == null || (releaseId != null && r.RoadieId == releaseId))
where (request.FilterToTrackId == null || request.FilterToTrackId != null && t.RoadieId == request.FilterToTrackId)
where (filterToTrackIds == null || filterToTrackIds.Contains(t.RoadieId))
where (request.FilterMinimumRating == null || t.Rating >= request.FilterMinimumRating.Value)
where (request.FilterValue == "" || (t.Title.Contains(request.FilterValue) || t.AlternateNames.Contains(request.FilterValue)))
where (!request.FilterFavoriteOnly || favoriteTrackIds.Contains(t.Id))

View file

@ -141,13 +141,13 @@ namespace Roadie.Api
services.AddScoped<IStatisticsService, StatisticsService>();
services.AddScoped<ICollectionService, CollectionService>();
services.AddScoped<IPlaylistService, PlaylistService>();
services.AddScoped<IBookmarkService, BookmarkService>();
services.AddScoped<IArtistService, ArtistService>();
services.AddScoped<IImageService, ImageService>();
services.AddScoped<IReleaseService, ReleaseService>();
services.AddScoped<ITrackService, TrackService>();
services.AddScoped<ILabelService, LabelService>();
services.AddScoped<IPlaylistService, PlaylistService>();
services.AddScoped<IBookmarkService, BookmarkService>();
services.AddScoped<IPlayActivityService, PlayActivityService>();
services.AddScoped<IGenreService, GenreService>();
services.AddScoped<ISubsonicService, SubsonicService>();

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Roadie.Library.Data
{
public partial class Bookmark
{
public override string ToString()
{
return $"Id [{ this.Id }], BookmarkType [{ this.BookmarkType }], BookmarkTargetId [{ this.BookmarkTargetId }]";
}
}
}

View file

@ -9,10 +9,13 @@ namespace Roadie.Library.Models
[Serializable]
public class BookmarkList : EntityInfoModelBase
{
public DataToken User { get; set; }
public DataToken Bookmark { get; set; }
public Image Thumbnail { get; set; }
public BookmarkType? Type { get; set; }
[JsonIgnore]
public int BookmarkTargetId { get; set; }
public string Comment { get; set; }
public int? Position { get; set; }
}
}

View file

@ -110,6 +110,7 @@ namespace Roadie.Library.Models.Pagination
public int? FilterFromYear { get; set; }
public int? FilterToYear { get; set; }
public string FilterByGenre { get; set; }
public Guid?[] FilterToTrackIds { get; set; }
public PagedRequest()
{ }