Profile edit work

This commit is contained in:
Steven Hildreth 2018-12-16 17:37:19 -06:00
parent 3728a27c6f
commit 711f98b419
14 changed files with 331 additions and 86 deletions

View file

@ -22,11 +22,11 @@ namespace Roadie.Api.Controllers
[AllowAnonymous]
public class AccountController : ControllerBase
{
private readonly IConfiguration configuration;
private readonly ILogger<AccountController> logger;
private readonly SignInManager<ApplicationUser> signInManager;
private readonly ITokenService tokenService;
private readonly UserManager<ApplicationUser> userManager;
private readonly IConfiguration Configuration;
private readonly ILogger<AccountController> Logger;
private readonly SignInManager<ApplicationUser> SignInManager;
private readonly ITokenService TokenService;
private readonly UserManager<ApplicationUser> UserManager;
private IRoadieSettings RoadieSettings { get; }
private ICacheManager CacheManager { get; }
private IAdminService AdminService { get; }
@ -40,11 +40,11 @@ namespace Roadie.Api.Controllers
ITokenService tokenService,
ICacheManager cacheManager)
{
this.userManager = userManager;
this.signInManager = signInManager;
this.configuration = configuration;
this.logger = logger;
this.tokenService = tokenService;
this.UserManager = userManager;
this.SignInManager = signInManager;
this.Configuration = configuration;
this.Logger = logger;
this.TokenService = tokenService;
this.CacheManager = cacheManager;
this.RoadieSettings = new RoadieSettings();
@ -61,19 +61,19 @@ namespace Roadie.Api.Controllers
try
{
// Login user
var loginResult = await signInManager.PasswordSignInAsync(model.Username, model.Password, isPersistent: false, lockoutOnFailure: false);
var loginResult = await SignInManager.PasswordSignInAsync(model.Username, model.Password, isPersistent: false, lockoutOnFailure: false);
if (!loginResult.Succeeded)
{
return BadRequest();
}
var user = await userManager.FindByNameAsync(model.Username);
var user = await UserManager.FindByNameAsync(model.Username);
var now = DateTime.UtcNow;
user.LastLogin = now;
user.LastApiAccess = now;
user.LastUpdated = now;
await userManager.UpdateAsync(user);
var t = await this.tokenService.GenerateToken(user, this.userManager);
this.logger.LogInformation($"Successfully authenticated User [{ model.Username}]");
await UserManager.UpdateAsync(user);
var t = await this.TokenService.GenerateToken(user, this.UserManager);
this.Logger.LogInformation($"Successfully authenticated User [{ model.Username}]");
this.CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
var avatarUrl = $"{this.Request.Scheme}://{this.Request.Host}/images/user/{ user.RoadieId }/{ this.RoadieSettings.ThumbnailImageSize.Width }/{ this.RoadieSettings.ThumbnailImageSize.Height }";
return Ok(new
@ -89,7 +89,7 @@ namespace Roadie.Api.Controllers
}
catch (Exception ex)
{
this.logger.LogError(ex, "Eror in CreateToken");
this.Logger.LogError(ex, "Eror in CreateToken");
return BadRequest();
}
}
@ -106,8 +106,8 @@ namespace Roadie.Api.Controllers
if (!String.IsNullOrWhiteSpace(username))
{
var user = await userManager.FindByNameAsync(username);
return Ok(await this.tokenService.GenerateToken(user, this.userManager));
var user = await UserManager.FindByNameAsync(username);
return Ok(await this.TokenService.GenerateToken(user, this.UserManager));
}
else
{
@ -126,19 +126,20 @@ namespace Roadie.Api.Controllers
var user = new ApplicationUser
{
UserName = registerModel.Username,
RegisteredOn = DateTime.UtcNow,
Email = registerModel.Email
};
var identityResult = await this.userManager.CreateAsync(user, registerModel.Password);
var identityResult = await this.UserManager.CreateAsync(user, registerModel.Password);
if (identityResult.Succeeded)
{
if(user.Id == 1)
{
await this.AdminService.DoInitialSetup(user, this.userManager);
await this.AdminService.DoInitialSetup(user, this.UserManager);
}
await signInManager.SignInAsync(user, isPersistent: false);
var t = await this.tokenService.GenerateToken(user, this.userManager);
this.logger.LogInformation($"Successfully authenticated User [{ registerModel.Username}]");
await SignInManager.SignInAsync(user, isPersistent: false);
var t = await this.TokenService.GenerateToken(user, this.UserManager);
this.Logger.LogInformation($"Successfully created and authenticated User [{ registerModel.Username}]");
this.CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
var avatarUrl = $"{this.Request.Scheme}://{this.Request.Host}/images/user/{ user.RoadieId }/{ this.RoadieSettings.ThumbnailImageSize.Width }/{ this.RoadieSettings.ThumbnailImageSize.Height }";
return Ok(new
@ -172,12 +173,12 @@ namespace Roadie.Api.Controllers
CreatedDate = DateTime.UtcNow
};
var identityResult = await this.userManager.ResetPasswordAsync(user, resetPasswordModel.Token, resetPasswordModel.Password);
var identityResult = await this.UserManager.ResetPasswordAsync(user, resetPasswordModel.Token, resetPasswordModel.Password);
if (identityResult.Succeeded)
{
this.CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
await signInManager.SignInAsync(user, isPersistent: false);
return Ok(this.tokenService.GenerateToken(user, this.userManager));
await SignInManager.SignInAsync(user, isPersistent: false);
return Ok(this.TokenService.GenerateToken(user, this.UserManager));
}
else
{

View file

@ -7,6 +7,7 @@ using Roadie.Api.Services;
using Roadie.Library.Caching;
using Roadie.Library.Identity;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users;
using System;
using System.Net;
using System.Net.Http;
@ -21,41 +22,73 @@ namespace Roadie.Api.Controllers
public class UserController : EntityControllerBase
{
private IUserService UserService { get; }
private readonly ITokenService TokenService;
public UserController(IUserService userService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, UserManager<ApplicationUser> userManager)
public UserController(IUserService userService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, ITokenService tokenService, UserManager<ApplicationUser> userManager)
: base(cacheManager, configuration, userManager)
{
this.Logger = logger.CreateLogger("RoadieApi.Controllers.LabelController");
this.Logger = logger.CreateLogger("RoadieApi.Controllers.UserController");
this.UserService = userService;
this.TokenService = tokenService;
}
//[EnableQuery]
//public IActionResult Get()
//{
// return Ok(this._RoadieDbContext.Labels.ProjectToType<models.Label>());
//}
[HttpGet("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
public async Task<IActionResult> Get(Guid id)
{
var user = await this.CurrentUserModel();
var result = await this.CacheManager.GetAsync($"urn:user_model_by_id:{ id }", async () =>
{
return await this.UserService.ById(user, id);
}, ControllerCacheRegionUrn);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
//[HttpGet("{id}")]
//[ProducesResponseType(200)]
//[ProducesResponseType(404)]
//public IActionResult Get(Guid id)
//{
// var key = id.ToString();
// var result = this._cacheManager.Get<models.Label>(key, () =>
// {
// var d = this._RoadieDbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
// if (d != null)
// {
// return d.Adapt<models.Label>();
// }
// return null;
// }, key);
// if (result == null)
// {
// return NotFound();
// }
// return Ok(result);
//}
[HttpPost("profile/edit")]
[ProducesResponseType(200)]
public async Task<IActionResult> UpdateProfile(User model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await this.CurrentUserModel();
var result = await this.UserService.UpdateProfile(user, model);
if (result == null || result.IsNotFoundResult)
{
return NotFound();
}
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
this.CacheManager.ClearRegion(ControllerCacheRegionUrn);
var modelUser = await UserManager.FindByNameAsync(model.UserName);
var t = await this.TokenService.GenerateToken(modelUser, this.UserManager);
this.CacheManager.ClearRegion(EntityControllerBase.ControllerCacheRegionUrn);
var avatarUrl = $"{this.Request.Scheme}://{this.Request.Host}/images/user/{ modelUser.RoadieId }/{ this.RoadieSettings.ThumbnailImageSize.Width }/{ this.RoadieSettings.ThumbnailImageSize.Height }";
return Ok(new
{
IsSuccess = true,
Username = modelUser.UserName,
modelUser.Email,
modelUser.LastLogin,
avatarUrl,
Token = t,
modelUser.Timeformat,
modelUser.Timezone
});
}
[HttpPost("setArtistRating/{releaseId}/{rating}")]

View file

@ -109,9 +109,9 @@ namespace Roadie.Api.ModelBinding
var model = new SubsonicRequest
{
AlbumCount = SafeParser.ToNumber<int?>(modelDictionary["albumCount"]) ?? 20,
AlbumCount = SafeParser.ToNumber<short?>(modelDictionary["albumCount"]) ?? 20,
AlbumOffset = SafeParser.ToNumber<int?>(modelDictionary["albumOffset"]),
ArtistCount = SafeParser.ToNumber<int?>(modelDictionary["artistCount"]) ?? 20,
ArtistCount = SafeParser.ToNumber<short?>(modelDictionary["artistCount"]) ?? 20,
ArtistName = SafeParser.ToString(modelDictionary["artist"]),
ArtistOffset = SafeParser.ToNumber<int?>(modelDictionary["artistOffset"]),
c = SafeParser.ToString(modelDictionary["c"]),
@ -127,8 +127,8 @@ namespace Roadie.Api.ModelBinding
p = SafeParser.ToString(modelDictionary["p"]),
Query = SafeParser.ToString(modelDictionary["query"]),
s = SafeParser.ToString(modelDictionary["s"]),
Size = SafeParser.ToNumber<int?>(modelDictionary["size"]),
SongCount = SafeParser.ToNumber<int?>(modelDictionary["songCount"]) ?? 20,
Size = SafeParser.ToNumber<short?>(modelDictionary["size"]),
SongCount = SafeParser.ToNumber<short?>(modelDictionary["songCount"]) ?? 20,
SongOffset = SafeParser.ToNumber<int?>(modelDictionary["songOffset"]),
t = SafeParser.ToString(modelDictionary["t"]),
ToYear = SafeParser.ToNumber<int?>(modelDictionary["toYear"]),

View file

@ -1,4 +1,5 @@
using Roadie.Library;
using Roadie.Library.Identity;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Users;
using System;
@ -8,6 +9,10 @@ namespace Roadie.Api.Services
{
public interface IUserService
{
Task<OperationResult<User>> ById(User user, Guid id);
Task<OperationResult<bool>> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel);
Task<PagedResult<UserList>> List(PagedRequest request);
Task<OperationResult<bool>> SetArtistBookmark(Guid artistId, User roadieUser, bool isBookmarked);
@ -31,5 +36,6 @@ namespace Roadie.Api.Services
Task<OperationResult<bool>> SetTrackBookmark(Guid trackId, User roadieUser, bool isBookmarked);
Task<OperationResult<short>> SetTrackRating(Guid trackId, User roadieUser, short rating);
}
}

View file

@ -172,7 +172,7 @@ namespace Roadie.Api.Services
{
BookmarkTargetId = track.Id,
BookmarkType = Library.Enums.BookmarkType.Track,
UserId = roadieUser.Id,
UserId = roadieUser.Id.Value,
Comment = comment,
Position = position
};
@ -1691,7 +1691,7 @@ namespace Roadie.Api.Services
}
var chatMessage = new data.ChatMessage
{
UserId = roadieUser.Id,
UserId = roadieUser.Id.Value,
Message = request.Message
};
this.DbContext.ChatMessages.Add(chatMessage);
@ -1817,7 +1817,7 @@ namespace Roadie.Api.Services
{
// Indexes for "Music" Artists alphabetically
pagedRequest.SkipValue = 0;
pagedRequest.Limit = int.MaxValue;
pagedRequest.Limit = short.MaxValue;
pagedRequest.Sort = "Artist.Text";
var artistList = await this.ArtistService.List(roadieUser: roadieUser,
request: pagedRequest,
@ -1898,7 +1898,7 @@ namespace Roadie.Api.Services
// Indexes for Artists alphabetically
var pagedRequest = request.PagedRequest;
pagedRequest.SkipValue = 0;
pagedRequest.Limit = int.MaxValue;
pagedRequest.Limit = short.MaxValue;
pagedRequest.Sort = "Artist.Text";
var artistList = await this.ArtistService.List(roadieUser: roadieUser,
request: pagedRequest,

View file

@ -29,11 +29,12 @@ namespace Roadie.Api.Services
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, utcNow.ToString())
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new Claim("roadie_id", user.RoadieId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, utcNow.ToString())
}.Union(userRoles);
var now = DateTime.UtcNow;

View file

@ -1,10 +1,12 @@
using Microsoft.EntityFrameworkCore;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Encoding;
using Roadie.Library.Identity;
using Roadie.Library.Imaging;
using Roadie.Library.Models;
using Roadie.Library.Models.Pagination;
using Roadie.Library.Models.Statistics;
@ -32,6 +34,142 @@ namespace Roadie.Api.Services
{
}
public async Task<OperationResult<User>> ById(User user, Guid id)
{
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);
var result = await this.CacheManager.GetAsync<OperationResult<User>>(cacheKey, async () =>
{
tsw.Restart();
var rr = await this.UserByIdAction(id);
tsw.Stop();
timings.Add("UserByIdAction", tsw.ElapsedMilliseconds);
return rr;
}, ApplicationUser.CacheRegionUrn(id));
sw.Stop();
if (result?.Data != null)
{
result.Data.Avatar = this.MakeUserThumbnailImage(id);
}
timings.Add("operation", sw.ElapsedMilliseconds);
this.Logger.LogDebug("ById Timings: id [{0}]", id);
return new OperationResult<User>(result.Messages)
{
Data = result?.Data,
Errors = result?.Errors,
IsNotFoundResult = result?.IsNotFoundResult ?? false,
IsSuccess = result?.IsSuccess ?? false,
OperationTime = sw.ElapsedMilliseconds
};
}
public async Task<OperationResult<bool>> UpdateProfile(User userPerformingUpdate, User userBeingUpdatedModel)
{
var user = this.DbContext.Users.FirstOrDefault(x => x.RoadieId == userBeingUpdatedModel.UserId);
if (user == null)
{
return new OperationResult<bool>(true, string.Format("User Not Found [{0}]", userBeingUpdatedModel.UserId));
}
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)
{
var userByUsername = this.DbContext.Users.FirstOrDefault(x => x.NormalizedUserName == userBeingUpdatedModel.UserName.ToUpper());
if (userByUsername != null)
{
return new OperationResult<bool>
{
Errors = new List<Exception> { new Exception("Username already in use") }
};
}
}
// Check that email (if changed) doesn't already exist
if (user.Email != userBeingUpdatedModel.Email)
{
var userByEmail = this.DbContext.Users.FirstOrDefault(x => x.NormalizedEmail == userBeingUpdatedModel.Email.ToUpper());
if (userByEmail != null)
{
return new OperationResult<bool>
{
Errors = new List<Exception> { new Exception("Email already in use") }
};
}
}
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;
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)
{
user.Avatar = ImageHelper.ResizeImage(imageData, this.Configuration.ThumbnailImageSize.Width, this.Configuration.ThumbnailImageSize.Height);
}
}
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(ApplicationUser.CacheRegionUrn(user.RoadieId));
this.Logger.LogInformation($"User `{ userPerformingUpdate }` modifed user `{ userBeingUpdatedModel }`");
return new OperationResult<bool>
{
IsSuccess = true,
Data = true
};
}
private async Task<OperationResult<User>> UserByIdAction(Guid id)
{
var user = this.GetUser(id);
if (user == null)
{
return new OperationResult<User>(true, string.Format("User Not Found [{0}]", id));
}
return new OperationResult<User>
{
IsSuccess = true,
Data = user.Adapt<User>()
};
}
public async Task<Library.Models.Pagination.PagedResult<UserList>> List(PagedRequest request)
{
var sw = new Stopwatch();

View file

@ -180,7 +180,7 @@ namespace Roadie.Library.Engines
sw.Stop();
if (artist == null || !artist.IsValid)
{
this.Logger.LogInformation("ArtistFactory: Artist Not Found By Name [{0}]", ArtistName);
this.Logger.LogInformation("ArtistLookupEngine: Artist Not Found By Name [{0}]", ArtistName);
if (doFindIfNotInDatabase)
{
OperationResult<Artist> ArtistSearch = null;

View file

@ -93,5 +93,19 @@ namespace Roadie.Library.Imaging
return outStream.ToArray();
}
}
public static byte[] ImageDataFromUrl(string imageUrl)
{
if (!string.IsNullOrEmpty(imageUrl))
{
var dataString = imageUrl.Trim().Replace('-', '+')
.Replace("data:image/jpeg;base64,", "")
.Replace("data:image/gif;base64,", "")
.Replace("data:image/png;base64,", "");
return Convert.FromBase64String(dataString);
}
return null;
}
}
}

View file

@ -30,8 +30,8 @@ namespace Roadie.Library.Models.Pagination
}
public string Sort { get; set; }
public string Order { get; set; }
public int? Limit { get; set; } = 10;
public int LimitValue
public short? Limit { get; set; } = 10;
public short LimitValue
{
get
{

View file

@ -12,7 +12,7 @@ namespace Roadie.Library.Models.Playlists
public DataToken Playlist { get; set; }
public DataToken User { get; set; }
public Image Thumbnail { get; set; }
public int? PlaylistCount { get; set; }
public short? PlaylistCount { get; set; }
public Image UserThumbnail { get; set; }
public bool IsPublic { get; set; }
public decimal? Duration { get; set; }

View file

@ -10,7 +10,7 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
{
public const string ArtistIdIdentifier = "A:";
public const string CollectionIdentifier = "C:";
public const int MaxPageSize = 100;
public const short MaxPageSize = 100;
public const string PlaylistdIdentifier = "P:";
public const string ReleaseIdIdentifier = "R:";
public const string TrackIdIdentifier = "T:";
@ -199,7 +199,7 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
/// <summary>
/// Maximum number of albums to return.
/// </summary>
public int? AlbumCount { get; set; }
public short? AlbumCount { get; set; }
/// <summary>
/// Search result offset for albums. Used for paging.
@ -209,7 +209,7 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
/// <summary>
/// Maximum number of artists to return.
/// </summary>
public int? ArtistCount { get; set; }
public short? ArtistCount { get; set; }
/// <summary>
/// Search result offset for artists. Used for paging.
@ -313,12 +313,12 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
/// <see>Various *Count properties depending on objects being searched and client version.</see>
/// <remark>Something this value is posted as 'count' versus 'size'</remark>
/// </summary>
public int? Size { get; set; }
public short? Size { get; set; }
/// <summary>
/// Maximum number of songs to return.
/// </summary>
public int? SongCount { get; set; }
public short? SongCount { get; set; }
/// <summary>
/// Search result offset for songs. Used for paging.

View file

@ -1,5 +1,6 @@
using Mapster;
using System;
using System.ComponentModel.DataAnnotations;
namespace Roadie.Library.Models.Users
{
@ -8,16 +9,67 @@ namespace Roadie.Library.Models.Users
{
public const string ActionKeyUserRated = "__userrated__";
public bool IsEditor { get; set; }
[MaxLength(100)]
public string ApiToken { get; set; }
public Image Avatar { get; set; }
/// <summary>
/// Posted image from a client of selected new base64 encoded avatar for the user
/// </summary>
public string AvatarData { get; set; }
public bool DoUseHtmlPlayer { get; set; }
[Required]
[MaxLength(100)]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[MaxLength(500)]
public string FtpDirectory { get; set; }
[MaxLength(500)]
public string FtpPassword { get; set; }
[MaxLength(250)]
public string FtpUrl { get; set; }
[MaxLength(50)]
public string FtpUsername { get; set; }
[Required]
[MaxLength(100)]
public string ConcurrencyStamp { get; set; }
public int? Id { get; set; }
public bool IsAdmin { get; set; }
public string UserName { get; set; }
public bool IsEditor { get; set; }
public bool IsPrivate { get; set; }
public short? PlayerTrackLimit { get; set; }
[MaxLength(65535)]
public string Profile { get; set; }
public short? RandomReleaseLimit { get; set; }
public short? RecentlyPlayedLimit { get; set; }
[StringLength(50)]
[Required]
public string Timeformat { get; set; }
[MaxLength(50)]
[Required]
public string Timezone { get; set; }
[AdaptMember("RoadieId")]
public Guid UserId { get; set; }
public int Id { get; set; }
public int? PlayerTrackLimit { get; set; }
public int? RecentlyPlayedLimit { get; set; }
public int? RandomReleaseLimit { get; set; }
public bool IsPrivate { get; set; }
[Required]
[MaxLength(20)]
public string UserName { get; set; }
public override string ToString()
{

View file

@ -102,7 +102,7 @@ namespace Roadie.Library.Processors
{ "processedFiles", processedFiles },
{ "newArtists", this.ArtistLookupEngine.AddedArtistIds.Count() },
{ "newReleases", this.ReleaseLookupEngine.AddedReleaseIds.Count() },
{ "newTracks", this.ReleaseLookupEngine.AddedTrackIds.Count() }
{ "newTracks", this.ReleaseFactory.AddedTrackIds.Count() }
},
OperationTime = sw.ElapsedMilliseconds
};