mirror of
https://github.com/sphildreth/roadie
synced 2024-11-22 04:03:10 +00:00
collection update
This commit is contained in:
parent
2334e0840f
commit
aea5afead7
9 changed files with 190 additions and 28 deletions
|
@ -114,5 +114,7 @@ namespace Roadie.Library.Data
|
|||
void UpdateRange(params object[] entities);
|
||||
|
||||
void UpdateRange(IEnumerable<object> entities);
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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" />
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
var token = HashHelper.MD5Hash((user.ApiToken ?? user.Email) + request.s);
|
||||
if (!token.Equals(request.t, StringComparison.OrdinalIgnoreCase))
|
||||
try
|
||||
{
|
||||
user = null;
|
||||
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 }]");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"Roadie.Api": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
"ASPNETCORE_ENVIRONMENT": "Production"
|
||||
},
|
||||
"applicationUrl": "http://localhost:5123/"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue