diff --git a/Roadie.Api.Library/Models/TrackList.cs b/Roadie.Api.Library/Models/TrackList.cs index 64bc8ab..cfe225a 100644 --- a/Roadie.Api.Library/Models/TrackList.cs +++ b/Roadie.Api.Library/Models/TrackList.cs @@ -77,7 +77,8 @@ namespace Roadie.Library.Models public int? FileSize { get; set; } - public static TrackList FromDataTrack(Data.Track track, + public static TrackList FromDataTrack(string trackPlayUrl, + Data.Track track, int releaseMediaNumber, Data.Release release, Data.Artist artist, @@ -111,7 +112,7 @@ namespace Roadie.Library.Models PlayedCount = track.PlayedCount, Rating = track.Rating, Title = track.Title, - TrackPlayUrl = $"{ baseUrl }/play/track/{ track.RoadieId }.mp3", + TrackPlayUrl = trackPlayUrl, Thumbnail = trackThumbnail }; diff --git a/Roadie.Api.Library/Roadie.Api.Library.csproj b/Roadie.Api.Library/Roadie.Api.Library.csproj index 1bf5487..d25a28f 100644 --- a/Roadie.Api.Library/Roadie.Api.Library.csproj +++ b/Roadie.Api.Library/Roadie.Api.Library.csproj @@ -20,7 +20,7 @@ - + diff --git a/Roadie.Api.Services/BookmarkService.cs b/Roadie.Api.Services/BookmarkService.cs index 578c63e..36f000a 100644 --- a/Roadie.Api.Services/BookmarkService.cs +++ b/Roadie.Api.Services/BookmarkService.cs @@ -61,6 +61,8 @@ namespace Roadie.Api.Services var rowCount = result.Count(); BookmarkList[] rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray(); + var user = this.GetUser(roadieUser.UserId); + foreach (var row in rows) { switch (row.Type) @@ -101,7 +103,8 @@ namespace Roadie.Api.Services Text = track.Title, Value = track.RoadieId.ToString() }; - row.Track = TrackList.FromDataTrack(track, + row.Track = TrackList.FromDataTrack(this.MakeTrackPlayUrl(user, track.Id, track.RoadieId), + track, track.ReleaseMedia.MediaNumber, track.ReleaseMedia.Release, track.ReleaseMedia.Release.Artist, @@ -111,6 +114,7 @@ namespace Roadie.Api.Services this.MakeReleaseThumbnailImage(track.ReleaseMedia.Release.RoadieId), this.MakeArtistThumbnailImage(track.ReleaseMedia.Release.Artist.RoadieId), this.MakeArtistThumbnailImage(track.TrackArtist == null ? null : (Guid?)track.TrackArtist.RoadieId)); + row.Track.TrackPlayUrl = this.MakeTrackPlayUrl(user, track.Id, track.RoadieId); row.Thumbnail = this.MakeTrackThumbnailImage(track.RoadieId); row.SortName = track.Title; break; diff --git a/Roadie.Api.Services/PlaylistService.cs b/Roadie.Api.Services/PlaylistService.cs index d3d544b..eaad1d9 100644 --- a/Roadie.Api.Services/PlaylistService.cs +++ b/Roadie.Api.Services/PlaylistService.cs @@ -147,6 +147,15 @@ namespace Roadie.Api.Services sw.Stop(); if (result?.Data != null && roadieUser != null) { + if (result?.Data?.Tracks != null) + { + var user = this.GetUser(roadieUser.UserId); + foreach (var track in result.Data.Tracks) + { + track.Track.TrackPlayUrl = this.MakeTrackPlayUrl(user, track.Track.DatabaseId, track.Track.Id); + } + } + result.Data.UserCanEdit = result.Data.Maintainer.Id == roadieUser.UserId || roadieUser.IsAdmin; var userBookmarkResult = await this.BookmarkService.List(roadieUser, new PagedRequest(), false, BookmarkType.Playlist); if (userBookmarkResult.IsSuccess) @@ -294,7 +303,8 @@ namespace Roadie.Api.Services select new PlaylistTrack { ListNumber = plt.pltr.ListNumber, - Track = TrackList.FromDataTrack(plt.t, + Track = TrackList.FromDataTrack(null, + plt.t, rm.MediaNumber, r, releaseArtist, diff --git a/Roadie.Api.Services/ReleaseService.cs b/Roadie.Api.Services/ReleaseService.cs index 3a14af8..6392fd1 100644 --- a/Roadie.Api.Services/ReleaseService.cs +++ b/Roadie.Api.Services/ReleaseService.cs @@ -101,6 +101,15 @@ namespace Roadie.Api.Services } if (result.Data.Medias != null) { + var user = this.GetUser(roadieUser.UserId); + foreach(var media in result.Data.Medias) + { + foreach(var track in media.Tracks) + { + track.TrackPlayUrl = this.MakeTrackPlayUrl(user, track.DatabaseId, track.Id); + } + } + var releaseTrackIds = result.Data.Medias.SelectMany(x => x.Tracks).Select(x => x.Id); var releaseUserTracks = (from ut in this.DbContext.UserTracks join t in this.DbContext.Tracks on ut.TrackId equals t.Id @@ -421,6 +430,7 @@ namespace Roadie.Api.Services where ut.UserId == roadieUser.Id where (from x in releaseTrackIds select x).Contains(ut.TrackId) select ut).ToArray(); + var user = this.GetUser(roadieUser.UserId); foreach (var release in rows) { var releaseMedias = new List(); @@ -432,7 +442,7 @@ namespace Roadie.Api.Services { var t = track.t.Adapt(); t.CssClass = string.IsNullOrEmpty(track.t.Hash) ? "Missing" : "Ok"; - t.TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ track.t.RoadieId}.mp3"; + t.TrackPlayUrl = this.MakeTrackPlayUrl(user, track.t.Id, track.t.RoadieId); var userRating = artistUserTracks.FirstOrDefault(x => x.TrackId == track.t.Id); if (userRating != null) { @@ -888,7 +898,6 @@ namespace Roadie.Api.Services t.MediaNumber = rm.MediaNumber; t.CssClass = string.IsNullOrEmpty(track.Hash) ? "Missing" : "Ok"; t.TrackArtist = track.TrackArtist != null ? ArtistList.FromDataArtist(track.TrackArtist, this.MakeArtistThumbnailImage(track.TrackArtist.RoadieId)) : null; - t.TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ t.Id}.mp3"; rmTracks.Add(t); } rm.Tracks = rmTracks; diff --git a/Roadie.Api.Services/Roadie.Api.Services.csproj b/Roadie.Api.Services/Roadie.Api.Services.csproj index 5d107c5..4613805 100644 --- a/Roadie.Api.Services/Roadie.Api.Services.csproj +++ b/Roadie.Api.Services/Roadie.Api.Services.csproj @@ -6,6 +6,7 @@ + diff --git a/Roadie.Api.Services/ServiceBase.cs b/Roadie.Api.Services/ServiceBase.cs index 6fb6f00..3ba1243 100644 --- a/Roadie.Api.Services/ServiceBase.cs +++ b/Roadie.Api.Services/ServiceBase.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using HashidsNet; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Roadie.Library; using Roadie.Library.Caching; @@ -17,6 +18,8 @@ namespace Roadie.Api.Services { public abstract class ServiceBase { + public static string TrackTokenSalt = "B0246908-FBD6-4E12-A96C-AF5B086115B3"; + protected readonly ICacheManager _cacheManager = null; protected readonly IRoadieSettings _configuration = null; protected readonly data.IRoadieDbContext _dbContext = null; @@ -699,5 +702,31 @@ namespace Roadie.Api.Services this.CacheManager.ClearRegion(release.CacheRegion); } } + + public static string TrackPlayToken(ApplicationUser user, Guid trackId) + { + var hashids = new Hashids(ServiceBase.TrackTokenSalt); + var trackIdPart = BitConverter.ToInt32(trackId.ToByteArray(), 6); + if(trackIdPart < 0) + { + trackIdPart = trackIdPart * -1; + } + var token = hashids.Encode(user.Id, SafeParser.ToNumber(user.CreatedDate.Value.ToString("DDHHmmss")), trackIdPart); + return token; + } + + protected string MakeTrackPlayUrl(ApplicationUser user, int trackId, Guid trackRoadieId) + { + return $"{ this.HttpContext.BaseUrl }/play/track/{user.Id}/{ ServiceBase.TrackPlayToken(user, trackRoadieId)}/{ trackRoadieId }.mp3"; + } + + public static bool ConfirmTrackPlayToken(ApplicationUser user, Guid trackRoadieId, string token) + { + if(string.IsNullOrEmpty(token)) + { + return false; + } + return ServiceBase.TrackPlayToken(user, trackRoadieId).Equals(token); + } } } \ No newline at end of file diff --git a/Roadie.Api.Services/TrackService.cs b/Roadie.Api.Services/TrackService.cs index bff7660..b08ff5c 100644 --- a/Roadie.Api.Services/TrackService.cs +++ b/Roadie.Api.Services/TrackService.cs @@ -415,6 +415,7 @@ namespace Roadie.Api.Services resultQuery = resultQuery.Where(x => x.ti.Tags != null && x.ti.Tags.Contains(tagValue)); } } + var user = this.GetUser(roadieUser.UserId); var result = resultQuery.Select(x => new TrackList { @@ -439,7 +440,7 @@ namespace Roadie.Api.Services PlayedCount = x.ti.PlayedCount, Rating = x.ti.Rating, Title = x.ti.Title, - TrackPlayUrl = $"{ this.HttpContext.BaseUrl }/play/track/{ x.ti.RoadieId }.mp3", + TrackPlayUrl = this.MakeTrackPlayUrl(user, x.ti.Id, x.ti.RoadieId), Thumbnail = this.MakeTrackThumbnailImage(x.ti.RoadieId) }); string sortBy = null; diff --git a/Roadie.Api/Controllers/PlayController.cs b/Roadie.Api/Controllers/PlayController.cs index a9e725c..6f5413f 100644 --- a/Roadie.Api/Controllers/PlayController.cs +++ b/Roadie.Api/Controllers/PlayController.cs @@ -65,10 +65,23 @@ namespace Roadie.Api.Controllers return File(System.Text.Encoding.Default.GetBytes(m3u), "audio/mpeg-url"); } - [HttpGet("track/{id}.{mp3?}")] - public async Task StreamTrack(Guid id) + /// + /// This was done to use a URL based token as many clients don't support adding Auth Bearer tokens to audio requests. + /// + [HttpGet("track/{userId}/{trackPlayToken}/{id}.{mp3?}")] + [AllowAnonymous] + public async Task StreamTrack(int userId, string trackPlayToken, Guid id) { - return await base.StreamTrack(id, this.TrackService, this.PlayActivityService); + var user = this.UserManager.Users.FirstOrDefault(x => x.Id == userId); + if(user == null) + { + return StatusCode((int)HttpStatusCode.Forbidden); + } + if(!ServiceBase.ConfirmTrackPlayToken(user, id, trackPlayToken)) + { + return StatusCode((int)HttpStatusCode.Forbidden); + } + return await base.StreamTrack(id, this.TrackService, this.PlayActivityService, this.UserModelForUser(user)); } } } \ No newline at end of file diff --git a/Roadie.Api/Roadie.Api.csproj b/Roadie.Api/Roadie.Api.csproj index 1b1cea6..38bd2e4 100644 --- a/Roadie.Api/Roadie.Api.csproj +++ b/Roadie.Api/Roadie.Api.csproj @@ -29,7 +29,7 @@ - +