Merge pull request #43 from sphildreth/enhace-42

resolves #42
This commit is contained in:
Steven Hildreth 2021-12-08 22:21:31 -06:00 committed by GitHub
commit 37c2eef4bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 211 additions and 72 deletions

View file

@ -1,27 +1,16 @@
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Roadie.Library.Caching;
using Roadie.Library.Configuration;
using Roadie.Library.Data;
using Roadie.Library.Engines;
using Roadie.Library.Extensions;
using Roadie.Library.MetaData.ID3Tags;
using Roadie.Library.Processors;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Xunit;
namespace Roadie.Library.Tests
{
public class ArtistLookupEngineTests
{
private IEventMessageLogger MessageLogger { get; }
private ILogger Logger
{
get
@ -30,8 +19,6 @@ namespace Roadie.Library.Tests
}
}
private IRoadieSettings Configuration { get; }
public DictionaryCacheManager CacheManager { get; }
private Encoding.IHttpEncoder HttpEncoder { get; }
@ -56,4 +43,4 @@ namespace Roadie.Library.Tests
Console.WriteLine($"Log Level [{ e.Level }] Log Message [{ e.Message }] ");
}
}
}
}

View file

@ -11,11 +11,11 @@
<ItemGroup>
<PackageReference Include="AutoCompare.Core" Version="1.0.0" />
<PackageReference Include="CsvHelper" Version="27.1.1" />
<PackageReference Include="EFCore.BulkExtensions" Version="6.0.2" />
<PackageReference Include="FluentFTP" Version="35.0.5" />
<PackageReference Include="CsvHelper" Version="27.2.1" />
<PackageReference Include="EFCore.BulkExtensions" Version="6.1.6" />
<PackageReference Include="FluentFTP" Version="35.2.1" />
<PackageReference Include="Hashids.net" Version="1.4.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.38" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.39" />
<PackageReference Include="IdSharp.Common" Version="1.0.1" />
<PackageReference Include="IdSharp.Tagging" Version="1.0.0-rc3" />
<PackageReference Include="Inflatable.Lastfm" Version="1.2.0" />
@ -36,7 +36,7 @@
<PackageReference Include="MimeMapping" Version="1.0.1.37" />
<PackageReference Include="NodaTime" Version="3.0.9" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0" />
<PackageReference Include="RestSharp" Version="106.13.0" />
<PackageReference Include="RestSharp" Version="106.15.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />

View file

@ -356,16 +356,29 @@ namespace Roadie.Api.Services
{
try
{
var playlist = await GetPlaylist(id).ConfigureAwait(false);
if (playlist == null)
string playlistImageFilename = null;
IImage image = null;
if (id == PlaylistService.DynamicFavoritePlaylistId)
{
return new FileOperationResult<IImage>(true, $"Playlist Not Found [{id}]");
image = new Library.Imaging.Image(id)
{
CreatedDate = DateTime.UtcNow
};
playlistImageFilename = Path.Combine(Configuration.ContentPath, @"images/d.favorite.playlist.jpg");
}
IImage image = new Library.Imaging.Image(id)
else
{
CreatedDate = playlist.CreatedDate
};
var playlistImageFilename = playlist.PathToImage(Configuration);
var playlist = await GetPlaylist(id).ConfigureAwait(false);
if (playlist == null)
{
return new FileOperationResult<IImage>(true, $"Playlist Not Found [{id}]");
}
image = new Library.Imaging.Image(id)
{
CreatedDate = playlist.CreatedDate
};
playlistImageFilename = playlist.PathToImage(Configuration);
}
try
{
if (File.Exists(playlistImageFilename))

View file

@ -28,6 +28,8 @@ namespace Roadie.Api.Services
{
public class PlaylistService : ServiceBase, IPlaylistService
{
public static Guid DynamicFavoritePlaylistId = Guid.Parse("54BA96A4-DFB5-4970-A989-CBA4BF0EFE75");
private IBookmarkService BookmarkService { get; }
public PlaylistService(IRoadieSettings configuration,
@ -85,7 +87,7 @@ namespace Roadie.Api.Services
{
ReleaseCount = result.ReleaseCount,
TrackCount = result.TrackCount,
TrackSize = result.DurationTime,
TrackSize = result.Duration.ToString(),
FileSize = playlistTracks.Sum(x => (long?)x.t.FileSize).ToFileSize()
};
tsw.Stop();
@ -232,11 +234,98 @@ namespace Roadie.Api.Services
{
var sw = Stopwatch.StartNew();
sw.Start();
var cacheKey = $"urn:playlist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
var result = await CacheManager.GetAsync(cacheKey, async () =>
OperationResult<Playlist> result = null;
var isPlaylistDynamic = id == PlaylistService.DynamicFavoritePlaylistId;
if (isPlaylistDynamic)
{
return await PlaylistByIdAction(id, includes).ConfigureAwait(false);
}, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
var now = DateTime.UtcNow;
var userFavoriteTracks = from ut in DbContext.UserTracks
join t in DbContext.Tracks on ut.TrackId equals t.Id
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
join releaseArtist in DbContext.Artists on r.ArtistId equals releaseArtist.Id
join trackArtist in DbContext.Artists on t.ArtistId equals trackArtist.Id into tas
from trackArtist in tas.DefaultIfEmpty()
where ut.UserId == roadieUser.Id
where ut.IsFavorite == true
select new
{
r,
releaseArtist,
trackArtist,
rm,
t
};
var dynamicStatus = new ReleaseGroupingStatistics
{
ReleaseCount = SafeParser.ToNumber<int?>(await userFavoriteTracks.Select(x => x.rm.ReleaseId).Distinct().CountAsync().ConfigureAwait(false)),
TrackCount = SafeParser.ToNumber<int?>(await userFavoriteTracks.Select(x => x.t.Id).CountAsync().ConfigureAwait(false)),
TrackSize = (await userFavoriteTracks.SumAsync(x => x.t.Duration)).ToString(),
FileSize = SafeParser.ToNumber<long?>(await userFavoriteTracks.SumAsync(x => x.t.FileSize).ConfigureAwait(false)).ToFileSize()
};
var dynamicTracks = new List<PlaylistTrack>();
var looper = 1;
foreach(var td in (await userFavoriteTracks.ToArrayAsync().ConfigureAwait(false)))
{
dynamicTracks.Add(new PlaylistTrack
{
ListNumber = looper,
Track = TrackList.FromDataTrack(null,
td.t,
td.rm.MediaNumber,
td.r,
td.releaseArtist,
td.trackArtist,
HttpContext.BaseUrl,
ImageHelper.MakeTrackThumbnailImage(Configuration, HttpContext, td.t.RoadieId),
ImageHelper.MakeReleaseThumbnailImage(Configuration, HttpContext, td.r.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, td.releaseArtist.RoadieId),
ImageHelper.MakeArtistThumbnailImage(Configuration, HttpContext, td.trackArtist == null ? null : (Guid?)td.trackArtist.RoadieId))
});
looper++;
}
result = new OperationResult<Playlist>
{
Data = new Playlist
{
Id = id,
Name = "[r] Favorites",
Description = "Dynamic Playlist of Favorited Tracks",
CreatedDate = now,
LastUpdated = now,
UserCanEdit = false,
IsPublic = false,
Maintainer = new UserList
{
IsAdmin = false,
IsEditor = false,
IsPrivate = true,
User = new DataToken
{
Text = "Roadie System"
},
Thumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, roadieUser.UserId)
},
Statistics = dynamicStatus,
ReleaseCount = SafeParser.ToNumber<short?>(dynamicStatus.ReleaseCount),
TrackCount = SafeParser.ToNumber<short?>(dynamicStatus.TrackCount),
Duration = (await userFavoriteTracks.SumAsync(x => x.t.Duration).ConfigureAwait(false)),
Thumbnail = ImageHelper.MakePlaylistThumbnailImage(Configuration, HttpContext, id),
Tracks = dynamicTracks,
MediumThumbnail = ImageHelper.MakeThumbnailImage(Configuration, HttpContext, id, "playlist", Configuration.MediumImageSize.Width, Configuration.MediumImageSize.Height)
},
IsSuccess = true
};
}
else
{
var cacheKey = $"urn:playlist_by_id_operation:{id}:{(includes == null ? "0" : string.Join("|", includes))}";
result = await CacheManager.GetAsync(cacheKey, async () =>
{
return await PlaylistByIdAction(id, includes).ConfigureAwait(false);
}, data.Artist.CacheRegionUrn(id)).ConfigureAwait(false);
}
sw.Stop();
if (result?.Data != null && roadieUser != null)
{
@ -248,27 +337,29 @@ namespace Roadie.Api.Services
track.Track.TrackPlayUrl = MakeTrackPlayUrl(user, HttpContext.BaseUrl, track.Track.Id);
}
}
result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin;
var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Playlist).ConfigureAwait(false);
if (userBookmarkResult.IsSuccess)
if (!isPlaylistDynamic)
{
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments.Any())
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = await (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr)
.ToArrayAsync()
.ConfigureAwait(false);
foreach (var comment in result.Data.Comments)
result.Data.UserCanEdit = result.Data.Maintainer?.Id == roadieUser.UserId || roadieUser.IsAdmin;
var userBookmarkResult = await BookmarkService.ListAsync(roadieUser, new PagedRequest(), false, BookmarkType.Playlist).ConfigureAwait(false);
if (userBookmarkResult.IsSuccess)
{
var userCommentReaction = Array.Find(userCommentReactions, x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
}
if (result.Data.Comments?.Any() == true)
{
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
var userCommentReactions = await (from cr in DbContext.CommentReactions
where commentIds.Contains(cr.CommentId)
where cr.UserId == roadieUser.Id
select cr)
.ToArrayAsync()
.ConfigureAwait(false);
foreach (var comment in result.Data.Comments)
{
var userCommentReaction = Array.Find(userCommentReactions, x => x.CommentId == comment.DatabaseId);
comment.IsDisliked = userCommentReaction?.ReactionValue == CommentReaction.Dislike;
comment.IsLiked = userCommentReaction?.ReactionValue == CommentReaction.Like;
}
}
}
}
@ -344,12 +435,12 @@ namespace Roadie.Api.Services
if (request.FilterToReleaseId.HasValue)
{
playlistReleaseTrackIds = await (from pl in DbContext.Playlists
join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
join t in DbContext.Tracks on pltr.TrackId equals t.Id
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
where r.RoadieId == request.FilterToReleaseId
select pl.Id
join pltr in DbContext.PlaylistTracks on pl.Id equals pltr.PlayListId
join t in DbContext.Tracks on pltr.TrackId equals t.Id
join rm in DbContext.ReleaseMedias on t.ReleaseMediaId equals rm.Id
join r in DbContext.Releases on rm.ReleaseId equals r.Id
where r.RoadieId == request.FilterToReleaseId
select pl.Id
).ToArrayAsync().ConfigureAwait(false);
}
var normalizedFilterValue = !string.IsNullOrEmpty(request.FilterValue)
@ -397,6 +488,46 @@ namespace Roadie.Api.Services
.Take(request.LimitValue)
.ToArrayAsync()
.ConfigureAwait(false);
// Dynamic list of favorites
if (!request.FilterToArtistId.HasValue && !request.FilterToReleaseId.HasValue && roadieUser != null)
{
var userFavoriteTracks = from ut in DbContext.UserTracks
join t in DbContext.Tracks on ut.TrackId equals t.Id
where ut.UserId == roadieUser.Id
where ut.IsFavorite == true
select t;
var numberOfFavorites = SafeParser.ToNumber<short?>(await userFavoriteTracks.CountAsync());
if (numberOfFavorites > 0)
{
var now = DateTime.UtcNow;
var dynamicPlaylist = new PlaylistList
{
Playlist = new DataToken
{
Text = "[r] Favorites",
Value = DynamicFavoritePlaylistId.ToString()
},
User = new DataToken
{
Text = roadieUser.UserName,
Value = roadieUser.UserId.ToString()
},
PlaylistCount = numberOfFavorites.Value,
IsPublic = false,
Duration = userFavoriteTracks.Sum(x => x.Duration),
TrackCount = numberOfFavorites.Value,
CreatedDate = now,
LastUpdated = now,
UserThumbnail = ImageHelper.MakeUserThumbnailImage(Configuration, HttpContext, roadieUser.UserId),
Id = DynamicFavoritePlaylistId,
Thumbnail = ImageHelper.MakePlaylistThumbnailImage(Configuration, HttpContext, DynamicFavoritePlaylistId)
};
rows = new List<PlaylistList>(rows.Concat<PlaylistList>(new PlaylistList[1] { dynamicPlaylist })).ToArray();
}
}
sw.Stop();
return new Library.Models.Pagination.PagedResult<PlaylistList>
{
@ -442,7 +573,7 @@ namespace Roadie.Api.Services
var sw = new Stopwatch();
sw.Start();
var errors = new List<Exception>();
var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false);
var playlist = await DbContext.Playlists.FirstOrDefaultAsync(x => x.RoadieId == model.Id).ConfigureAwait(false);
if (playlist == null)
{
return new OperationResult<bool>(true, $"Playlist Not Found [{model.Id}]");
@ -531,8 +662,8 @@ namespace Roadie.Api.Services
playlist.Tracks.Clear();
var tracks = await (from t in DbContext.Tracks
join plt in request.Tracks on t.RoadieId equals plt.Track.Id
select t).ToArrayAsync().ConfigureAwait(false);
join plt in request.Tracks on t.RoadieId equals plt.Track.Id
select t).ToArrayAsync().ConfigureAwait(false);
foreach (var newPlaylistTrack in request.Tracks.OrderBy(x => x.ListNumber))
{
var track = Array.Find(tracks, x => x.RoadieId == newPlaylistTrack.Track.Id);

View file

@ -11,7 +11,7 @@
<PackageReference Include="Hashids.net" Version="1.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.14.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.14" />
</ItemGroup>

View file

@ -386,17 +386,25 @@ namespace Roadie.Api.Services
join t in DbContext.Tracks on plt.TrackId equals t.Id
where p.RoadieId == request.FilterToPlaylistId.Value
orderby plt.ListNumber
select new
{
plt.ListNumber,
t.Id
}).ToArrayAsync().ConfigureAwait(false);
select new KeyValuePair<int, int>(t.Id, plt.ListNumber)).ToArrayAsync().ConfigureAwait(false);
if(!request.FilterFavoriteOnly &&
request.FilterToPlaylistId == PlaylistService.DynamicFavoritePlaylistId)
{
var dynamicPlaylistFavoriteTrackIds = await (from ut in DbContext.UserTracks
join t in DbContext.Tracks on ut.TrackId equals t.Id
where ut.UserId == roadieUser.Id
where ut.IsFavorite == true
orderby t.CreatedDate descending
select t.Id).ToArrayAsync().ConfigureAwait(false);
playlistTrackInfos = dynamicPlaylistFavoriteTrackIds.Select((x,i) => new KeyValuePair<int, int>(x, i+1)).ToArray();
}
rowCount = playlistTrackInfos.Length;
playListTrackPositions = playlistTrackInfos
.Skip(request.SkipValue)
.Take(request.LimitValue)
.ToDictionary(x => x.Id, x => x.ListNumber);
.ToDictionary(x => x.Key, x => x.Value);
playlistTrackIds = playListTrackPositions.Select(x => x.Key).ToArray();
request.Sort = "TrackNumber";
request.Order = "ASC";

View file

@ -41,18 +41,18 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.11.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.0" />
<PackageReference Include="Pastel" Version="2.1.0" />
<PackageReference Include="Pastel" Version="3.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Exceptions" Version="8.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.LiteDB.NetStandard" Version="1.0.14" />
<PackageReference Include="Serilog.Sinks.RollingFileAlternate" Version="2.0.9" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.14.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.14" />
</ItemGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB