mirror of
https://github.com/sphildreth/roadie
synced 2024-11-10 14:54:11 +00:00
Added download ability for admins and editors.
This commit is contained in:
parent
3a4f95fd24
commit
62ad0cee29
7 changed files with 150 additions and 65 deletions
|
@ -32,13 +32,15 @@ namespace Roadie.Api.Controllers
|
|||
private Library.Models.Users.User SubsonicUser { get; set; }
|
||||
|
||||
private ITrackService TrackService { get; }
|
||||
private IReleaseService ReleaseService { get; }
|
||||
|
||||
public SubsonicController(ISubsonicService subsonicService, ITrackService trackService, IPlayActivityService playActivityService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, UserManager<ApplicationUser> userManager)
|
||||
public SubsonicController(ISubsonicService subsonicService, 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.SubsonicController");
|
||||
this.SubsonicService = subsonicService;
|
||||
this.TrackService = trackService;
|
||||
this.ReleaseService = releaseService;
|
||||
this.PlayActivityService = playActivityService;
|
||||
}
|
||||
|
||||
|
@ -554,6 +556,37 @@ namespace Roadie.Api.Controllers
|
|||
return await base.StreamTrack(trackId.Value, this.TrackService, this.PlayActivityService, this.SubsonicUser);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("download.view")]
|
||||
[HttpPost("download.view")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> Download(SubsonicRequest request)
|
||||
{
|
||||
var authResult = await this.AuthenticateUser(request);
|
||||
if (authResult != null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
var trackId = request.TrackId;
|
||||
if (trackId != null)
|
||||
{
|
||||
return await base.StreamTrack(trackId.Value, this.TrackService, this.PlayActivityService, this.SubsonicUser);
|
||||
}
|
||||
var releaseId = request.ReleaseId;
|
||||
if(releaseId != null)
|
||||
{
|
||||
var releaseZip = await this.ReleaseService.ReleaseZipped(this.SubsonicUser, releaseId.Value);
|
||||
if(!releaseZip.IsSuccess)
|
||||
{
|
||||
return NotFound("Unknown Release id");
|
||||
}
|
||||
return File(releaseZip.Data, "application/zip",(string)releaseZip.AdditionalData["ZipFileName"]);
|
||||
|
||||
}
|
||||
return NotFound($"Unknown download id `{ request.id }`");
|
||||
}
|
||||
|
||||
|
||||
private async Task<IActionResult> AuthenticateUser(SubsonicRequest request)
|
||||
{
|
||||
var appUser = await this.SubsonicService.Authenticate(request);
|
||||
|
|
|
@ -13,5 +13,6 @@ namespace Roadie.Api.Services
|
|||
Task<OperationResult<Release>> ById(User roadieUser, Guid id, IEnumerable<string> includes = null);
|
||||
|
||||
Task<PagedResult<ReleaseList>> List(User user, PagedRequest request, bool? doRandomize = false);
|
||||
Task<FileOperationResult<byte[]>> ReleaseZipped(User roadieUser, Guid id);
|
||||
}
|
||||
}
|
|
@ -225,7 +225,7 @@ namespace Roadie.Api.Services
|
|||
artistFolder = artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
|
||||
if (!Directory.Exists(artistFolder))
|
||||
{
|
||||
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist [{ artist.ToString() }]");
|
||||
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ artist.ToString() }`");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -439,14 +439,14 @@ namespace Roadie.Api.Services
|
|||
artistFolder = release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
|
||||
if (!Directory.Exists(artistFolder))
|
||||
{
|
||||
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist [{ release.Artist.ToString() }]");
|
||||
this.Logger.LogWarning($"Artist Folder [{ artistFolder }], Not Found For Artist `{ release.Artist.ToString() }`");
|
||||
}
|
||||
else
|
||||
{
|
||||
releaseFolder = release.ReleaseFileFolder(artistFolder);
|
||||
if (!Directory.Exists(releaseFolder))
|
||||
{
|
||||
this.Logger.LogWarning($"Release Folder [{ releaseFolder }], Not Found For Release [{ release.ToString() }]");
|
||||
this.Logger.LogWarning($"Release Folder [{ releaseFolder }], Not Found For Release `{ release.ToString() }`");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -460,7 +460,7 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Logger.LogError(ex, $"Error Reading Release Folder [{ releaseFolder }] Artist Folder [{ artistFolder }] For Artist [{ release.Artist.Id }]");
|
||||
this.Logger.LogError(ex, $"Error Reading Release Folder [{ releaseFolder }] Artist Folder [{ artistFolder }] For Artist `{ release.Artist.Id }`");
|
||||
}
|
||||
imageBytes = imageBytes ?? release.Thumbnail;
|
||||
var image = new data.Image
|
||||
|
|
|
@ -16,6 +16,8 @@ using Roadie.Library.Utility;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -324,6 +326,63 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<FileOperationResult<byte[]>> ReleaseZipped(User roadieUser, Guid id)
|
||||
{
|
||||
var release = this.GetRelease(id);
|
||||
if (release == null)
|
||||
{
|
||||
return new FileOperationResult<byte[]>(true, string.Format("Release Not Found [{0}]", id));
|
||||
}
|
||||
|
||||
byte[] zipBytes = null;
|
||||
string zipFileName = null;
|
||||
try
|
||||
{
|
||||
var artistFolder = release.Artist.ArtistFileFolder(this.Configuration, this.Configuration.LibraryFolder);
|
||||
var releaseFolder = release.ReleaseFileFolder(artistFolder);
|
||||
if(!Directory.Exists(releaseFolder))
|
||||
{
|
||||
this.Logger.LogCritical($"Release Folder [{ releaseFolder }] not found for Release `{ release }`");
|
||||
return new FileOperationResult<byte[]>(true, string.Format("Release Folder Not Found [{0}]", id));
|
||||
}
|
||||
var releaseFiles = Directory.GetFiles(releaseFolder);
|
||||
using (MemoryStream zipStream = new MemoryStream())
|
||||
{
|
||||
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create))
|
||||
{
|
||||
foreach (var releaseFile in releaseFiles)
|
||||
{
|
||||
var fileInfo = new FileInfo(releaseFile);
|
||||
if (fileInfo.Extension.ToLower() == ".mp3" || fileInfo.Extension.ToLower() == ".jpg")
|
||||
{
|
||||
ZipArchiveEntry entry = zip.CreateEntry(fileInfo.Name);
|
||||
using (Stream entryStream = entry.Open())
|
||||
{
|
||||
using (FileStream s = fileInfo.OpenRead())
|
||||
{
|
||||
s.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
zipBytes = zipStream.ToArray();
|
||||
}
|
||||
zipFileName = $"{ release.Artist.Name }_{release.Title}.zip".ToFileNameFriendly();
|
||||
this.Logger.LogInformation($"User `{ roadieUser }` downloaded Release `{ release }` ZipFileName [{ zipFileName }], Zip Size [{ zipBytes?.Length }]");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Logger.LogError(ex, "Error creating zip for Release `{0}`", release.ToString());
|
||||
}
|
||||
return new FileOperationResult<byte[]>
|
||||
{
|
||||
IsSuccess = zipBytes != null,
|
||||
Data = zipBytes,
|
||||
AdditionalData = new Dictionary<string, object> {{ "ZipFileName", zipFileName }}
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<OperationResult<Release>> ReleaseByIdAction(Guid id, IEnumerable<string> includes = null)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace Roadie.Api.Services
|
|||
public const string SubsonicVersion = "1.16.1";
|
||||
|
||||
private IArtistService ArtistService { get; }
|
||||
private ICollectionService CollectionService { get; }
|
||||
private IImageService ImageService { get; }
|
||||
private IPlaylistService PlaylistService { get; }
|
||||
private IReleaseService ReleaseService { get; }
|
||||
|
@ -58,11 +59,12 @@ namespace Roadie.Api.Services
|
|||
: base(configuration, httpEncoder, context, cacheManager, logger, httpContext)
|
||||
{
|
||||
this.ArtistService = artistService;
|
||||
this.CollectionService = collectionService;
|
||||
this.ImageService = imageService;
|
||||
this.PlaylistService = playlistService;
|
||||
this.ReleaseService = releaseService;
|
||||
this.TrackService = trackService;
|
||||
this.ImageService = imageService;
|
||||
this.UserManger = userManager;
|
||||
this.PlaylistService = playlistService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -142,15 +144,6 @@ namespace Roadie.Api.Services
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a given media file. Similar to stream, but this method returns the original media data without transcoding or downsampling.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicFileOperationResult<subsonic.Response>> Download(subsonic.Request request, User roadieUser)
|
||||
{
|
||||
// TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetAlbum(subsonic.Request request, User roadieUser)
|
||||
{
|
||||
var releaseId = SafeParser.ToGuid(request.id);
|
||||
|
@ -400,20 +393,29 @@ namespace Roadie.Api.Services
|
|||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetArtists(subsonic.Request request, User roadieUser)
|
||||
{
|
||||
var indexes = new List<subsonic.IndexID3>();
|
||||
// Indexes for Artists alphabetically
|
||||
var musicFolder = this.MusicFolders().FirstOrDefault(x => x.id == (request.MusicFolderId ?? 2));
|
||||
var pagedRequest = request.PagedRequest;
|
||||
pagedRequest.SkipValue = 0;
|
||||
pagedRequest.Limit = int.MaxValue;
|
||||
pagedRequest.Sort = "Artist.Text";
|
||||
var artistList = await this.ArtistService.List(roadieUser, pagedRequest);
|
||||
foreach (var artistGroup in artistList.Rows.GroupBy(x => x.Artist.Text.Substring(0, 1)))
|
||||
if (musicFolder == this.CollectionMusicFolder())
|
||||
{
|
||||
indexes.Add(new subsonic.IndexID3
|
||||
// Indexes for "Collection" Artists alphabetically
|
||||
// not sure what to do here since this is albums not artists in a "Collection".
|
||||
}
|
||||
else
|
||||
{
|
||||
// Indexes for "Music" Artists alphabetically
|
||||
pagedRequest.SkipValue = 0;
|
||||
pagedRequest.Limit = int.MaxValue;
|
||||
pagedRequest.Sort = "Artist.Text";
|
||||
var artistList = await this.ArtistService.List(roadieUser, pagedRequest);
|
||||
foreach (var artistGroup in artistList.Rows.GroupBy(x => x.Artist.Text.Substring(0, 1)))
|
||||
{
|
||||
name = artistGroup.Key,
|
||||
artist = this.SubsonicArtistID3sForArtists(artistGroup)
|
||||
});
|
||||
};
|
||||
indexes.Add(new subsonic.IndexID3
|
||||
{
|
||||
name = artistGroup.Key,
|
||||
artist = this.SubsonicArtistID3sForArtists(artistGroup)
|
||||
});
|
||||
};
|
||||
}
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
{
|
||||
IsSuccess = true,
|
||||
|
@ -836,38 +838,11 @@ namespace Roadie.Api.Services
|
|||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetPlaylists(subsonic.Request request, User roadieUser, string filterToUserName)
|
||||
{
|
||||
//var playlists = (from playlist in this.DbContext.Playlists
|
||||
// join u in this.DbContext.Users on playlist.UserId equals u.Id
|
||||
// let trackCount = (from pl in this.DbContext.PlaylistTracks
|
||||
// where pl.PlayListId == playlist.Id
|
||||
// select pl.Id).Count()
|
||||
// let playListDuration = (from pl in this.DbContext.PlaylistTracks
|
||||
// join t in this.DbContext.Tracks on pl.TrackId equals t.Id
|
||||
// where pl.PlayListId == playlist.Id
|
||||
// select t.Duration).Sum()
|
||||
// where (playlist.IsPublic) || (roadieUser != null && playlist.UserId == roadieUser.Id)
|
||||
// select new subsonic.Playlist
|
||||
// {
|
||||
// id = playlist.RoadieId.ToString(),
|
||||
// name = playlist.Name,
|
||||
// comment = playlist.Description,
|
||||
// owner = u.UserName,
|
||||
// songCount = trackCount,
|
||||
// duration = playListDuration.ToSecondsFromMilliseconds(),
|
||||
// created = playlist.CreatedDate,
|
||||
// changed = playlist.LastUpdated ?? playlist.CreatedDate,
|
||||
// coverArt = this.MakePlaylistThumbnailImage(playlist.RoadieId).Url,
|
||||
// @public = playlist.IsPublic,
|
||||
// publicSpecified = playlist.IsPublic
|
||||
// }
|
||||
// );
|
||||
|
||||
var pagedRequest = request.PagedRequest;
|
||||
pagedRequest.Sort = "Playlist.Text";
|
||||
pagedRequest.Order = "ASC";
|
||||
var playlistResult = await this.PlaylistService.List(pagedRequest, roadieUser);
|
||||
|
||||
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
{
|
||||
IsSuccess = true,
|
||||
|
@ -1624,6 +1599,11 @@ namespace Roadie.Api.Services
|
|||
return this.MusicFolders().First(x => x.id == 1);
|
||||
}
|
||||
|
||||
private subsonic.MusicFolder MusicMusicFolder()
|
||||
{
|
||||
return this.MusicFolders().First(x => x.id == 2);
|
||||
}
|
||||
|
||||
private List<subsonic.MusicFolder> MusicFolders()
|
||||
{
|
||||
return new List<subsonic.MusicFolder>
|
||||
|
@ -1886,9 +1866,9 @@ namespace Roadie.Api.Services
|
|||
adminRole = isAdmin,
|
||||
avatarLastChanged = user.LastUpdated ?? user.CreatedDate ?? DateTime.UtcNow,
|
||||
avatarLastChangedSpecified = user.LastUpdated.HasValue,
|
||||
commentRole = false, // TODO set to yes when commenting is enabled
|
||||
commentRole = true,
|
||||
coverArtRole = isEditor || isAdmin,
|
||||
downloadRole = false, // Disable downloads
|
||||
downloadRole = isEditor || isAdmin, // Disable downloads
|
||||
email = user.Email,
|
||||
jukeboxRole = true,
|
||||
maxBitRate = 320,
|
||||
|
@ -1897,7 +1877,7 @@ namespace Roadie.Api.Services
|
|||
podcastRole = false, // Disable podcast nonsense
|
||||
scrobblingEnabled = false, // Disable scrobbling
|
||||
settingsRole = isAdmin,
|
||||
shareRole = false, // Disable sharing
|
||||
shareRole = false, // TODO enabled when sharing is implmeneted
|
||||
streamRole = true,
|
||||
uploadRole = true,
|
||||
username = user.UserName,
|
||||
|
|
|
@ -137,6 +137,18 @@ namespace Roadie.Library.Data
|
|||
return false;
|
||||
}
|
||||
|
||||
public int? ReleaseYear
|
||||
{
|
||||
get
|
||||
{
|
||||
if(this.ReleaseDate.HasValue)
|
||||
{
|
||||
return this.ReleaseDate.Value.Year;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return this releases file folder for the given artist folder
|
||||
/// </summary>
|
||||
|
@ -149,7 +161,7 @@ namespace Roadie.Library.Data
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Id [{0}], Title [{1}], Release Date [{2}]", this.Id, this.Title, this.ReleaseDate);
|
||||
return $"Id [{ this.Id }], Title [{ this.Title }], Release Date [{this.ReleaseYear}]";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -280,7 +280,7 @@ namespace Roadie.Library.Factories
|
|||
}
|
||||
}
|
||||
|
||||
this.Logger.LogInformation("Added New Release: [{0}]", release.ToString());
|
||||
this.Logger.LogInformation("Added New Release: `{0}`", release.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -521,7 +521,7 @@ namespace Roadie.Library.Factories
|
|||
sw.Stop();
|
||||
if (release == null || !release.IsValid)
|
||||
{
|
||||
this._logger.LogInformation("ReleaseFactory: Release Not Found For Artist [{0}] MetaData [{1}]", artist.ToString(), metaData.ToString());
|
||||
this._logger.LogInformation("ReleaseFactory: Release Not Found For Artist `{0}` MetaData [{1}]", artist.ToString(), metaData.ToString());
|
||||
if (doFindIfNotInDatabase)
|
||||
{
|
||||
OperationResult<Data.Release> releaseSearch = new OperationResult<Data.Release>();
|
||||
|
@ -1367,7 +1367,7 @@ namespace Roadie.Library.Factories
|
|||
var releaseDirectory = new DirectoryInfo(releasePath);
|
||||
if (!Directory.Exists(releasePath))
|
||||
{
|
||||
this.Logger.LogWarning("Unable To Find Release Folder [{0}] For Release [{1}]", releasePath, release.ToString());
|
||||
this.Logger.LogWarning("Unable To Find Release Folder [{0}] For Release `{1}`", releasePath, release.ToString());
|
||||
}
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
|
@ -1382,7 +1382,7 @@ namespace Roadie.Library.Factories
|
|||
|
||||
if (!File.Exists(trackPath))
|
||||
{
|
||||
this.Logger.LogWarning("Track [{0}], File [{1}] Not Found.", existingTrack.ToString(), trackPath);
|
||||
this.Logger.LogWarning("Track `{0}`, File [{1}] Not Found.", existingTrack.ToString(), trackPath);
|
||||
if (!doJustInfo)
|
||||
{
|
||||
existingTrack.UpdateTrackMissingFile(now);
|
||||
|
@ -1561,13 +1561,13 @@ namespace Roadie.Library.Factories
|
|||
}
|
||||
else
|
||||
{
|
||||
this.Logger.LogWarning("Release Track File Has Invalid MetaData [{0}]", audioMetaData.ToString());
|
||||
this.Logger.LogWarning("Release Track File Has Invalid MetaData `{0}`", audioMetaData.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Logger.LogWarning("Unable To Find Releaes Path [{0}] For Release [{1}]", releasePath, release.ToString());
|
||||
this.Logger.LogWarning("Unable To Find Releaes Path [{0}] For Release `{1}`", releasePath, release.ToString());
|
||||
}
|
||||
var releaseMediaNumbersFound = new List<short?>();
|
||||
foreach (var kp in releaseMediaTracksFound)
|
||||
|
@ -1633,7 +1633,7 @@ namespace Roadie.Library.Factories
|
|||
}
|
||||
|
||||
sw.Stop();
|
||||
this.Logger.LogInformation("Scanned Release [{0}] Folder [{1}], Modified Release [{2}], OperationTime [{3}]", release.ToString(), releasePath, modifiedRelease, sw.ElapsedMilliseconds);
|
||||
this.Logger.LogInformation("Scanned Release `{0}` Folder [{1}], Modified Release [{2}], OperationTime [{3}]", release.ToString(), releasePath, modifiedRelease, sw.ElapsedMilliseconds);
|
||||
result = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
Loading…
Reference in a new issue