mirror of
https://github.com/sphildreth/roadie
synced 2024-11-22 20:23:16 +00:00
M3U results implemented for Track and Release.
This commit is contained in:
parent
375295746e
commit
27744337da
9 changed files with 120 additions and 39 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace Roadie.Api
|
|||
//});
|
||||
app.UseSignalR(routes =>
|
||||
{
|
||||
routes.MapHub<PlayActivityHub>("playActivityHub");
|
||||
routes.MapHub<PlayActivityHub>("/playActivityHub");
|
||||
});
|
||||
app.UseMvc(b =>
|
||||
{
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
{
|
||||
"urls": "http://localhost:5123",
|
||||
"Kestrel": {
|
||||
"EndPoints": {
|
||||
"Http": {
|
||||
"Url": "http://localhost:5123/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
"IncludeScopes": false,
|
||||
"Console": {
|
||||
|
|
25
RoadieLibrary/Utility/M3uHelper.cs
Normal file
25
RoadieLibrary/Utility/M3uHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue