mirror of
https://github.com/sphildreth/roadie
synced 2024-11-25 05:30:24 +00:00
commit
37c2eef4bc
8 changed files with 211 additions and 72 deletions
|
@ -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; }
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -355,17 +355,30 @@ namespace Roadie.Api.Services
|
|||
private async Task<FileOperationResult<IImage>> PlaylistImageAction(Guid id, EntityTagHeaderValue etag = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string playlistImageFilename = null;
|
||||
IImage image = null;
|
||||
if (id == PlaylistService.DynamicFavoritePlaylistId)
|
||||
{
|
||||
image = new Library.Imaging.Image(id)
|
||||
{
|
||||
CreatedDate = DateTime.UtcNow
|
||||
};
|
||||
playlistImageFilename = Path.Combine(Configuration.ContentPath, @"images/d.favorite.playlist.jpg");
|
||||
}
|
||||
else
|
||||
{
|
||||
var playlist = await GetPlaylist(id).ConfigureAwait(false);
|
||||
if (playlist == null)
|
||||
{
|
||||
return new FileOperationResult<IImage>(true, $"Playlist Not Found [{id}]");
|
||||
}
|
||||
IImage image = new Library.Imaging.Image(id)
|
||||
image = new Library.Imaging.Image(id)
|
||||
{
|
||||
CreatedDate = playlist.CreatedDate
|
||||
};
|
||||
var playlistImageFilename = playlist.PathToImage(Configuration);
|
||||
playlistImageFilename = playlist.PathToImage(Configuration);
|
||||
}
|
||||
try
|
||||
{
|
||||
if (File.Exists(playlistImageFilename))
|
||||
|
|
|
@ -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();
|
||||
OperationResult<Playlist> result = null;
|
||||
var isPlaylistDynamic = id == PlaylistService.DynamicFavoritePlaylistId;
|
||||
if (isPlaylistDynamic)
|
||||
{
|
||||
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))}";
|
||||
var result = await CacheManager.GetAsync(cacheKey, async () =>
|
||||
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,14 +337,15 @@ 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;
|
||||
if (!isPlaylistDynamic)
|
||||
{
|
||||
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)
|
||||
{
|
||||
result.Data.UserBookmarked = userBookmarkResult?.Rows?.FirstOrDefault(x => x?.Bookmark?.Value == result?.Data?.Id?.ToString()) != null;
|
||||
}
|
||||
if (result.Data.Comments.Any())
|
||||
if (result.Data.Comments?.Any() == true)
|
||||
{
|
||||
var commentIds = result.Data.Comments.Select(x => x.DatabaseId).ToArray();
|
||||
var userCommentReactions = await (from cr in DbContext.CommentReactions
|
||||
|
@ -272,6 +362,7 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new OperationResult<Playlist>(result.Messages)
|
||||
{
|
||||
|
@ -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>
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
select new KeyValuePair<int, int>(t.Id, plt.ListNumber)).ToArrayAsync().ConfigureAwait(false);
|
||||
|
||||
if(!request.FilterFavoriteOnly &&
|
||||
request.FilterToPlaylistId == PlaylistService.DynamicFavoritePlaylistId)
|
||||
{
|
||||
plt.ListNumber,
|
||||
t.Id
|
||||
}).ToArrayAsync().ConfigureAwait(false);
|
||||
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";
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
BIN
Roadie.Api/wwwroot/Images/d.favorite.playlist.jpg
Normal file
BIN
Roadie.Api/wwwroot/Images/d.favorite.playlist.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
Loading…
Reference in a new issue