mirror of
https://github.com/sphildreth/roadie
synced 2024-11-22 12:13:10 +00:00
Work for collection and redo of timeinfo
This commit is contained in:
parent
6242831067
commit
e59a3195a8
24 changed files with 469 additions and 52 deletions
|
@ -7,8 +7,10 @@ using Roadie.Api.Services;
|
||||||
using Roadie.Library.Caching;
|
using Roadie.Library.Caching;
|
||||||
using Roadie.Library.Identity;
|
using Roadie.Library.Identity;
|
||||||
using Roadie.Library.Models.Pagination;
|
using Roadie.Library.Models.Pagination;
|
||||||
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using models = Roadie.Library.Models;
|
||||||
|
|
||||||
namespace Roadie.Api.Controllers
|
namespace Roadie.Api.Controllers
|
||||||
{
|
{
|
||||||
|
@ -27,33 +29,22 @@ namespace Roadie.Api.Controllers
|
||||||
this.CollectionService = collectionService;
|
this.CollectionService = collectionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
//[EnableQuery]
|
[HttpGet("{id}")]
|
||||||
//public IActionResult Get()
|
[ProducesResponseType(200)]
|
||||||
//{
|
[ProducesResponseType(404)]
|
||||||
// return Ok(this._RoadieDbContext.Labels.ProjectToType<models.Label>());
|
public async Task<IActionResult> Get(Guid id, string inc = null)
|
||||||
//}
|
{
|
||||||
|
var result = await this.CollectionService.ById(await this.CurrentUserModel(), id, (inc ?? models.Collections.Collection.DefaultIncludes).ToLower().Split(","));
|
||||||
//[HttpGet("{id}")]
|
if (result == null || result.IsNotFoundResult)
|
||||||
//[ProducesResponseType(200)]
|
{
|
||||||
//[ProducesResponseType(404)]
|
return NotFound();
|
||||||
//public IActionResult Get(Guid id)
|
}
|
||||||
//{
|
if (!result.IsSuccess)
|
||||||
// var key = id.ToString();
|
{
|
||||||
// var result = this._cacheManager.Get<models.Label>(key, () =>
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||||
// {
|
}
|
||||||
// var d = this._RoadieDbContext.Labels.FirstOrDefault(x => x.RoadieId == id);
|
return Ok(result);
|
||||||
// if (d != null)
|
}
|
||||||
// {
|
|
||||||
// return d.Adapt<models.Label>();
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
// }, key);
|
|
||||||
// if (result == null)
|
|
||||||
// {
|
|
||||||
// return NotFound();
|
|
||||||
// }
|
|
||||||
// return Ok(result);
|
|
||||||
//}
|
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
|
|
|
@ -206,7 +206,7 @@ namespace Roadie.Api.Services
|
||||||
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||||
where r.ArtistId == artist.Id
|
where r.ArtistId == artist.Id
|
||||||
select rm.Id).Count(),
|
select rm.Id).Count(),
|
||||||
TrackTime = validCartistTracks.Any() ? TimeSpan.FromSeconds(Math.Floor((double)trackTime / 1000)).ToString(@"dd\:hh\:mm\:ss") : "--:--",
|
TrackTime = validCartistTracks.Any() ? new TimeInfo((decimal)trackTime).ToFullFormattedString() : "--:--",
|
||||||
TrackCount = validCartistTracks.Count(),
|
TrackCount = validCartistTracks.Count(),
|
||||||
TrackPlayedCount = artist.PlayedCount
|
TrackPlayedCount = artist.PlayedCount
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,8 +24,7 @@ namespace Roadie.Api.Services
|
||||||
IHttpContext httpContext,
|
IHttpContext httpContext,
|
||||||
data.IRoadieDbContext context,
|
data.IRoadieDbContext context,
|
||||||
ICacheManager cacheManager,
|
ICacheManager cacheManager,
|
||||||
ILogger<BookmarkService> logger,
|
ILogger<BookmarkService> logger)
|
||||||
ICollectionService collectionService)
|
|
||||||
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Mapster;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Roadie.Library;
|
||||||
using Roadie.Library.Caching;
|
using Roadie.Library.Caching;
|
||||||
using Roadie.Library.Configuration;
|
using Roadie.Library.Configuration;
|
||||||
using Roadie.Library.Encoding;
|
using Roadie.Library.Encoding;
|
||||||
using Roadie.Library.Enums;
|
using Roadie.Library.Enums;
|
||||||
|
using Roadie.Library.Extensions;
|
||||||
using Roadie.Library.Models;
|
using Roadie.Library.Models;
|
||||||
using Roadie.Library.Models.Collections;
|
using Roadie.Library.Models.Collections;
|
||||||
using Roadie.Library.Models.Pagination;
|
using Roadie.Library.Models.Pagination;
|
||||||
|
using Roadie.Library.Models.Statistics;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
using Roadie.Library.Utility;
|
using Roadie.Library.Utility;
|
||||||
using System;
|
using System;
|
||||||
|
@ -21,16 +25,134 @@ namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
public class CollectionService : ServiceBase, ICollectionService
|
public class CollectionService : ServiceBase, ICollectionService
|
||||||
{
|
{
|
||||||
|
private IBookmarkService BookmarkService { get; } = null;
|
||||||
|
|
||||||
public CollectionService(IRoadieSettings configuration,
|
public CollectionService(IRoadieSettings configuration,
|
||||||
IHttpEncoder httpEncoder,
|
IHttpEncoder httpEncoder,
|
||||||
IHttpContext httpContext,
|
IHttpContext httpContext,
|
||||||
data.IRoadieDbContext dbContext,
|
data.IRoadieDbContext dbContext,
|
||||||
ICacheManager cacheManager,
|
ICacheManager cacheManager,
|
||||||
ILogger<CollectionService> logger)
|
ILogger<CollectionService> logger,
|
||||||
|
IBookmarkService bookmarkService)
|
||||||
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
|
: base(configuration, httpEncoder, dbContext, cacheManager, logger, httpContext)
|
||||||
{
|
{
|
||||||
|
this.BookmarkService = bookmarkService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<OperationResult<Collection>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null)
|
||||||
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
sw.Start();
|
||||||
|
var cacheKey = string.Format("urn:collection_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
|
||||||
|
var result = await this.CacheManager.GetAsync<OperationResult<Collection>>(cacheKey, async () =>
|
||||||
|
{
|
||||||
|
return await this.CollectionByIdAction(id, includes);
|
||||||
|
}, data.Artist.CacheRegionUrn(id));
|
||||||
|
sw.Stop();
|
||||||
|
if (result?.Data != null && roadieUser != null)
|
||||||
|
{
|
||||||
|
var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Collection);
|
||||||
|
if (userBookmarkResult.IsSuccess)
|
||||||
|
{
|
||||||
|
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x.Bookmark.Text == result.Data.Id.ToString()) != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new OperationResult<Collection>(result.Messages)
|
||||||
|
{
|
||||||
|
Data = result?.Data,
|
||||||
|
IsNotFoundResult = result?.IsNotFoundResult ?? false,
|
||||||
|
Errors = result?.Errors,
|
||||||
|
IsSuccess = result?.IsSuccess ?? false,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperationResult<Collection>> CollectionByIdAction(Guid id, IEnumerable<string> includes = null)
|
||||||
|
{
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
|
var collection = this.GetCollection(id);
|
||||||
|
|
||||||
|
if (collection == null)
|
||||||
|
{
|
||||||
|
return new OperationResult<Collection>(true, string.Format("Collection Not Found [{0}]", id));
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = collection.Adapt<Collection>();
|
||||||
|
result.AlternateNames = collection.AlternateNames;
|
||||||
|
result.Tags = collection.Tags;
|
||||||
|
result.URLs = collection.URLs;
|
||||||
|
result.Thumbnail = this.MakeCollectionThumbnailImage(collection.RoadieId);
|
||||||
|
result.MediumThumbnail = base.MakeThumbnailImage(id, "collection", this.Configuration.MediumImageSize.Width, this.Configuration.MediumImageSize.Height);
|
||||||
|
result.CollectionFoundCount = (from crc in this.DbContext.CollectionReleases
|
||||||
|
where crc.CollectionId == collection.Id
|
||||||
|
select crc.Id).Count();
|
||||||
|
if (includes != null && includes.Any())
|
||||||
|
{
|
||||||
|
if (includes.Contains("list"))
|
||||||
|
{
|
||||||
|
result.ListInCSVFormat = collection.ListInCSVFormat;
|
||||||
|
result.ListInCSV = collection.ListInCSV;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.ListInCSV = null;
|
||||||
|
result.ListInCSVFormat = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includes.Contains("releases"))
|
||||||
|
{
|
||||||
|
result.Releases = (from crc in this.DbContext.CollectionReleases
|
||||||
|
join r in this.DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id
|
||||||
|
where crc.CollectionId == collection.Id
|
||||||
|
orderby crc.ListNumber
|
||||||
|
select new CollectionRelease
|
||||||
|
{
|
||||||
|
ListNumber = crc.ListNumber,
|
||||||
|
Release = Library.Models.Releases.ReleaseList.FromDataRelease(r, r.Artist, this.HttpContext.BaseUrl, this.MakeArtistThumbnailImage(r.Artist.RoadieId), this.MakeReleaseThumbnailImage(r.RoadieId))
|
||||||
|
}).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includes.Contains("stats"))
|
||||||
|
{
|
||||||
|
var collectionReleases = (from crc in this.DbContext.CollectionReleases
|
||||||
|
join r in this.DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id
|
||||||
|
where crc.CollectionId == collection.Id
|
||||||
|
select r);
|
||||||
|
|
||||||
|
var collectionTracks = (from crc in this.DbContext.CollectionReleases
|
||||||
|
join r in this.DbContext.Releases.Include(x => x.Artist) on crc.ReleaseId equals r.Id
|
||||||
|
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
|
||||||
|
join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId
|
||||||
|
where crc.CollectionId == collection.Id
|
||||||
|
select t);
|
||||||
|
|
||||||
|
result.Statistics = new CollectionStatistics
|
||||||
|
{
|
||||||
|
FileSize = collectionTracks.Sum(x => (long?)x.FileSize).ToFileSize(),
|
||||||
|
MissingTrackCount = collectionTracks.Count(x => x.Hash != null),
|
||||||
|
ReleaseCount = collectionReleases.Count(),
|
||||||
|
ReleaseMediaCount = collectionReleases.Sum(x => x.MediaCount),
|
||||||
|
Duration = collectionReleases.Sum(x => (long?)x.Duration),
|
||||||
|
TrackCount = collectionReleases.Sum(x => x.TrackCount),
|
||||||
|
TrackPlayedCount = collectionReleases.Sum(x => x.PlayedCount)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
return new OperationResult<Collection>
|
||||||
|
{
|
||||||
|
Data = result,
|
||||||
|
IsSuccess = result != null,
|
||||||
|
OperationTime = sw.ElapsedMilliseconds
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<Library.Models.Pagination.PagedResult<CollectionList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null)
|
public async Task<Library.Models.Pagination.PagedResult<CollectionList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null)
|
||||||
{
|
{
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
using Roadie.Library.Models.Collections;
|
using Roadie.Library;
|
||||||
|
using Roadie.Library.Models.Collections;
|
||||||
using Roadie.Library.Models.Pagination;
|
using Roadie.Library.Models.Pagination;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Roadie.Api.Services
|
namespace Roadie.Api.Services
|
||||||
{
|
{
|
||||||
public interface ICollectionService
|
public interface ICollectionService
|
||||||
{
|
{
|
||||||
|
Task<OperationResult<Collection>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
|
||||||
|
|
||||||
Task<PagedResult<CollectionList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null);
|
Task<PagedResult<CollectionList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, Guid? releaseId = null, Guid? artistId = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -135,6 +135,7 @@ namespace Roadie.Api.Services
|
||||||
join r in this.DbContext.Releases on cr.ReleaseId equals r.Id
|
join r in this.DbContext.Releases on cr.ReleaseId equals r.Id
|
||||||
where c.RoadieId == request.FilterToCollectionId.Value
|
where c.RoadieId == request.FilterToCollectionId.Value
|
||||||
select r.Id).ToArray();
|
select r.Id).ToArray();
|
||||||
|
|
||||||
}
|
}
|
||||||
int[] favoriteReleaseIds = new int[0];
|
int[] favoriteReleaseIds = new int[0];
|
||||||
if (request.FilterFavoriteOnly)
|
if (request.FilterFavoriteOnly)
|
||||||
|
@ -271,6 +272,52 @@ namespace Roadie.Api.Services
|
||||||
release.Genre = genre?.dt ?? new DataToken();
|
release.Genre = genre?.dt ?? new DataToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.FilterToCollectionId.HasValue)
|
||||||
|
{
|
||||||
|
// Get and number the releases found for the Collection
|
||||||
|
var collectionReleases = (from c in this.DbContext.Collections
|
||||||
|
join cr in this.DbContext.CollectionReleases on c.Id equals cr.CollectionId
|
||||||
|
where collectionReleaseIds.Contains(cr.ReleaseId)
|
||||||
|
orderby cr.ListNumber
|
||||||
|
select cr);
|
||||||
|
foreach (var release in rows)
|
||||||
|
{
|
||||||
|
var cr = collectionReleases.FirstOrDefault(x => x.ReleaseId == release.DatabaseId);
|
||||||
|
if (cr != null)
|
||||||
|
{
|
||||||
|
release.ListNumber = cr.ListNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For the missing releases create a dummy release list item
|
||||||
|
var resultListNumbers = (from x in rows select x.ListNumber).ToList();
|
||||||
|
var collection = this.GetCollection(request.FilterToCollectionId.Value);
|
||||||
|
var missingReleases = (from par in collection.PositionArtistReleases()
|
||||||
|
where !resultListNumbers.Contains(par.Position)
|
||||||
|
select par);
|
||||||
|
var newRows = new List<ReleaseList>(rows);
|
||||||
|
foreach(var missingRelease in missingReleases)
|
||||||
|
{
|
||||||
|
newRows.Add(new ReleaseList
|
||||||
|
{
|
||||||
|
Artist = new DataToken
|
||||||
|
{
|
||||||
|
Text = missingRelease.Artist
|
||||||
|
},
|
||||||
|
Release = new DataToken
|
||||||
|
{
|
||||||
|
Text = missingRelease.Release
|
||||||
|
},
|
||||||
|
CssClass = "missing",
|
||||||
|
ArtistThumbnail = new Image($"{this.HttpContext.ImageBaseUrl }/unknown.jpg"),
|
||||||
|
Thumbnail = new Image($"{this.HttpContext.ImageBaseUrl }/unknown.jpg"),
|
||||||
|
ListNumber = missingRelease.Position
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Resort the list for the collection by listNumber
|
||||||
|
rows = newRows.OrderBy(x => x.ListNumber).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
||||||
|
rowCount = newRows.Count();
|
||||||
|
}
|
||||||
|
|
||||||
if (roadieUser != null)
|
if (roadieUser != null)
|
||||||
{
|
{
|
||||||
var userReleaseRatings = (from ur in this.DbContext.UserReleases
|
var userReleaseRatings = (from ur in this.DbContext.UserReleases
|
||||||
|
@ -507,7 +554,7 @@ namespace Roadie.Api.Services
|
||||||
TrackCount = release.TrackCount,
|
TrackCount = release.TrackCount,
|
||||||
TrackPlayedCount = release.PlayedCount,
|
TrackPlayedCount = release.PlayedCount,
|
||||||
TrackSize = releaseTracks.Sum(x => (long?)x.size).ToFileSize(),
|
TrackSize = releaseTracks.Sum(x => (long?)x.size).ToFileSize(),
|
||||||
TrackTime = releaseTracks.Any() ? TimeSpan.FromSeconds(Math.Floor((double)releaseTime / 1000)).ToString(@"hh\:mm\:ss") : "--:--"
|
TrackTime = releaseTracks.Any() ? new TimeInfo((decimal)releaseTime).ToFullFormattedString() : "--:--"
|
||||||
};
|
};
|
||||||
result.MaxMediaNumber = releaseMedias.Max(x => x.MediaNumber);
|
result.MaxMediaNumber = releaseMedias.Max(x => x.MediaNumber);
|
||||||
result.Statistics = releaseStats;
|
result.Statistics = releaseStats;
|
||||||
|
|
|
@ -518,6 +518,7 @@ namespace Roadie.Api.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Image MakeImage(Guid id, string type, int? width, int? height, string caption = null)
|
private Image MakeImage(Guid id, string type, int? width, int? height, string caption = null)
|
||||||
{
|
{
|
||||||
if (width.HasValue && height.HasValue && (width.Value != this.Configuration.ThumbnailImageSize.Width || height.Value != this.Configuration.ThumbnailImageSize.Height))
|
if (width.HasValue && height.HasValue && (width.Value != this.Configuration.ThumbnailImageSize.Width || height.Value != this.Configuration.ThumbnailImageSize.Height))
|
||||||
|
@ -526,5 +527,7 @@ namespace Roadie.Api.Services
|
||||||
}
|
}
|
||||||
return new Image($"{this.HttpContext.ImageBaseUrl }/{type}/{id}", caption, null);
|
return new Image($"{this.HttpContext.ImageBaseUrl }/{type}/{id}", caption, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -150,9 +150,9 @@ namespace Roadie.Api.Services
|
||||||
FavoriteCount = userTracks.Count(x => x.IsFavorite ?? false),
|
FavoriteCount = userTracks.Count(x => x.IsFavorite ?? false),
|
||||||
PlayedCount = userTracks.Sum(x => x.PlayedCount),
|
PlayedCount = userTracks.Sum(x => x.PlayedCount),
|
||||||
FileSizeFormatted = ((long?)track.FileSize).ToFileSize(),
|
FileSizeFormatted = ((long?)track.FileSize).ToFileSize(),
|
||||||
Time = TimeSpan.FromSeconds(Math.Floor((double)track.Duration / 1000)).ToString(@"hh\:mm\:ss")
|
Time = new TimeInfo((decimal)track.Duration).ToFullFormattedString()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,9 @@ namespace Roadie.Api
|
||||||
// c.SwaggerEndpoint("/swagger/swagger.json", "Roadie API");
|
// c.SwaggerEndpoint("/swagger/swagger.json", "Roadie API");
|
||||||
// c.RoutePrefix = string.Empty;
|
// c.RoutePrefix = string.Empty;
|
||||||
//});
|
//});
|
||||||
|
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseSignalR(routes =>
|
app.UseSignalR(routes =>
|
||||||
{
|
{
|
||||||
routes.MapHub<PlayActivityHub>("/playActivityHub");
|
routes.MapHub<PlayActivityHub>("/playActivityHub");
|
||||||
|
|
BIN
RoadieApi/wwwroot/Images/unknown.jpg
Normal file
BIN
RoadieApi/wwwroot/Images/unknown.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -1,4 +1,5 @@
|
||||||
using Roadie.Library.Enums;
|
using Roadie.Library.Enums;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
using System;
|
using CsvHelper;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Roadie.Library.Enums;
|
||||||
|
using Roadie.Library.Utility;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Roadie.Library.Data
|
namespace Roadie.Library.Data
|
||||||
|
@ -23,5 +28,129 @@ namespace Roadie.Library.Data
|
||||||
return Collection.CacheUrn(this.RoadieId);
|
return Collection.CacheUrn(this.RoadieId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int? _positionColumn = null;
|
||||||
|
public int PositionColumn
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this._positionColumn == null)
|
||||||
|
{
|
||||||
|
var looper = -1;
|
||||||
|
foreach (var pos in this.ListInCSVFormat.Split(','))
|
||||||
|
{
|
||||||
|
looper++;
|
||||||
|
if (pos.ToLower().Equals("position"))
|
||||||
|
{
|
||||||
|
this._positionColumn = looper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._positionColumn.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? _artistColumn = null;
|
||||||
|
public int ArtistColumn
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this._artistColumn == null)
|
||||||
|
{
|
||||||
|
var looper = -1;
|
||||||
|
foreach (var pos in this.ListInCSVFormat.Split(','))
|
||||||
|
{
|
||||||
|
looper++;
|
||||||
|
if (pos.ToLower().Equals("artist"))
|
||||||
|
{
|
||||||
|
this._artistColumn = looper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._artistColumn.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? _releaseColumn = null;
|
||||||
|
public int ReleaseColumn
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this._releaseColumn == null)
|
||||||
|
{
|
||||||
|
var looper = -1;
|
||||||
|
foreach (var pos in this.ListInCSVFormat.Split(','))
|
||||||
|
{
|
||||||
|
looper++;
|
||||||
|
if (pos.ToLower().Equals("release"))
|
||||||
|
{
|
||||||
|
this._releaseColumn = looper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._releaseColumn.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private IEnumerable<PositionAristRelease> _positionAristReleases = null;
|
||||||
|
|
||||||
|
public IEnumerable<PositionAristRelease> PositionArtistReleases()
|
||||||
|
{
|
||||||
|
if (this._positionAristReleases == null)
|
||||||
|
{
|
||||||
|
var rows = new List<PositionAristRelease>();
|
||||||
|
using (var sr = new StringReader(this.ListInCSV))
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var csv = new CsvReader(sr, new CsvHelper.Configuration.Configuration { MissingFieldFound = null, HasHeaderRecord = false });
|
||||||
|
while (csv.Read())
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
rows.Add(new PositionAristRelease
|
||||||
|
{
|
||||||
|
Index = index,
|
||||||
|
Position = csv.GetField<int>(this.PositionColumn),
|
||||||
|
Artist = SafeParser.ToString(csv.GetField<string>(this.ArtistColumn)),
|
||||||
|
Release = SafeParser.ToString(csv.GetField<string>(this.ReleaseColumn)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._positionAristReleases = rows;
|
||||||
|
}
|
||||||
|
return this._positionAristReleases;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class PositionAristRelease
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public Statuses Status { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("Status")]
|
||||||
|
public string StatusVerbose
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.Status.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// This is the index (position in the list regardless of the position number)
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public int Index { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// This is the position number for the list (can be a year "1984" can be a number "14")
|
||||||
|
/// </summary>
|
||||||
|
public int Position { get; set; }
|
||||||
|
public string Artist { get; set; }
|
||||||
|
public string Release { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("Position [{0}], Artist [{1}], Release [{2}]", this.Position, this.Artist, this.Release);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
RoadieLibrary/Extensions/DecimalExt.cs
Normal file
19
RoadieLibrary/Extensions/DecimalExt.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using Roadie.Library.Utility;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Roadie.Library.Extensions
|
||||||
|
{
|
||||||
|
public static class DecimalExt
|
||||||
|
{
|
||||||
|
public static int ToSecondsFromMilliseconds(this decimal? value)
|
||||||
|
{
|
||||||
|
if (value > 0)
|
||||||
|
{
|
||||||
|
return (int)new TimeInfo(value.Value).Seconds;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
RoadieLibrary/Extensions/TimeSpanExt.cs
Normal file
18
RoadieLibrary/Extensions/TimeSpanExt.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Roadie.Library.Extensions
|
||||||
|
{
|
||||||
|
public static class TimeSpanExt
|
||||||
|
{
|
||||||
|
public static string ToDuration(this TimeSpan input)
|
||||||
|
{
|
||||||
|
if(input == null || input.TotalMilliseconds == 0)
|
||||||
|
{
|
||||||
|
return "--/--/--";
|
||||||
|
}
|
||||||
|
return input.ToString(@"ddd\.hh\:mm\:ss");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Roadie.Library.Models.Statistics;
|
||||||
|
|
||||||
namespace Roadie.Library.Models.Collections
|
namespace Roadie.Library.Models.Collections
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class Collection : EntityModelBase
|
public class Collection : EntityModelBase
|
||||||
{
|
{
|
||||||
|
public const string DefaultIncludes = "stats";
|
||||||
|
|
||||||
public int CollectionCount { get; set; }
|
public int CollectionCount { get; set; }
|
||||||
|
|
||||||
public string CollectionType { get; set; }
|
public string CollectionType { get; set; }
|
||||||
|
@ -17,10 +20,44 @@ namespace Roadie.Library.Models.Collections
|
||||||
[MaxLength(200)]
|
[MaxLength(200)]
|
||||||
public string Edition { get; set; }
|
public string Edition { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
public DataToken Maintainer { get; set; }
|
public DataToken Maintainer { get; set; }
|
||||||
|
|
||||||
public IEnumerable<CollectionRelease> Releases { get; set; }
|
public IEnumerable<CollectionRelease> Releases { get; set; }
|
||||||
|
|
||||||
public Image Thumbnail { get; set; }
|
public Image Thumbnail { get; set; }
|
||||||
|
public Image MediumThumbnail { get; set; }
|
||||||
|
public string ListInCSVFormat { get; set; }
|
||||||
|
public string ListInCSV { get; set; }
|
||||||
|
|
||||||
|
public int PercentComplete
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.CollectionCount == 0 || this.CollectionFoundCount == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (int)Math.Floor((decimal)this.CollectionFoundCount / (decimal)this.CollectionCount * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? MissingReleaseCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(this.CollectionCount == this.CollectionFoundCount)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.CollectionCount - this.CollectionFoundCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? CollectionFoundCount { get; set; }
|
||||||
|
public CollectionStatistics Statistics { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ namespace Roadie.Library.Models.Collections
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class CollectionRelease
|
public class CollectionRelease
|
||||||
{
|
{
|
||||||
public Release Release { get; set; }
|
public ReleaseList Release { get; set; }
|
||||||
public int ListNumber { get; set; }
|
public int ListNumber { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Roadie.Library.Models.Statistics;
|
using Roadie.Library.Models.Statistics;
|
||||||
|
using Roadie.Library.Utility;
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ namespace Roadie.Library.Models
|
||||||
|
|
||||||
public ReleaseGroupingStatistics Statistics { get; set; }
|
public ReleaseGroupingStatistics Statistics { get; set; }
|
||||||
|
|
||||||
public int? Duration { get; set; }
|
public decimal? Duration { get; set; }
|
||||||
public string DurationTime
|
public string DurationTime
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -41,7 +42,7 @@ namespace Roadie.Library.Models
|
||||||
{
|
{
|
||||||
return "--:--";
|
return "--:--";
|
||||||
}
|
}
|
||||||
return TimeSpan.FromSeconds(this.Duration.Value / 1000).ToString(@"dd\.hh\:mm\:ss");
|
return new TimeInfo(this.Duration.Value).ToFullFormattedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using Roadie.Library.Models.Statistics;
|
using Roadie.Library.Models.Statistics;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
|
using Roadie.Library.Utility;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -28,7 +29,7 @@ namespace Roadie.Library.Models.Playlists
|
||||||
public Image MediumThumbnail { get; set; }
|
public Image MediumThumbnail { get; set; }
|
||||||
public short? TrackCount { get; set; }
|
public short? TrackCount { get; set; }
|
||||||
public short? ReleaseCount { get; set; }
|
public short? ReleaseCount { get; set; }
|
||||||
public int? Duration { get; set; }
|
public decimal? Duration { get; set; }
|
||||||
public string DurationTime
|
public string DurationTime
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -37,7 +38,7 @@ namespace Roadie.Library.Models.Playlists
|
||||||
{
|
{
|
||||||
return "--:--";
|
return "--:--";
|
||||||
}
|
}
|
||||||
return TimeSpan.FromSeconds(this.Duration.Value / 1000).ToString(@"dd\.hh\:mm\:ss");
|
return new TimeInfo(this.Duration.Value).ToFullFormattedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using Roadie.Library.Utility;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ namespace Roadie.Library.Models.Playlists
|
||||||
public int? PlaylistCount { get; set; }
|
public int? PlaylistCount { get; set; }
|
||||||
public Image UserThumbnail { get; set; }
|
public Image UserThumbnail { get; set; }
|
||||||
public bool IsPublic { get; set; }
|
public bool IsPublic { get; set; }
|
||||||
public int? Duration { get; set; }
|
public decimal? Duration { get; set; }
|
||||||
public string DurationTime
|
public string DurationTime
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -22,7 +23,7 @@ namespace Roadie.Library.Models.Playlists
|
||||||
{
|
{
|
||||||
return "--:--";
|
return "--:--";
|
||||||
}
|
}
|
||||||
return TimeSpan.FromSeconds(this.Duration.Value / 1000).ToString(@"hh\:mm\:ss");
|
return new TimeInfo(this.Duration.Value).ToFullFormattedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Newtonsoft.Json;
|
||||||
using Roadie.Library.Enums;
|
using Roadie.Library.Enums;
|
||||||
using Roadie.Library.Extensions;
|
using Roadie.Library.Extensions;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
|
using Roadie.Library.Utility;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -47,7 +48,7 @@ namespace Roadie.Library.Models.Releases
|
||||||
public Statuses? Status { get; set; }
|
public Statuses? Status { get; set; }
|
||||||
public DataToken Genre { get; set; }
|
public DataToken Genre { get; set; }
|
||||||
public DateTime? LastPlayed { get; set; }
|
public DateTime? LastPlayed { get; set; }
|
||||||
public int? Duration { get; set; }
|
public decimal? Duration { get; set; }
|
||||||
public string DurationTime
|
public string DurationTime
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -56,12 +57,25 @@ namespace Roadie.Library.Models.Releases
|
||||||
{
|
{
|
||||||
return "--:--";
|
return "--:--";
|
||||||
}
|
}
|
||||||
return TimeSpan.FromSeconds(this.Duration.Value / 1000).ToString(@"hh\:mm\:ss");
|
return new TimeInfo(this.Duration.Value).ToFullFormattedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public int? MediaCount { get; set; }
|
public int? MediaCount { get; set; }
|
||||||
|
|
||||||
|
public bool IsValid
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.Id != Guid.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is populated on a request to get the list of releases for a collection.
|
||||||
|
/// </summary>
|
||||||
|
public int? ListNumber { get; set; }
|
||||||
|
|
||||||
public static ReleaseList FromDataRelease(Data.Release release, Data.Artist artist, string baseUrl, Image artistThumbnail, Image thumbnail)
|
public static ReleaseList FromDataRelease(Data.Release release, Data.Artist artist, string baseUrl, Image artistThumbnail, Image thumbnail)
|
||||||
{
|
{
|
||||||
return new ReleaseList
|
return new ReleaseList
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using Roadie.Library.Utility;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Roadie.Library.Models.Statistics
|
namespace Roadie.Library.Models.Statistics
|
||||||
{
|
{
|
||||||
|
@ -13,5 +14,19 @@ namespace Roadie.Library.Models.Statistics
|
||||||
public int? TrackCount { get; set; }
|
public int? TrackCount { get; set; }
|
||||||
public int? TrackPlayedCount { get; set; }
|
public int? TrackPlayedCount { get; set; }
|
||||||
public string TrackTime { get; set; }
|
public string TrackTime { get; set; }
|
||||||
|
public decimal? Duration { get; set; }
|
||||||
|
|
||||||
|
public string DurationTime
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!this.Duration.HasValue)
|
||||||
|
{
|
||||||
|
return "--:--";
|
||||||
|
}
|
||||||
|
return new TimeInfo(this.Duration.Value).ToFullFormattedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Roadie.Library.Models.Releases;
|
using Roadie.Library.Models.Releases;
|
||||||
using Roadie.Library.Models.Users;
|
using Roadie.Library.Models.Users;
|
||||||
|
using Roadie.Library.Utility;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
@ -24,14 +25,14 @@ namespace Roadie.Library.Models
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return this.Duration.HasValue ? TimeSpan.FromSeconds(this.Duration.Value / 1000).ToString(@"hh\:mm\:ss") : "--:--";
|
return this.Duration.HasValue ? new TimeInfo((decimal)this.Duration.Value).ToFullFormattedString() : "--:--";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string DurationTimeShort
|
public string DurationTimeShort
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return this.Duration.HasValue ? TimeSpan.FromSeconds(this.Duration.Value / 1000).ToString(@"mm\:ss") : "--:--";
|
return this.Duration.HasValue ? new TimeInfo((decimal)this.Duration.Value).ToShortFormattedString() : "--:--";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public DateTime? LastPlayed { get; set; }
|
public DateTime? LastPlayed { get; set; }
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CsvHelper" Version="12.0.1" />
|
||||||
<PackageReference Include="FluentFTP" Version="19.2.2" />
|
<PackageReference Include="FluentFTP" Version="19.2.2" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.8.10" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.8.10" />
|
||||||
<PackageReference Include="ID3" Version="0.5.0-beta.1" />
|
<PackageReference Include="ID3" Version="0.5.0-beta.1" />
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace Roadie.Library.Utility
|
||||||
|
|
||||||
public decimal Hours { get; set; }
|
public decimal Hours { get; set; }
|
||||||
|
|
||||||
public string HoursForamtted
|
public string HoursFormatted
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -77,6 +77,7 @@ namespace Roadie.Library.Utility
|
||||||
|
|
||||||
public TimeInfo(decimal milliseconds)
|
public TimeInfo(decimal milliseconds)
|
||||||
{
|
{
|
||||||
|
|
||||||
var secondsTotal = milliseconds / 1000;
|
var secondsTotal = milliseconds / 1000;
|
||||||
var minutesTotal = Math.Floor(secondsTotal / 60);
|
var minutesTotal = Math.Floor(secondsTotal / 60);
|
||||||
var hoursTotal = Math.Floor(minutesTotal / 60);
|
var hoursTotal = Math.Floor(minutesTotal / 60);
|
||||||
|
@ -96,9 +97,18 @@ namespace Roadie.Library.Utility
|
||||||
return $"{ (string.IsNullOrEmpty(yearsFormatted) ? string.Empty : yearsFormatted + ":") }{ this.ToString() }";
|
return $"{ (string.IsNullOrEmpty(yearsFormatted) ? string.Empty : yearsFormatted + ":") }{ this.ToString() }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ToShortFormattedString()
|
||||||
|
{
|
||||||
|
return $"{ MinutesFormatted }:{ SecondsFormatted }";
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"{ DaysFormatted}:{ HoursForamtted}:{ MinutesFormatted }";
|
if(this.Days > 0)
|
||||||
|
{
|
||||||
|
return $"{ DaysFormatted}:{ HoursFormatted}:{ MinutesFormatted}:{ SecondsFormatted }";
|
||||||
|
}
|
||||||
|
return $"{ HoursFormatted}:{ MinutesFormatted }:{ SecondsFormatted }";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue