collection update

This commit is contained in:
Steven Hildreth 2019-01-18 17:00:44 -06:00
parent 2334e0840f
commit aea5afead7
9 changed files with 190 additions and 28 deletions

View file

@ -114,5 +114,7 @@ namespace Roadie.Library.Data
void UpdateRange(params object[] entities);
void UpdateRange(IEnumerable<object> entities);
}
}

View file

@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="CsvHelper" Version="12.1.1" />
<PackageReference Include="EFCore.BulkExtensions" Version="2.3.7" />
<PackageReference Include="FluentFTP" Version="19.2.2" />
<PackageReference Include="HtmlAgilityPack" Version="1.8.11" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" />

View file

@ -6,6 +6,7 @@ using Roadie.Api.Hubs;
using Roadie.Library;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Data;
using Roadie.Library.Encoding;
using Roadie.Library.Engines;
using Roadie.Library.Enums;
@ -24,6 +25,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using data = Roadie.Library.Data;
namespace Roadie.Api.Services
@ -292,6 +294,127 @@ namespace Roadie.Api.Services
};
}
public async Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = true)
{
var sw = new Stopwatch();
sw.Start();
var result = new List<PositionAristRelease>();
var errors = new List<Exception>();
var collection = this.DbContext.Collections.FirstOrDefault(x => x.RoadieId == collectionId);
if (collection == null)
{
await this.LogAndPublish($"ScanCollection Unknown Collection [{ collectionId}]", LogLevel.Warning);
return new OperationResult<bool>(true, $"Collection Not Found [{ collectionId }]");
}
try
{
if (doPurgeFirst)
{
var crs = this.DbContext.CollectionReleases.Where(x => x.CollectionId == collection.Id).ToArray();
this.DbContext.CollectionReleases.RemoveRange(crs);
await this.DbContext.SaveChangesAsync();
}
var par = collection.PositionArtistReleases();
if (par != null)
{
var now = DateTime.UtcNow;
var modifiedDb = false;
foreach (var csvRelease in par)
{
data.Release release = null;
CollectionRelease isInCollection = null;
OperationResult<data.Release> releaseResult = null;
data.Artist artist = null;
var artistResult = await this.ArtistLookupEngine.GetByName(new AudioMetaData { Artist = csvRelease.Artist });
if (!artistResult.IsSuccess)
{
this.Logger.LogWarning("Unable To Find Artist [{0}]", csvRelease.Artist);
csvRelease.Status = Library.Enums.Statuses.Missing;
}
else
{
artist = artistResult.Data;
}
if (artist != null)
{
releaseResult = await this.ReleaseLookupEngine.GetByName(artist, new AudioMetaData { Release = csvRelease.Release });
if (!releaseResult.IsSuccess)
{
this.Logger.LogWarning("Unable To Find Release [{0}]", csvRelease.Release);
csvRelease.Status = Library.Enums.Statuses.Missing;
}
}
if (releaseResult != null)
{
release = releaseResult.Data;
}
if (artist != null && release != null)
{
isInCollection = this.DbContext.CollectionReleases.FirstOrDefault(x => x.CollectionId == collection.Id && x.ListNumber == csvRelease.Position && x.ReleaseId == release.Id);
// Found in Database but not in collection add to Collection
if (isInCollection == null)
{
this.DbContext.CollectionReleases.Add(new CollectionRelease
{
CollectionId = collection.Id,
ReleaseId = release.Id,
ListNumber = csvRelease.Position,
});
modifiedDb = true;
}
// If Item in Collection is at different List number update CollectionRelease
else if (isInCollection.ListNumber != csvRelease.Position)
{
isInCollection.LastUpdated = now;
isInCollection.ListNumber = csvRelease.Position;
modifiedDb = true;
}
}
else
{
this.Logger.LogWarning("Unable To Find Artist Or Release For Collection Entry [{0}]", csvRelease.ToString());
result.Add(csvRelease);
}
}
if (modifiedDb)
{
collection.LastUpdated = now;
var dto = new Library.Models.Collections.CollectionList
{
CollectionCount = collection.CollectionCount,
CollectionFoundCount = (from cr in this.DbContext.CollectionReleases
where cr.CollectionId == collection.Id
select cr.CollectionId).Count()
};
if (dto.PercentComplete == 100)
{
// Lock so future scans dont happen, with DB RI when releases are deleted they are removed from collection
collection.IsLocked = true;
collection.Status = Statuses.Complete;
}
await this.DbContext.SaveChangesAsync();
this.CacheManager.ClearRegion(collection.CacheRegion);
}
}
}
catch (Exception ex)
{
this.Logger.LogError(ex);
errors.Add(ex);
}
sw.Stop();
this.Logger.LogInformation(string.Format("RescanCollection `{0}`, By User `{1}`", collection, user));
return new OperationResult<bool>
{
IsSuccess = !errors.Any(),
Data = true,
OperationTime = sw.ElapsedMilliseconds,
Errors = errors
};
}
public async Task<OperationResult<bool>> ScanInboundFolder(ApplicationUser user, bool isReadOnly = false)
{
var d = new DirectoryInfo(this.Configuration.InboundFolder);

View file

@ -23,5 +23,7 @@ namespace Roadie.Api.Services
Task<OperationResult<bool>> ScanLibraryFolder(ApplicationUser user, bool isReadOnly = false);
Task<OperationResult<bool>> ScanRelease(ApplicationUser user, Guid releaseId, bool isReadOnly = false);
Task<OperationResult<bool>> ScanCollection(ApplicationUser user, Guid collectionId, bool isReadOnly = false, bool doPurgeFirst = true);
}
}

View file

@ -177,7 +177,7 @@ namespace Roadie.Api.Services
join r in this.DbContext.Releases on cr.ReleaseId equals r.Id
where c.RoadieId == request.FilterToCollectionId.Value
orderby cr.ListNumber
select r.Id).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
select r.Id).ToArray();
}
int[] favoriteReleaseIds = new int[0];
if (request.FilterFavoriteOnly)
@ -334,10 +334,9 @@ namespace Roadie.Api.Services
var collectionReleases = (from c in this.DbContext.Collections
join cr in this.DbContext.CollectionReleases on c.Id equals cr.CollectionId
where c.RoadieId == request.FilterToCollectionId
where collectionReleaseIds.Contains(cr.ReleaseId)
orderby cr.ListNumber
select cr);
foreach (var par in collection.PositionArtistReleases().OrderBy(x => x.Index).Skip(request.SkipValue).Take(request.LimitValue))
var pars = collection.PositionArtistReleases().ToArray();
foreach (var par in pars)
{
var cr = collectionReleases.FirstOrDefault(x => x.ListNumber == par.Position);
// Release is known for Collection CSV, find newRow and update ListNumber
@ -350,12 +349,6 @@ namespace Roadie.Api.Services
{
parRelease.ListNumber = par.Position;
}
else
{
var anotherInstanceOfReleaseInCollection = parRelease.ShallowCopy();
anotherInstanceOfReleaseInCollection.ListNumber = par.Position;
newRows.Add(anotherInstanceOfReleaseInCollection);
}
}
}
// Release is not known add missing dummy release to rows
@ -379,7 +372,7 @@ namespace Roadie.Api.Services
}
}
// Resort the list for the collection by listNumber
rows = newRows.OrderBy(x => x.ListNumber).ToArray();
rows = newRows.OrderBy(x => x.ListNumber).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
rowCount = collection.CollectionCount;
}

View file

@ -112,27 +112,29 @@ namespace Roadie.Api.Services
}
try
{
var user = this.GetUser(request.u);
var user = this.DbContext.Users
.FirstOrDefault(x => x.UserName == request.u);
if (user == null)
{
this.Logger.LogInformation($"Unknown User [{ request.u }]");
return new subsonic.SubsonicOperationResult<subsonic.SubsonicAuthenticateResponse>(subsonic.ErrorCodes.WrongUsernameOrPassword, $"Unknown Username");
}
var password = request.Password;
var wasAuthenticatedAgainstPassword = false;
if (!string.IsNullOrEmpty(request.s))
{
try
{
var token = HashHelper.MD5Hash((user.ApiToken ?? user.Email) + request.s);
if (!token.Equals(request.t, StringComparison.OrdinalIgnoreCase))
{
user = null;
}
else
}
catch
{
wasAuthenticatedAgainstPassword = true;
}
}
else if (user != null && !string.IsNullOrEmpty(user.PasswordHash) && !string.IsNullOrEmpty(password))
if (user != null && !string.IsNullOrEmpty(user.PasswordHash) && !string.IsNullOrEmpty(password))
{
try
{
@ -141,24 +143,21 @@ namespace Roadie.Api.Services
{
user = null;
}
else
{
wasAuthenticatedAgainstPassword = true;
}
}
catch
{
}
}
if (wasAuthenticatedAgainstPassword)
if (user != null)
{
// Since API dont update LastLogin which likely invalidates any browser logins
user.LastApiAccess = DateTime.UtcNow;
var now = DateTime.UtcNow;
user.LastUpdated = now;
user.LastApiAccess = now;
await this.DbContext.SaveChangesAsync();
}
if (user == null)
{
this.Logger.LogInformation($"Unknown User [{ request.u }]");
this.Logger.LogInformation($"Invalid Credentials given for User [{ request.u }]");
return new subsonic.SubsonicOperationResult<subsonic.SubsonicAuthenticateResponse>(subsonic.ErrorCodes.WrongUsernameOrPassword, $"Unknown Username");
}
this.Logger.LogInformation($"Subsonic: Successfully Authenticated User [{ user.ToString() }] via Application [{ request.c }], Application Version [{ request.v }]");

View file

@ -84,6 +84,18 @@ namespace Roadie.Api.Controllers
return Ok(result);
}
[HttpPost("scan/collection/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> ScanCollection(Guid id)
{
var result = await this.AdminService.ScanCollection(await this.UserManager.GetUserAsync(User), id);
if (!result.IsSuccess)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
return Ok(result);
}
[HttpPost("delete/release/{id}")]
[ProducesResponseType(200)]
public async Task<IActionResult> DeleteRelease(Guid id, bool? doDeleteFiles)

View file

@ -6,6 +6,8 @@ using Microsoft.Extensions.Logging;
using Roadie.Api.Services;
using Roadie.Library.Caching;
using Roadie.Library.Identity;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace Roadie.Api.Controllers
@ -25,6 +27,34 @@ namespace Roadie.Api.Controllers
this.StatisticsService = statisticsService;
}
[HttpGet("ping")]
[ProducesResponseType(200)]
[AllowAnonymous]
public IActionResult Ping()
{
return Ok("pong");
}
[HttpGet("info")]
[ProducesResponseType(200)]
[AllowAnonymous]
public IActionResult Info()
{
var proc = Process.GetCurrentProcess();
var mem = proc.WorkingSet64;
var cpu = proc.TotalProcessorTime;
var messages = new List<string>();
messages.Add("▜ Memory Information: ");
messages.Add(string.Format("My process used working set {0:n3} K of working set and CPU {1:n} msec", mem / 1024.0, cpu.TotalMilliseconds));
foreach (var aProc in Process.GetProcesses())
{
messages.Add(string.Format("Proc {0,30} CPU {1,-20:n} msec", aProc.ProcessName, cpu.TotalMilliseconds));
}
messages.Add("▟ Memory Information: ");
return Ok(messages);
}
[HttpGet("library")]
[ProducesResponseType(200)]
public async Task<IActionResult> Library()

View file

@ -3,7 +3,7 @@
"Roadie.Api": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Production"
},
"applicationUrl": "http://localhost:5123/"
}