M3U results implemented for Track and Release.

This commit is contained in:
Steven Hildreth 2018-11-18 08:42:02 -06:00
parent 375295746e
commit 27744337da
9 changed files with 120 additions and 39 deletions

View file

@ -6,8 +6,10 @@ using Microsoft.Extensions.Logging;
using Roadie.Api.Services;
using Roadie.Library.Caching;
using Roadie.Library.Identity;
using Roadie.Library.Utility;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
@ -20,18 +22,50 @@ namespace Roadie.Api.Controllers
public class PlayController : EntityControllerBase
{
private IPlayActivityService PlayActivityService { get; }
private IReleaseService ReleaseService { get; }
private ITrackService TrackService { get; }
public PlayController(ITrackService trackService, IPlayActivityService playActivityService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, UserManager<ApplicationUser> userManager)
public PlayController(ITrackService trackService, IReleaseService releaseService, IPlayActivityService playActivityService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, UserManager<ApplicationUser> userManager)
: base(cacheManager, configuration, userManager)
{
this._logger = logger.CreateLogger("RoadieApi.Controllers.PlayController");
this.TrackService = trackService;
this.PlayActivityService = playActivityService;
this.ReleaseService = releaseService;
}
[HttpGet("track/{id}")]
[HttpGet("release/m3u/{id}.{m3u?}")]
public async Task<FileResult> M3uForRelease(Guid id)
{
var user = await this.CurrentUserModel();
var release = await this.ReleaseService.ById(user, id, new string[1] { "tracks" });
if (release == null || release.IsNotFoundResult)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
var m3u = M3uHelper.M3uContentForTracks(release.Data.Medias.SelectMany(x => x.Tracks));
return File(System.Text.Encoding.Default.GetBytes(m3u), "audio/mpeg-url");
}
[HttpGet("track/m3u/{id}.{m3u?}")]
public async Task<FileResult> M3uForTrack(Guid id)
{
var user = await this.CurrentUserModel();
var track = await this.TrackService.ById(user, id, null);
if (track == null || track.IsNotFoundResult)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
var release = await this.ReleaseService.ById(user, Guid.Parse(track.Data.Release.Value), new string[1] { "tracks" });
if (release == null || release.IsNotFoundResult)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
var m3u = M3uHelper.M3uContentForTracks(release.Data.Medias.SelectMany(x => x.Tracks).Where(x => x.Id == id));
return File(System.Text.Encoding.Default.GetBytes(m3u), "audio/mpeg-url");
}
[HttpGet("track/{id}.{mp3?}")]
public async Task<FileStreamResult> StreamTrack(Guid id)
{
var user = await this.CurrentUserModel();
@ -63,7 +97,7 @@ namespace Roadie.Api.Controllers
Response.Headers.Add("Expires", info.Data.Expires);
var stream = new MemoryStream(info.Data.Bytes);
var playListUser = await this.PlayActivityService.CreatePlayActivity(user, info.Data);
this._logger.LogInformation($"StreamTrack PlayActivity `{ playListUser }`, StreamInfo `{ info.Data.ToString() }`");
this._logger.LogInformation($"StreamTrack PlayActivity `{ playListUser?.ToString() }`, StreamInfo `{ info.Data.ToString() }`");
return new FileStreamResult(stream, info.Data.ContentType)
{
FileDownloadName = info.Data.FileName

View file

@ -246,6 +246,7 @@ namespace Roadie.Api.Services
select ut.PlayedCount ?? 0).Sum(),
Thumbnail = MakeReleaseThumbnailImage(r.RoadieId)
}).ToArray().GroupBy(x => x.Release.Value).Select(x => x.First()).OrderBy(x => x.Release.Text).ToArray();
result.ArtistContributionReleases = result.ArtistContributionReleases.Any() ? result.ArtistContributionReleases : null;
}
if (includes.Contains("labels"))
{
@ -283,6 +284,7 @@ namespace Roadie.Api.Services
TrackCount = trackCount,
Thumbnail = MakeLabelThumbnailImage(l.RoadieId)
}).ToArray().GroupBy(x => x.Label.Value).Select(x => x.First()).OrderBy(x => x.SortName).ThenBy(x => x.Label.Text).ToArray();
result.ArtistLabels = result.ArtistLabels.Any() ? result.ArtistLabels : null;
}
}
sw.Stop();

View file

@ -88,7 +88,7 @@ namespace Roadie.Api.Services
ReleasePlayUrl = $"{ this.HttpContext.BaseUrl }/play/release/{ r.RoadieId}",
Rating = t.Rating,
UserRating = usertrack.Rating,
TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ t.RoadieId}",
TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ t.RoadieId}.mp3",
ArtistThumbnail = this.MakeArtistThumbnailImage(trackArtist != null ? trackArtist.RoadieId : releaseArtist.RoadieId),
ReleaseThumbnail = this.MakeReleaseThumbnailImage(r.RoadieId),
UserThumbnail = this.MakeUserThumbnailImage(u.RoadieId)
@ -127,10 +127,10 @@ namespace Roadie.Api.Services
{
return new OperationResult<PlayActivityList>($"CreatePlayActivity: Invalid Track. Track Id [{streamInfo.Track.Value}], FilePath [{track.FilePath}], Filename [{track.FileName}]");
}
var user = this.GetUser(roadieUser.UserId);
var user = this.GetUser(roadieUser?.UserId);
if (user == null)
{
return new OperationResult<PlayActivityList>($"CreatePlayActivity: Unable To Find User [{ roadieUser.UserId }]");
return new OperationResult<PlayActivityList>($"CreatePlayActivity: Unable To Find User [{ roadieUser?.UserId }]");
}
var now = DateTime.UtcNow;
track.PlayedCount = (track.PlayedCount ?? 0) + 1;
@ -183,7 +183,7 @@ namespace Roadie.Api.Services
ReleasePlayUrl = $"{ this.HttpContext.BaseUrl }/play/release/{ track.ReleaseMedia.Release.RoadieId}",
Rating = track.Rating,
UserRating = userTrack.Rating,
TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ track.RoadieId}",
TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ track.RoadieId}.mp3",
ArtistThumbnail = this.MakeArtistThumbnailImage(track.TrackArtist != null ? track.TrackArtist.RoadieId : track.ReleaseMedia.Release.Artist.RoadieId),
ReleaseThumbnail = this.MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId),
UserThumbnail = this.MakeUserThumbnailImage(roadieUser.UserId)

View file

@ -44,7 +44,7 @@ namespace Roadie.Api.Services
{
return await this.ReleaseByIdAction(id, includes);
}, data.Artist.CacheRegionUrn(id));
if (result?.Data != null)
if (result?.Data != null && roadieUser != null)
{
var release = this.GetRelease(id);
result.Data.UserBookmark = this.GetUserBookmarks(roadieUser).FirstOrDefault(x => x.Type == BookmarkType.Release && x.Bookmark.Value == release.RoadieId.ToString());
@ -226,7 +226,7 @@ namespace Roadie.Api.Services
// var userRating = artistUserTracks.FirstOrDefault(x => x.TrackId == track.t.Id);
// var t = track.t.Adapt<TrackList>();
// t.CssClass = string.IsNullOrEmpty(track.t.Hash) ? "Missing" : "Ok";
// t.TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ track.t.RoadieId}";
// t.TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ track.t.RoadieId}.mp3";
// t.UserTrack = new UserTrack
// {
// Rating = userRating.Rating,
@ -412,35 +412,36 @@ namespace Roadie.Api.Services
}
if (includes.Contains("tracks"))
{
var releaseTracks = (from r in this.DbContext.Releases
join rm in this.DbContext.ReleaseMedias on r.Id equals rm.ReleaseId
join t in this.DbContext.Tracks on rm.Id equals t.ReleaseMediaId
join a in this.DbContext.Artists on t.ArtistId equals a.Id into tas
from a in tas.DefaultIfEmpty()
where r.Id == release.Id
orderby rm.MediaNumber, t.TrackNumber
select new
{
t,
releaseMedia = rm,
trackArtist = a
}).ToArray();
var releaseMedias = new List<ReleaseMediaList>();
var releaseTrackIds = releaseTracks.Select(x => x.t.Id).ToList();
foreach (var releaseMedia in releaseTracks.Select(x => x.releaseMedia).Distinct())
foreach (var releaseMedia in release.Medias.OrderBy(x => x.MediaNumber))
{
var rm = releaseMedia.Adapt<ReleaseMediaList>();
var rmTracks = new List<TrackList>();
foreach (var track in releaseTracks.Where(x => x.t.ReleaseMediaId == releaseMedia.Id))
foreach (var track in releaseMedia.Tracks.OrderBy(x => x.TrackNumber))
{
var t = track.t.Adapt<TrackList>();
t.TrackArtist = track.trackArtist != null ? new DataToken
var t = track.Adapt<TrackList>();
t.Artist = new DataToken
{
Text = track.trackArtist.Name,
Value = track.trackArtist.RoadieId.ToString()
Text = release.Artist.Name,
Value = release.Artist.RoadieId.ToString()
};
t.Release = new DataToken
{
Text = release.Title,
Value = release.RoadieId.ToString()
};
t.Track = new DataToken
{
Text = track.Title,
Value = track.RoadieId.ToString()
};
t.TrackArtist = track.TrackArtist != null ? new DataToken
{
Text = track.TrackArtist.Name,
Value = track.TrackArtist.RoadieId.ToString()
} : null;
t.TrackArtistThumbnail = track.trackArtist != null ? this.MakeArtistThumbnailImage(track.trackArtist.RoadieId) : null;
t.TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ t.Id}";
t.TrackArtistThumbnail = track.TrackArtist != null ? this.MakeArtistThumbnailImage(track.TrackArtist.RoadieId) : null;
t.TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ t.Id}.mp3";
rmTracks.Add(t);
}
rm.Tracks = rmTracks;

View file

@ -129,6 +129,9 @@ namespace Roadie.Api.Services
.Include(x => x.Artist)
.Include(x => x.Genres)
.Include("Genres.Genre")
.Include(x => x.Medias)
.Include("Medias.Tracks")
.Include("Medias.Tracks.TrackArtist")
.FirstOrDefault(x => x.RoadieId == id);
}, data.Release.CacheRegionUrn(id));
}
@ -156,20 +159,29 @@ namespace Roadie.Api.Services
}, data.Track.CacheRegionUrn(id));
}
protected ApplicationUser GetUser(Guid id)
protected ApplicationUser GetUser(Guid? id)
{
return this.CacheManager.Get(ApplicationUser.CacheUrn(id), () =>
if(!id.HasValue)
{
return null;
}
return this.CacheManager.Get(ApplicationUser.CacheUrn(id.Value), () =>
{
return this.DbContext.Users
.Include(x => x.ArtistRatings)
.Include(x => x.ReleaseRatings)
.Include(x => x.TrackRatings)
.FirstOrDefault(x => x.RoadieId == id);
}, ApplicationUser.CacheRegionUrn(id));
}, ApplicationUser.CacheRegionUrn(id.Value));
}
protected List<BookmarkList> GetUserBookmarks(User roadieUser)
{
if(roadieUser == null)
{
return null;
}
return this.CacheManager.Get($"urn:user_bookmarks:{ roadieUser.Id }", () =>
{
var bookmarks = from b in this.DbContext.Bookmarks
@ -311,5 +323,6 @@ namespace Roadie.Api.Services
{
return new Image($"{this.HttpContext.ImageBaseUrl }/thumbnail/{ type }/{id}");
}
}
}

View file

@ -94,7 +94,7 @@ namespace Roadie.Api.Services
return new OperationResult<Track>(true, string.Format("Track Not Found [{0}]", id));
}
var result = track.Adapt<Track>();
result.PlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{track.RoadieId}";
result.PlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{track.RoadieId}.mp3";
result.IsLocked = (track.IsLocked ?? false) ||
(track.ReleaseMedia.IsLocked ?? false) ||
(track.ReleaseMedia.Release.IsLocked ?? false ) ||
@ -285,7 +285,7 @@ namespace Roadie.Api.Services
ArtistThumbnail = this.MakeArtistThumbnailImage(x.releaseArtist.RoadieId),
Title = x.t.Title,
TrackArtistThumbnail = x.trackArtist != null ? this.MakeArtistThumbnailImage(x.trackArtist.RoadieId) : null,
TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ x.t.RoadieId }",
TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ x.t.RoadieId }.mp3",
Thumbnail = this.MakeTrackThumbnailImage(x.t.RoadieId)
});
string sortBy = null;

View file

@ -75,7 +75,7 @@ namespace Roadie.Api
//});
app.UseSignalR(routes =>
{
routes.MapHub<PlayActivityHub>("playActivityHub");
routes.MapHub<PlayActivityHub>("/playActivityHub");
});
app.UseMvc(b =>
{

View file

@ -1,5 +1,11 @@
{
"urls": "http://localhost:5123",
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://localhost:5123/"
}
}
},
"Logging": {
"IncludeScopes": false,
"Console": {

View file

@ -0,0 +1,25 @@
using Roadie.Library.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace Roadie.Library.Utility
{
public static class M3uHelper
{
public static string M3uContentForTracks(IEnumerable<TrackList> tracks)
{
var result = new List<string>
{
"#EXTM3U"
};
foreach (var track in tracks)
{
result.Add($"#EXTINF:{ track.Duration },{ track.Artist.Text} - { track.Track.Text }");
result.Add($"{ track.TrackPlayUrl }");
result.Add(string.Empty);
}
return string.Join("\r\n", result);
}
}
}