2018-12-16 23:37:19 +00:00
|
|
|
|
using Mapster;
|
2018-12-17 00:08:55 +00:00
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
2018-12-16 23:37:19 +00:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2019-03-10 16:55:21 +00:00
|
|
|
|
using Newtonsoft.Json;
|
2018-12-01 03:22:35 +00:00
|
|
|
|
using Roadie.Library;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
using Roadie.Library.Caching;
|
|
|
|
|
using Roadie.Library.Configuration;
|
|
|
|
|
using Roadie.Library.Encoding;
|
2019-06-30 22:14:36 +00:00
|
|
|
|
using Roadie.Library.Enums;
|
2018-12-01 03:22:35 +00:00
|
|
|
|
using Roadie.Library.Identity;
|
2018-12-16 23:37:19 +00:00
|
|
|
|
using Roadie.Library.Imaging;
|
2019-06-08 22:32:15 +00:00
|
|
|
|
using Roadie.Library.MetaData.LastFm;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
using Roadie.Library.Models.Pagination;
|
2019-06-30 22:14:36 +00:00
|
|
|
|
using Roadie.Library.Models.Releases;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
using Roadie.Library.Models.Statistics;
|
|
|
|
|
using Roadie.Library.Models.Users;
|
|
|
|
|
using Roadie.Library.Utility;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Linq.Dynamic.Core;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using data = Roadie.Library.Data;
|
2019-01-10 23:40:04 +00:00
|
|
|
|
using models = Roadie.Library.Models;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
|
|
|
|
|
namespace Roadie.Api.Services
|
|
|
|
|
{
|
|
|
|
|
public class UserService : ServiceBase, IUserService
|
|
|
|
|
{
|
2019-06-08 22:32:15 +00:00
|
|
|
|
private ILastFmHelper LastFmHelper { get; }
|
|
|
|
|
|
2018-12-17 00:08:55 +00:00
|
|
|
|
private UserManager<ApplicationUser> UserManager { get; }
|
|
|
|
|
|
2018-11-15 15:10:29 +00:00
|
|
|
|
public UserService(IRoadieSettings configuration,
|
2019-06-30 22:14:36 +00:00
|
|
|
|
IHttpEncoder httpEncoder,
|
2019-07-07 03:16:33 +00:00
|
|
|
|
IHttpContext httpContext,
|
|
|
|
|
data.IRoadieDbContext context,
|
|
|
|
|
ICacheManager cacheManager,
|
|
|
|
|
ILogger<ArtistService> logger,
|
|
|
|
|
UserManager<ApplicationUser> userManager,
|
|
|
|
|
ILastFmHelper lastFmHelper
|
2019-06-30 22:14:36 +00:00
|
|
|
|
)
|
2018-11-15 15:10:29 +00:00
|
|
|
|
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
UserManager = userManager;
|
2019-07-07 03:16:33 +00:00
|
|
|
|
LastFmHelper = lastFmHelper;
|
2019-06-30 22:14:36 +00:00
|
|
|
|
;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-10 23:40:04 +00:00
|
|
|
|
public async Task<OperationResult<User>> ById(User user, Guid id, IEnumerable<string> includes)
|
2018-12-16 23:37:19 +00:00
|
|
|
|
{
|
|
|
|
|
var timings = new Dictionary<string, long>();
|
|
|
|
|
var tsw = new Stopwatch();
|
|
|
|
|
|
|
|
|
|
var sw = Stopwatch.StartNew();
|
|
|
|
|
sw.Start();
|
|
|
|
|
var cacheKey = string.Format("urn:user_by_id_operation:{0}", id);
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var result = await CacheManager.GetAsync(cacheKey, async () =>
|
2018-12-16 23:37:19 +00:00
|
|
|
|
{
|
|
|
|
|
tsw.Restart();
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var rr = await UserByIdAction(id, includes);
|
2018-12-16 23:37:19 +00:00
|
|
|
|
tsw.Stop();
|
|
|
|
|
timings.Add("UserByIdAction", tsw.ElapsedMilliseconds);
|
|
|
|
|
return rr;
|
|
|
|
|
}, ApplicationUser.CacheRegionUrn(id));
|
|
|
|
|
sw.Stop();
|
2019-06-30 22:14:36 +00:00
|
|
|
|
if (result?.Data != null) result.Data.Avatar = MakeUserThumbnailImage(id);
|
2018-12-16 23:37:19 +00:00
|
|
|
|
timings.Add("operation", sw.ElapsedMilliseconds);
|
2019-06-30 22:14:36 +00:00
|
|
|
|
Logger.LogDebug("ById Timings: id [{0}]", id);
|
2018-12-16 23:37:19 +00:00
|
|
|
|
return new OperationResult<User>(result.Messages)
|
|
|
|
|
{
|
|
|
|
|
Data = result?.Data,
|
|
|
|
|
Errors = result?.Errors,
|
|
|
|
|
IsNotFoundResult = result?.IsNotFoundResult ?? false,
|
|
|
|
|
IsSuccess = result?.IsSuccess ?? false,
|
|
|
|
|
OperationTime = sw.ElapsedMilliseconds
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-24 19:40:49 +00:00
|
|
|
|
public Task<Library.Models.Pagination.PagedResult<UserList>> List(PagedRequest request)
|
2018-11-15 15:10:29 +00:00
|
|
|
|
{
|
|
|
|
|
var sw = new Stopwatch();
|
|
|
|
|
sw.Start();
|
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var result = from u in DbContext.Users
|
|
|
|
|
let lastActivity = (from ut in DbContext.UserTracks
|
|
|
|
|
where ut.UserId == u.Id
|
|
|
|
|
select ut.LastPlayed).Max()
|
|
|
|
|
where request.FilterValue.Length == 0 ||
|
|
|
|
|
request.FilterValue.Length > 0 && u.UserName.Contains(request.FilterValue)
|
|
|
|
|
select new UserList
|
|
|
|
|
{
|
|
|
|
|
DatabaseId = u.Id,
|
|
|
|
|
Id = u.RoadieId,
|
|
|
|
|
User = new models.DataToken
|
|
|
|
|
{
|
|
|
|
|
Text = u.UserName,
|
|
|
|
|
Value = u.RoadieId.ToString()
|
|
|
|
|
},
|
|
|
|
|
IsEditor = u.UserRoles.Any(x => x.Role.Name == "Editor"),
|
|
|
|
|
IsPrivate = u.IsPrivate,
|
|
|
|
|
Thumbnail = MakeUserThumbnailImage(u.RoadieId),
|
|
|
|
|
CreatedDate = u.CreatedDate,
|
|
|
|
|
LastUpdated = u.LastUpdated,
|
|
|
|
|
RegisteredDate = u.RegisteredOn,
|
|
|
|
|
LastLoginDate = u.LastLogin,
|
|
|
|
|
LastApiAccessDate = u.LastApiAccess,
|
|
|
|
|
LastActivity = lastActivity
|
|
|
|
|
};
|
2018-11-15 15:10:29 +00:00
|
|
|
|
|
|
|
|
|
UserList[] rows = null;
|
|
|
|
|
var rowCount = result.Count();
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var sortBy = string.IsNullOrEmpty(request.Sort)
|
|
|
|
|
? request.OrderValue(new Dictionary<string, string> { { "User.Text", "ASC" } })
|
|
|
|
|
: request.OrderValue();
|
2018-11-15 15:10:29 +00:00
|
|
|
|
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
|
|
|
|
|
|
|
|
|
if (rows.Any())
|
|
|
|
|
foreach (var row in rows)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var userArtists = DbContext.UserArtists.Include(x => x.Artist)
|
|
|
|
|
.Where(x => x.UserId == row.DatabaseId).ToArray();
|
|
|
|
|
var userReleases = DbContext.UserReleases.Include(x => x.Release)
|
|
|
|
|
.Where(x => x.UserId == row.DatabaseId).ToArray();
|
|
|
|
|
var userTracks = DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == row.DatabaseId)
|
|
|
|
|
.ToArray();
|
2018-11-15 15:10:29 +00:00
|
|
|
|
|
|
|
|
|
row.Statistics = new UserStatistics
|
|
|
|
|
{
|
|
|
|
|
RatedArtists = userArtists.Where(x => x.Rating > 0).Count(),
|
|
|
|
|
FavoritedArtists = userArtists.Where(x => x.IsFavorite ?? false).Count(),
|
|
|
|
|
DislikedArtists = userArtists.Where(x => x.IsDisliked ?? false).Count(),
|
|
|
|
|
RatedReleases = userReleases.Where(x => x.Rating > 0).Count(),
|
|
|
|
|
FavoritedReleases = userReleases.Where(x => x.IsFavorite ?? false).Count(),
|
|
|
|
|
DislikedReleases = userReleases.Where(x => x.IsDisliked ?? false).Count(),
|
|
|
|
|
RatedTracks = userTracks.Where(x => x.Rating > 0).Count(),
|
|
|
|
|
PlayedTracks = userTracks.Where(x => x.PlayedCount.HasValue).Select(x => x.PlayedCount).Sum(),
|
|
|
|
|
FavoritedTracks = userTracks.Where(x => x.IsFavorite ?? false).Count(),
|
|
|
|
|
DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count()
|
|
|
|
|
};
|
|
|
|
|
}
|
2019-06-30 22:14:36 +00:00
|
|
|
|
|
2018-11-15 15:10:29 +00:00
|
|
|
|
sw.Stop();
|
2018-12-24 19:40:49 +00:00
|
|
|
|
return Task.FromResult(new Library.Models.Pagination.PagedResult<UserList>
|
2018-11-15 15:10:29 +00:00
|
|
|
|
{
|
|
|
|
|
TotalCount = rowCount,
|
|
|
|
|
CurrentPage = request.PageValue,
|
|
|
|
|
TotalPages = (int)Math.Ceiling((double)rowCount / request.LimitValue),
|
|
|
|
|
OperationTime = sw.ElapsedMilliseconds,
|
|
|
|
|
Rows = rows
|
2018-12-24 19:40:49 +00:00
|
|
|
|
});
|
2018-11-15 15:10:29 +00:00
|
|
|
|
}
|
2018-12-01 03:22:35 +00:00
|
|
|
|
|
2018-12-12 14:31:39 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetArtistBookmark(Guid artistId, User roadieUser, bool isBookmarked)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
var artist = GetArtist(artistId);
|
|
|
|
|
if (artist == null) return new OperationResult<bool>(true, $"Invalid Artist [{artistId}]");
|
|
|
|
|
var result = await SetBookmark(user, BookmarkType.Artist, artist.Id, isBookmarked);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(artist.CacheRegion);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetArtistDisliked(Guid artistId, User roadieUser, bool isDisliked)
|
2018-12-12 14:31:39 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
return await ToggleArtistDisliked(artistId, user, isDisliked);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetArtistFavorite(Guid artistId, User roadieUser, bool isFavorite)
|
2018-12-24 22:35:59 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
return await ToggleArtistFavorite(artistId, user, isFavorite);
|
2018-12-24 22:35:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-01 14:57:27 +00:00
|
|
|
|
public async Task<OperationResult<short>> SetArtistRating(Guid artistId, User roadieUser, short rating)
|
2018-12-01 03:22:35 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<short>(true, $"Invalid User [{roadieUser}]");
|
2018-12-01 03:22:35 +00:00
|
|
|
|
return await base.SetArtistRating(artistId, user, rating);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetCollectionBookmark(Guid collectionId, User roadieUser,
|
|
|
|
|
bool isBookmarked)
|
2018-12-01 03:22:35 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
var collection = GetCollection(collectionId);
|
|
|
|
|
if (collection == null) return new OperationResult<bool>(true, $"Invalid Collection [{collectionId}]");
|
|
|
|
|
var result = await SetBookmark(user, BookmarkType.Collection, collection.Id, isBookmarked);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(collection.CacheRegion);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
2018-12-01 03:22:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-12 14:31:39 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetLabelBookmark(Guid labelId, User roadieUser, bool isBookmarked)
|
2018-12-01 03:22:35 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
var label = GetLabel(labelId);
|
|
|
|
|
if (label == null) return new OperationResult<bool>(true, $"Invalid Label [{labelId}]");
|
|
|
|
|
var result = await SetBookmark(user, BookmarkType.Label, label.Id, isBookmarked);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(label.CacheRegion);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
2018-12-01 03:22:35 +00:00
|
|
|
|
}
|
2018-12-01 18:05:24 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetPlaylistBookmark(Guid playlistId, User roadieUser,
|
|
|
|
|
bool isBookmarked)
|
2018-12-01 18:05:24 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
var playlist = GetPlaylist(playlistId);
|
|
|
|
|
if (playlist == null) return new OperationResult<bool>(true, $"Invalid Playlist [{playlistId}]");
|
|
|
|
|
var result = await SetBookmark(user, BookmarkType.Playlist, playlist.Id, isBookmarked);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(playlist.CacheRegion);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<OperationResult<bool>> SetReleaseBookmark(Guid releaseid, User roadieUser, bool isBookmarked)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
var release = GetRelease(releaseid);
|
|
|
|
|
if (release == null) return new OperationResult<bool>(true, $"Invalid Release [{releaseid}]");
|
|
|
|
|
var result = await SetBookmark(user, BookmarkType.Release, release.Id, isBookmarked);
|
2018-12-01 18:05:24 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(release.CacheRegion);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
2018-12-01 18:05:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetReleaseDisliked(Guid releaseId, User roadieUser, bool isDisliked)
|
2019-01-05 22:40:33 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
return await ToggleReleaseDisliked(releaseId, user, isDisliked);
|
2019-01-05 22:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetReleaseFavorite(Guid releaseId, User roadieUser, bool isFavorite)
|
2018-12-24 22:35:59 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
return await ToggleReleaseFavorite(releaseId, user, isFavorite);
|
2018-12-24 22:35:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-12 14:31:39 +00:00
|
|
|
|
public async Task<OperationResult<short>> SetReleaseRating(Guid releaseId, User roadieUser, short rating)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<short>(true, $"Invalid User [{roadieUser}]");
|
2018-12-12 14:31:39 +00:00
|
|
|
|
return await base.SetReleaseRating(releaseId, user, rating);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<OperationResult<bool>> SetTrackBookmark(Guid trackId, User roadieUser, bool isBookmarked)
|
2018-12-11 23:09:52 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
var track = GetTrack(trackId);
|
|
|
|
|
if (track == null) return new OperationResult<bool>(true, $"Invalid Track [{trackId}]");
|
|
|
|
|
var result = await SetBookmark(user, BookmarkType.Track, track.Id, isBookmarked);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(track.CacheRegion);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetTrackDisliked(Guid trackId, User roadieUser, bool isDisliked)
|
2018-12-12 14:31:39 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
return await ToggleTrackDisliked(trackId, user, isDisliked);
|
2018-12-12 14:31:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<bool>> SetTrackFavorite(Guid trackId, User roadieUser, bool isFavorite)
|
2019-01-05 22:40:33 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"Invalid User [{roadieUser}]");
|
|
|
|
|
return await ToggleTrackFavorite(trackId, user, isFavorite);
|
2019-01-05 22:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<short>> SetTrackRating(Guid trackId, User roadieUser, short rating)
|
|
|
|
|
{
|
2019-03-10 16:55:21 +00:00
|
|
|
|
var timings = new Dictionary<string, long>();
|
2019-05-29 22:25:40 +00:00
|
|
|
|
var sw = Stopwatch.StartNew();
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(roadieUser.UserId);
|
2019-03-10 16:55:21 +00:00
|
|
|
|
sw.Stop();
|
|
|
|
|
timings.Add("GetUser", sw.ElapsedMilliseconds);
|
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
if (user == null) return new OperationResult<short>(true, $"Invalid User [{roadieUser}]");
|
2019-03-10 16:55:21 +00:00
|
|
|
|
sw.Start();
|
|
|
|
|
var result = await base.SetTrackRating(trackId, user, rating);
|
|
|
|
|
sw.Stop();
|
|
|
|
|
timings.Add("SetTrackRating", sw.ElapsedMilliseconds);
|
|
|
|
|
|
|
|
|
|
result.AdditionalData.Add("Timing", sw.ElapsedMilliseconds);
|
2019-06-30 22:14:36 +00:00
|
|
|
|
Logger.LogInformation(
|
|
|
|
|
$"User `{roadieUser}` set rating [{rating}] on TrackId [{trackId}]. Result [{JsonConvert.SerializeObject(result)}]");
|
2019-03-10 16:55:21 +00:00
|
|
|
|
return result;
|
2019-01-08 22:40:26 +00:00
|
|
|
|
}
|
2019-01-05 22:40:33 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
public async Task<OperationResult<bool>> UpdateIntegrationGrant(Guid userId, string integrationName,
|
|
|
|
|
string token)
|
2019-06-08 22:32:15 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userId);
|
|
|
|
|
if (user == null) return new OperationResult<bool>(true, $"User Not Found [{userId}]");
|
|
|
|
|
if (integrationName == "lastfm") return await UpdateLastFMSessionKey(user, token);
|
2019-06-08 22:32:15 +00:00
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
public async Task<OperationResult<bool>> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId);
|
2019-01-08 22:40:26 +00:00
|
|
|
|
if (user == null)
|
2019-06-30 22:14:36 +00:00
|
|
|
|
return new OperationResult<bool>(true,
|
|
|
|
|
string.Format("User Not Found [{0}]", userBeingUpdatedModel.UserId));
|
2019-01-08 22:40:26 +00:00
|
|
|
|
if (user.Id != userPerformingUpdate.Id && !userPerformingUpdate.IsAdmin)
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
Errors = new List<Exception> { new Exception("Access Denied") }
|
|
|
|
|
};
|
|
|
|
|
// Check concurrency stamp
|
|
|
|
|
if (user.ConcurrencyStamp != userBeingUpdatedModel.ConcurrencyStamp)
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
Errors = new List<Exception> { new Exception("User data is stale.") }
|
|
|
|
|
};
|
|
|
|
|
// Check that username (if changed) doesn't already exist
|
|
|
|
|
if (user.UserName != userBeingUpdatedModel.UserName)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var userByUsername = DbContext.Users.FirstOrDefault(x =>
|
|
|
|
|
x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper());
|
2019-01-08 22:40:26 +00:00
|
|
|
|
if (userByUsername != null)
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
Errors = new List<Exception> { new Exception("Username already in use") }
|
|
|
|
|
};
|
|
|
|
|
}
|
2019-06-30 22:14:36 +00:00
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
// Check that email (if changed) doesn't already exist
|
|
|
|
|
if (user.Email != userBeingUpdatedModel.Email)
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var userByEmail =
|
|
|
|
|
DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper());
|
2019-01-08 22:40:26 +00:00
|
|
|
|
if (userByEmail != null)
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
Errors = new List<Exception> { new Exception("Email already in use") }
|
|
|
|
|
};
|
|
|
|
|
}
|
2019-06-30 22:14:36 +00:00
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
user.UserName = userBeingUpdatedModel.UserName;
|
|
|
|
|
user.NormalizedUserName = userBeingUpdatedModel.UserName.ToUpper();
|
|
|
|
|
user.Email = userBeingUpdatedModel.Email;
|
|
|
|
|
user.NormalizedEmail = userBeingUpdatedModel.Email.ToUpper();
|
|
|
|
|
user.ApiToken = userBeingUpdatedModel.ApiToken;
|
|
|
|
|
user.Timezone = userBeingUpdatedModel.Timezone;
|
|
|
|
|
user.Timeformat = userBeingUpdatedModel.Timeformat;
|
|
|
|
|
user.PlayerTrackLimit = userBeingUpdatedModel.PlayerTrackLimit;
|
|
|
|
|
user.RandomReleaseLimit = userBeingUpdatedModel.RandomReleaseLimit;
|
|
|
|
|
user.RecentlyPlayedLimit = userBeingUpdatedModel.RecentlyPlayedLimit;
|
|
|
|
|
user.Profile = userBeingUpdatedModel.Profile;
|
|
|
|
|
user.DoUseHtmlPlayer = userBeingUpdatedModel.DoUseHtmlPlayer;
|
2019-01-27 05:05:43 +00:00
|
|
|
|
user.RemoveTrackFromQueAfterPlayed = userBeingUpdatedModel.RemoveTrackFromQueAfterPlayed;
|
2019-01-08 22:40:26 +00:00
|
|
|
|
user.IsPrivate = userBeingUpdatedModel.IsPrivate;
|
|
|
|
|
user.LastUpdated = DateTime.UtcNow;
|
|
|
|
|
user.FtpUrl = userBeingUpdatedModel.FtpUrl;
|
|
|
|
|
user.FtpDirectory = userBeingUpdatedModel.FtpDirectory;
|
|
|
|
|
user.FtpUsername = userBeingUpdatedModel.FtpUsername;
|
|
|
|
|
user.FtpPassword = EncryptionHelper.Encrypt(userBeingUpdatedModel.FtpPassword, user.RoadieId.ToString());
|
|
|
|
|
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(userBeingUpdatedModel.AvatarData))
|
|
|
|
|
{
|
|
|
|
|
var imageData = ImageHelper.ImageDataFromUrl(userBeingUpdatedModel.AvatarData);
|
|
|
|
|
if (imageData != null)
|
2019-06-30 22:14:36 +00:00
|
|
|
|
user.Avatar = ImageHelper.ResizeImage(imageData, Configuration.ThumbnailImageSize.Width,
|
|
|
|
|
Configuration.ThumbnailImageSize.Height);
|
2019-01-08 22:40:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
await DbContext.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(userBeingUpdatedModel.Password) &&
|
|
|
|
|
!string.IsNullOrEmpty(userBeingUpdatedModel.PasswordConfirmation))
|
2019-01-08 22:40:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (userBeingUpdatedModel.Password != userBeingUpdatedModel.PasswordConfirmation)
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
Errors = new List<Exception> { new Exception("Password does not match confirmation") }
|
|
|
|
|
};
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var resetToken = await UserManager.GeneratePasswordResetTokenAsync(user);
|
|
|
|
|
var identityResult =
|
|
|
|
|
await UserManager.ResetPasswordAsync(user, resetToken, userBeingUpdatedModel.Password);
|
2019-01-08 22:40:26 +00:00
|
|
|
|
if (!identityResult.Succeeded)
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
Errors = identityResult.Errors != null
|
|
|
|
|
? identityResult.Errors.Select(x =>
|
|
|
|
|
new Exception($"Code [{x.Code}], Description [{x.Description}]"))
|
|
|
|
|
: new List<Exception> { new Exception("Unable to reset password") }
|
2019-01-08 22:40:26 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(ApplicationUser.CacheRegionUrn(user.RoadieId));
|
2019-01-08 22:40:26 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
Logger.LogInformation($"User `{userPerformingUpdate}` modifed user `{userBeingUpdatedModel}`");
|
2019-01-08 22:40:26 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
|
|
|
|
}
|
2019-01-05 22:40:33 +00:00
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
private async Task<OperationResult<bool>> SetBookmark(ApplicationUser user, BookmarkType bookmarktype,
|
|
|
|
|
int bookmarkTargetId, bool isBookmarked)
|
2018-12-12 14:31:39 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var bookmark = DbContext.Bookmarks.FirstOrDefault(x => x.BookmarkTargetId == bookmarkTargetId &&
|
|
|
|
|
x.BookmarkType == bookmarktype &&
|
|
|
|
|
x.UserId == user.Id);
|
2019-01-05 22:40:33 +00:00
|
|
|
|
if (!isBookmarked)
|
2018-12-11 23:09:52 +00:00
|
|
|
|
{
|
|
|
|
|
// Remove bookmark
|
2018-12-12 14:31:39 +00:00
|
|
|
|
if (bookmark != null)
|
2018-12-11 23:09:52 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
DbContext.Bookmarks.Remove(bookmark);
|
|
|
|
|
await DbContext.SaveChangesAsync();
|
2018-12-11 23:09:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Add bookmark
|
2018-12-12 14:31:39 +00:00
|
|
|
|
if (bookmark == null)
|
2018-12-11 23:09:52 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
DbContext.Bookmarks.Add(new data.Bookmark
|
2018-12-11 23:09:52 +00:00
|
|
|
|
{
|
2018-12-12 14:31:39 +00:00
|
|
|
|
UserId = user.Id,
|
|
|
|
|
BookmarkTargetId = bookmarkTargetId,
|
|
|
|
|
BookmarkType = bookmarktype,
|
2018-12-11 23:09:52 +00:00
|
|
|
|
CreatedDate = DateTime.UtcNow,
|
2019-06-30 22:14:36 +00:00
|
|
|
|
Status = Statuses.Ok
|
2018-12-11 23:09:52 +00:00
|
|
|
|
});
|
2019-06-30 22:14:36 +00:00
|
|
|
|
await DbContext.SaveChangesAsync();
|
2018-12-11 23:09:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:14:36 +00:00
|
|
|
|
CacheManager.ClearRegion(user.CacheRegion);
|
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<OperationResult<bool>> UpdateLastFMSessionKey(ApplicationUser user, string token)
|
|
|
|
|
{
|
|
|
|
|
var lastFmSessionKeyResult = await LastFmHelper.GetSessionKeyForUserToken(token);
|
|
|
|
|
if (!lastFmSessionKeyResult.IsSuccess)
|
|
|
|
|
return new OperationResult<bool>(false, $"Unable to Get LastFM Session Key For Token [{token}]");
|
|
|
|
|
// Check concurrency stamp
|
|
|
|
|
if (user.ConcurrencyStamp != user.ConcurrencyStamp)
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
Errors = new List<Exception> { new Exception("User data is stale.") }
|
|
|
|
|
};
|
|
|
|
|
user.LastFMSessionKey = lastFmSessionKeyResult.Data;
|
|
|
|
|
user.LastUpdated = DateTime.UtcNow;
|
|
|
|
|
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
|
|
|
|
await DbContext.SaveChangesAsync();
|
|
|
|
|
|
|
|
|
|
CacheManager.ClearRegion(ApplicationUser.CacheRegionUrn(user.RoadieId));
|
|
|
|
|
|
|
|
|
|
Logger.LogInformation($"User `{user}` Updated LastFm SessionKey");
|
2018-12-11 23:09:52 +00:00
|
|
|
|
|
|
|
|
|
return new OperationResult<bool>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
|
|
|
|
Data = true
|
|
|
|
|
};
|
|
|
|
|
}
|
2019-01-08 22:40:26 +00:00
|
|
|
|
|
2019-01-10 23:40:04 +00:00
|
|
|
|
private Task<OperationResult<User>> UserByIdAction(Guid id, IEnumerable<string> includes)
|
2019-01-08 22:40:26 +00:00
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var user = GetUser(id);
|
2019-01-08 22:40:26 +00:00
|
|
|
|
if (user == null)
|
|
|
|
|
return Task.FromResult(new OperationResult<User>(true, string.Format("User Not Found [{0}]", id)));
|
2019-01-10 23:40:04 +00:00
|
|
|
|
var model = user.Adapt<User>();
|
|
|
|
|
if (includes != null && includes.Any())
|
|
|
|
|
if (includes.Contains("stats"))
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
var userArtists =
|
|
|
|
|
DbContext.UserArtists.Include(x => x.Artist).Where(x => x.UserId == user.Id).ToArray() ??
|
|
|
|
|
new data.UserArtist[0];
|
|
|
|
|
var userReleases =
|
|
|
|
|
DbContext.UserReleases.Include(x => x.Release).Where(x => x.UserId == user.Id).ToArray() ??
|
|
|
|
|
new data.UserRelease[0];
|
|
|
|
|
var userTracks =
|
|
|
|
|
DbContext.UserTracks.Include(x => x.Track).Where(x => x.UserId == user.Id).ToArray() ??
|
|
|
|
|
new data.UserTrack[0];
|
|
|
|
|
|
|
|
|
|
var mostPlayedArtist = (from a in DbContext.Artists
|
|
|
|
|
join r in DbContext.Releases on a.Id equals r.ArtistId
|
|
|
|
|
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
|
|
|
|
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
|
|
|
|
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
|
2019-01-10 23:40:04 +00:00
|
|
|
|
where ut.UserId == user.Id
|
|
|
|
|
select new { a, ut.PlayedCount })
|
2019-06-30 22:14:36 +00:00
|
|
|
|
.GroupBy(a => a.a)
|
|
|
|
|
.Select(x => new
|
|
|
|
|
{
|
|
|
|
|
Artist = x.Key,
|
|
|
|
|
Played = x.Sum(t => t.PlayedCount)
|
|
|
|
|
})
|
|
|
|
|
.OrderByDescending(x => x.Played)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
var mostPlayedReleaseId = (from r in DbContext.Releases
|
|
|
|
|
join rm in DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
|
|
|
|
join t in DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
|
|
|
|
join ut in DbContext.UserTracks on t.Id equals ut.TrackId
|
2019-01-12 21:10:00 +00:00
|
|
|
|
where ut.UserId == user.Id
|
|
|
|
|
select new { r, ut.PlayedCount })
|
2019-06-30 22:14:36 +00:00
|
|
|
|
.GroupBy(r => r.r)
|
|
|
|
|
.Select(x => new
|
|
|
|
|
{
|
|
|
|
|
Release = x.Key,
|
|
|
|
|
Played = x.Sum(t => t.PlayedCount)
|
|
|
|
|
})
|
|
|
|
|
.OrderByDescending(x => x.Played)
|
|
|
|
|
.Select(x => x.Release.RoadieId)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
var mostPlayedRelease = GetRelease(mostPlayedReleaseId);
|
2019-01-10 23:40:04 +00:00
|
|
|
|
|
|
|
|
|
var mostPlayedTrackUserTrack = userTracks
|
2019-06-30 22:14:36 +00:00
|
|
|
|
.OrderByDescending(x => x.PlayedCount)
|
|
|
|
|
.FirstOrDefault();
|
|
|
|
|
var mostPlayedTrack = mostPlayedTrackUserTrack == null
|
|
|
|
|
? null
|
|
|
|
|
: DbContext.Tracks
|
|
|
|
|
.Include(x => x.TrackArtist)
|
|
|
|
|
.Include(x => x.ReleaseMedia)
|
|
|
|
|
.Include("ReleaseMedia.Release")
|
|
|
|
|
.Include("ReleaseMedia.Release.Artist")
|
|
|
|
|
.FirstOrDefault(x => x.Id == mostPlayedTrackUserTrack.TrackId);
|
2019-01-10 23:40:04 +00:00
|
|
|
|
|
|
|
|
|
model.Statistics = new UserStatistics
|
|
|
|
|
{
|
2019-06-30 22:14:36 +00:00
|
|
|
|
MostPlayedArtist = mostPlayedArtist == null
|
|
|
|
|
? null
|
|
|
|
|
: models.ArtistList.FromDataArtist(mostPlayedArtist.Artist,
|
|
|
|
|
MakeArtistThumbnailImage(mostPlayedArtist.Artist.RoadieId)),
|
|
|
|
|
MostPlayedRelease = mostPlayedRelease == null
|
|
|
|
|
? null
|
|
|
|
|
: ReleaseList.FromDataRelease(mostPlayedRelease,
|
|
|
|
|
mostPlayedRelease.Artist,
|
|
|
|
|
HttpContext.BaseUrl,
|
|
|
|
|
MakeArtistThumbnailImage(mostPlayedRelease.Artist.RoadieId),
|
|
|
|
|
MakeReleaseThumbnailImage(mostPlayedRelease.RoadieId)),
|
|
|
|
|
MostPlayedTrack = mostPlayedTrack == null
|
|
|
|
|
? null
|
|
|
|
|
: models.TrackList.FromDataTrack(
|
|
|
|
|
MakeTrackPlayUrl(user, mostPlayedTrack.Id, mostPlayedTrack.RoadieId),
|
|
|
|
|
mostPlayedTrack,
|
|
|
|
|
mostPlayedTrack.ReleaseMedia.MediaNumber,
|
|
|
|
|
mostPlayedTrack.ReleaseMedia.Release,
|
|
|
|
|
mostPlayedTrack.ReleaseMedia.Release.Artist,
|
|
|
|
|
mostPlayedTrack.TrackArtist,
|
|
|
|
|
HttpContext.BaseUrl,
|
|
|
|
|
MakeTrackThumbnailImage(mostPlayedTrack.RoadieId),
|
|
|
|
|
MakeReleaseThumbnailImage(mostPlayedTrack.ReleaseMedia.Release.RoadieId),
|
|
|
|
|
MakeArtistThumbnailImage(mostPlayedTrack.ReleaseMedia.Release.Artist.RoadieId),
|
|
|
|
|
MakeArtistThumbnailImage(mostPlayedTrack.TrackArtist == null
|
|
|
|
|
? null
|
|
|
|
|
: (Guid?)mostPlayedTrack.TrackArtist.RoadieId)),
|
2019-01-10 23:40:04 +00:00
|
|
|
|
RatedArtists = userArtists.Where(x => x.Rating > 0).Count(),
|
|
|
|
|
FavoritedArtists = userArtists.Where(x => x.IsFavorite ?? false).Count(),
|
|
|
|
|
DislikedArtists = userArtists.Where(x => x.IsDisliked ?? false).Count(),
|
|
|
|
|
RatedReleases = userReleases.Where(x => x.Rating > 0).Count(),
|
|
|
|
|
FavoritedReleases = userReleases.Where(x => x.IsFavorite ?? false).Count(),
|
|
|
|
|
DislikedReleases = userReleases.Where(x => x.IsDisliked ?? false).Count(),
|
|
|
|
|
RatedTracks = userTracks.Where(x => x.Rating > 0).Count(),
|
|
|
|
|
PlayedTracks = userTracks.Where(x => x.PlayedCount.HasValue).Select(x => x.PlayedCount).Sum(),
|
|
|
|
|
FavoritedTracks = userTracks.Where(x => x.IsFavorite ?? false).Count(),
|
|
|
|
|
DislikedTracks = userTracks.Where(x => x.IsDisliked ?? false).Count()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-08 22:40:26 +00:00
|
|
|
|
return Task.FromResult(new OperationResult<User>
|
|
|
|
|
{
|
|
|
|
|
IsSuccess = true,
|
2019-01-10 23:40:04 +00:00
|
|
|
|
Data = model
|
2019-01-08 22:40:26 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
2018-11-15 15:10:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|