mirror of
https://github.com/sphildreth/roadie
synced 2024-11-10 14:54:11 +00:00
Subsonic API Work. Removed some non dotnet core packages.
This commit is contained in:
parent
d8b096b543
commit
4a62537a42
16 changed files with 700 additions and 360 deletions
|
@ -87,6 +87,35 @@ namespace Roadie.Api.Controllers
|
|||
return this.BuildResponse(request, result);
|
||||
}
|
||||
|
||||
[HttpGet("deletePlaylist.view")]
|
||||
[HttpPost("deletePlaylist.view")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> DeletePlaylist(SubsonicRequest request)
|
||||
{
|
||||
var authResult = await this.AuthenticateUser(request);
|
||||
if (authResult != null)
|
||||
{
|
||||
return authResult;
|
||||
}
|
||||
var result = await this.SubsonicService.DeletePlaylist(request, this.SubsonicUser);
|
||||
return this.BuildResponse(request, result);
|
||||
}
|
||||
|
||||
[HttpGet("updatePlaylist.view")]
|
||||
[HttpPost("updatePlaylist.view")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> UpdatePlaylist(SubsonicRequest request, string playlistId, string name, string comment, bool? @public, string[] songIdToAdd, int[] songIndexToRemove)
|
||||
{
|
||||
var authResult = await this.AuthenticateUser(request);
|
||||
if (authResult != null)
|
||||
{
|
||||
return authResult;
|
||||
}
|
||||
var result = await this.SubsonicService.UpdatePlaylist(request, this.SubsonicUser, playlistId, name, comment, @public, songIdToAdd, songIndexToRemove);
|
||||
return this.BuildResponse(request, result);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("getBookmarks.view")]
|
||||
[HttpPost("getBookmarks.view")]
|
||||
[ProducesResponseType(200)]
|
||||
|
@ -364,11 +393,31 @@ namespace Roadie.Api.Controllers
|
|||
return this.BuildResponse(request, result, "playlist");
|
||||
}
|
||||
|
||||
[HttpGet("createPlaylist.view")]
|
||||
[HttpPost("createPlaylist.view")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> CreatePlaylist(SubsonicRequest request, string playlistId, string name, string[] songId)
|
||||
{
|
||||
var authResult = await this.AuthenticateUser(request);
|
||||
if (authResult != null)
|
||||
{
|
||||
return authResult;
|
||||
}
|
||||
var result = await this.SubsonicService.CreatePlaylist(request, this.SubsonicUser, name, songId, playlistId);
|
||||
return this.BuildResponse(request, result, "playlist");
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("getPlaylists.view")]
|
||||
[HttpPost("getPlaylists.view")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> GetPlaylists(SubsonicRequest request, string username)
|
||||
{
|
||||
var authResult = await this.AuthenticateUser(request);
|
||||
if (authResult != null)
|
||||
{
|
||||
return authResult;
|
||||
}
|
||||
var result = await this.SubsonicService.GetPlaylists(request, this.SubsonicUser, username);
|
||||
return this.BuildResponse(request, result, "playlists");
|
||||
}
|
||||
|
@ -378,6 +427,11 @@ namespace Roadie.Api.Controllers
|
|||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> GetPodcasts(SubsonicRequest request, bool includeEpisodes)
|
||||
{
|
||||
var authResult = await this.AuthenticateUser(request);
|
||||
if (authResult != null)
|
||||
{
|
||||
return authResult;
|
||||
}
|
||||
var result = await this.SubsonicService.GetPodcasts(request);
|
||||
return this.BuildResponse(request, result, "podcasts");
|
||||
}
|
||||
|
@ -483,14 +537,14 @@ namespace Roadie.Api.Controllers
|
|||
[HttpGet("getTopSongs.view")]
|
||||
[HttpPost("getTopSongs.view")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> GetTopSongs(SubsonicRequest request, string artist, int? count = 50)
|
||||
public async Task<IActionResult> GetTopSongs(SubsonicRequest request, int? count = 50)
|
||||
{
|
||||
var authResult = await this.AuthenticateUser(request);
|
||||
if (authResult != null)
|
||||
{
|
||||
return authResult;
|
||||
}
|
||||
var result = await this.SubsonicService.GetTopSongs(request, this.SubsonicUser, artist, count);
|
||||
var result = await this.SubsonicService.GetTopSongs(request, this.SubsonicUser, count);
|
||||
return this.BuildResponse(request, result, "topSongs");
|
||||
}
|
||||
|
||||
|
@ -667,7 +721,7 @@ namespace Roadie.Api.Controllers
|
|||
}
|
||||
postBody = JsonConvert.SerializeObject(formDictionary);
|
||||
}
|
||||
this.Logger.LogTrace($"Subsonic Request: Method [{ method }], Accept Header [{ acceptHeader }], Path [{ queryPath }], Query String [{ queryString }], Posted Body [{ postBody }], Response Error Code [{ response?.ErrorCode }], Request [{ JsonConvert.SerializeObject(request) }] ResponseType [{ responseType }]");
|
||||
this.Logger.LogTrace($"Subsonic Request: Method [{ method }], Accept Header [{ acceptHeader }], Path [{ queryPath }], Query String [{ queryString }], Posted Body [{ postBody }], Response Error Code [{ response?.ErrorCode }], Request [{ JsonConvert.SerializeObject(request, Formatting.Indented) }] ResponseType [{ responseType }]");
|
||||
if (response?.ErrorCode.HasValue ?? false)
|
||||
{
|
||||
return this.SendError(request, response);
|
||||
|
|
|
@ -25,34 +25,36 @@ namespace Roadie.Api.ModelBinding
|
|||
// Create a dictionary of all the properties to populate on the result model
|
||||
var modelDictionary = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
{ "u", null },
|
||||
{ "p", null },
|
||||
{ "s", null },
|
||||
{ "t", null },
|
||||
{ "v", null },
|
||||
{ "c", null },
|
||||
{ "id", null },
|
||||
{ "f", null },
|
||||
{ "callback", null },
|
||||
{ "musicFolderId", null },
|
||||
{ "albumCount", null },
|
||||
{ "albumOffset", null },
|
||||
{ "artist", null },
|
||||
{ "artistCount", null },
|
||||
{ "artistOffset", null },
|
||||
{ "c", null },
|
||||
{ "callback", null },
|
||||
{ "f", null },
|
||||
{ "fromYear", null },
|
||||
{ "genre", null },
|
||||
{ "id", null },
|
||||
{ "musicFolderId", null },
|
||||
{ "offset", null },
|
||||
{ "p", null },
|
||||
{ "query", null },
|
||||
{ "s", null },
|
||||
{ "size", null },
|
||||
{ "songCount", null },
|
||||
{ "songOffset", null },
|
||||
{ "t", null },
|
||||
{ "toYear", null },
|
||||
{ "type", null }
|
||||
{ "type", null },
|
||||
{ "u", null },
|
||||
{ "v", null }
|
||||
};
|
||||
|
||||
// Setup model dictionary from Query Parameters
|
||||
modelDictionary["albumCount"] = queryDictionary.ContainsKey("albumCount") ? SafeParser.ToNumber<int?>(queryDictionary["albumCount"].First()) : null;
|
||||
modelDictionary["albumOffset"] = queryDictionary.ContainsKey("albumOffset") ? SafeParser.ToNumber<int?>(queryDictionary["albumOffset"].First()) : null;
|
||||
modelDictionary["artist"] = queryDictionary.ContainsKey("artist") ? queryDictionary["artist"].First() : null;
|
||||
modelDictionary["artistCount"] = queryDictionary.ContainsKey("artistCount") ? SafeParser.ToNumber<int?>(queryDictionary["artistCount"].First()) : null;
|
||||
modelDictionary["artistOffset"] = queryDictionary.ContainsKey("artistOffset") ? SafeParser.ToNumber<int?>(queryDictionary["artistOffset"].First()) : null;
|
||||
modelDictionary["c"] = queryDictionary.ContainsKey("c") ? queryDictionary["c"].First() : null;
|
||||
|
@ -102,6 +104,7 @@ namespace Roadie.Api.ModelBinding
|
|||
AlbumCount = SafeParser.ToNumber<int?>(modelDictionary["albumCount"]) ?? 20,
|
||||
AlbumOffset = SafeParser.ToNumber<int?>(modelDictionary["albumOffset"]),
|
||||
ArtistCount = SafeParser.ToNumber<int?>(modelDictionary["artistCount"]) ?? 20,
|
||||
ArtistName = SafeParser.ToString(modelDictionary["artist"]),
|
||||
ArtistOffset = SafeParser.ToNumber<int?>(modelDictionary["artistOffset"]),
|
||||
c = SafeParser.ToString(modelDictionary["c"]),
|
||||
callback = SafeParser.ToString(modelDictionary["callback"]),
|
||||
|
|
22
RoadieApi/Properties/PublishProfiles/FolderProfile.pubxml
Normal file
22
RoadieApi/Properties/PublishProfiles/FolderProfile.pubxml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
|
||||
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<WebPublishMethod>FileSystem</WebPublishMethod>
|
||||
<PublishProvider>FileSystem</PublishProvider>
|
||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||
<LastUsedPlatform>x64</LastUsedPlatform>
|
||||
<SiteUrlToLaunchAfterPublish />
|
||||
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
|
||||
<ExcludeApp_Data>False</ExcludeApp_Data>
|
||||
<ProjectGuid>68c80416-0d72-409d-b727-3fea7ab7fd2c</ProjectGuid>
|
||||
<publishUrl>bin\x64\Debug\netcoreapp2.1\publish\</publishUrl>
|
||||
<DeleteExistingFiles>False</DeleteExistingFiles>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<SelfContained>false</SelfContained>
|
||||
<_IsPortable>true</_IsPortable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -25,7 +25,6 @@
|
|||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.RollingFileAlternate" Version="2.0.9" />
|
||||
<PackageReference Include="Serilog.Sinks.SQLite" Version="4.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.3.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.9" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -308,7 +308,7 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
public async Task<Library.Models.Pagination.PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false)
|
||||
public async Task<Library.Models.Pagination.PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true, bool? doArtistCounts = true)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
@ -322,8 +322,9 @@ namespace Roadie.Api.Services
|
|||
select a.Id
|
||||
).ToArray();
|
||||
}
|
||||
|
||||
var onlyWithReleases = onlyIncludeWithReleases ?? true;
|
||||
var result = (from a in this.DbContext.Artists
|
||||
where (!onlyWithReleases || a.ReleaseCount > 0)
|
||||
where (request.FilterToArtistId == null || a.RoadieId == request.FilterToArtistId)
|
||||
where (request.FilterMinimumRating == null || a.Rating >= request.FilterMinimumRating.Value)
|
||||
where (request.FilterValue == "" || (a.Name.Contains(request.FilterValue) || a.SortName.Contains(request.FilterValue) || a.AlternateNames.Contains(request.FilterValue)))
|
||||
|
@ -367,6 +368,27 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
rows = result.OrderBy(sortBy).Skip(request.SkipValue).Take(request.LimitValue).ToArray();
|
||||
}
|
||||
if(rows.Any() && (doArtistCounts ?? true))
|
||||
{
|
||||
var rowArtistIds = rows.Select(x => x.DatabaseId);
|
||||
var artistReleases = (from a in this.DbContext.Artists
|
||||
join r in this.DbContext.Releases on a.Id equals r.ArtistId
|
||||
where a.ReleaseCount > 0
|
||||
where r.TrackCount > 0
|
||||
where rowArtistIds.Contains(a.Id)
|
||||
select new
|
||||
{
|
||||
r.Id,
|
||||
r.TrackCount,
|
||||
r.PlayedCount
|
||||
}).ToList();
|
||||
foreach(var row in rows)
|
||||
{
|
||||
row.ArtistReleaseCount = artistReleases.Where(r => r.Id == row.DatabaseId).Select(r => r.Id).Count();
|
||||
row.ArtistTrackCount = artistReleases.Where(r => r.Id == row.DatabaseId).Sum(r => r.TrackCount);
|
||||
row.ArtistPlayedCount = artistReleases.Where(r => r.Id == row.DatabaseId).Sum(r => r.PlayedCount);
|
||||
}
|
||||
}
|
||||
if (rows.Any() && roadieUser != null)
|
||||
{
|
||||
foreach (var userArtistRating in this.GetUser(roadieUser.UserId).ArtistRatings.Where(x => rows.Select(r => r.DatabaseId).Contains(x.ArtistId)))
|
||||
|
|
|
@ -12,6 +12,6 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
Task<OperationResult<Artist>> ById(User roadieUser, Guid id, IEnumerable<string> includes);
|
||||
|
||||
Task<PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false);
|
||||
Task<PagedResult<ArtistList>> List(User roadieUser, PagedRequest request, bool? doRandomize = false, bool? onlyIncludeWithReleases = true, bool? doArtistCounts = true);
|
||||
}
|
||||
}
|
|
@ -9,8 +9,10 @@ namespace Roadie.Api.Services
|
|||
Task<SubsonicOperationResult<SubsonicAuthenticateResponse>> Authenticate(Request request);
|
||||
|
||||
Task<SubsonicOperationResult<Response>> CreateBookmark(Request request, Roadie.Library.Models.Users.User roadieUser, int position, string comment);
|
||||
Task<SubsonicOperationResult<Response>> CreatePlaylist(Request request, Roadie.Library.Models.Users.User roadieUser, string name, string[] songIds, string playlistId = null);
|
||||
|
||||
Task<SubsonicOperationResult<Response>> DeleteBookmark(Request request, Roadie.Library.Models.Users.User roadieUser);
|
||||
Task<SubsonicOperationResult<Response>> DeletePlaylist(Request request, Roadie.Library.Models.Users.User roadieUser);
|
||||
|
||||
Task<SubsonicOperationResult<Response>> GetAlbum(Request request, Roadie.Library.Models.Users.User roadieUser);
|
||||
|
||||
|
@ -56,7 +58,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
Task<SubsonicOperationResult<Response>> GetStarred(Request request, Roadie.Library.Models.Users.User roadieUser, StarredVersion version);
|
||||
|
||||
Task<SubsonicOperationResult<Response>> GetTopSongs(Request request, Roadie.Library.Models.Users.User roadieUser, string artistName, int? count = 50);
|
||||
Task<SubsonicOperationResult<Response>> GetTopSongs(Request request, Roadie.Library.Models.Users.User roadieUser, int? count = 50);
|
||||
|
||||
Task<SubsonicOperationResult<Response>> GetUser(Request request, string username);
|
||||
|
||||
|
@ -68,5 +70,7 @@ namespace Roadie.Api.Services
|
|||
|
||||
Task<SubsonicOperationResult<Response>> ToggleStar(Request request, Roadie.Library.Models.Users.User roadieUser, bool star, string[] albumIds = null, string[] artistIds = null);
|
||||
Task<SubsonicOperationResult<Response>> SetRating(Request request, Roadie.Library.Models.Users.User roadieUser, short rating);
|
||||
|
||||
Task<SubsonicOperationResult<Response>> UpdatePlaylist(Request request, Roadie.Library.Models.Users.User roadieUser, string playlistId, string name = null, string comment = null, bool? isPublic = null, string[] songIdsToAdd = null, int[] songIndexesToRemove = null);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using Mapster;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Caching;
|
||||
|
@ -129,7 +130,7 @@ namespace Roadie.Api.Services
|
|||
this.Logger.LogInformation($"Unknown User [{ request.u }]");
|
||||
return new subsonic.SubsonicOperationResult<subsonic.SubsonicAuthenticateResponse>(subsonic.ErrorCodes.WrongUsernameOrPassword, $"Unknown Username");
|
||||
}
|
||||
this.Logger.LogInformation($"Successfully Authenticated User [{ user.ToString() }]");
|
||||
this.Logger.LogInformation($"Subsonic: Successfully Authenticated User [{ user.ToString() }] via Application [{ request.c }], Application Version [{ request.v }]");
|
||||
return new subsonic.SubsonicOperationResult<subsonic.SubsonicAuthenticateResponse>
|
||||
{
|
||||
IsSuccess = true,
|
||||
|
@ -146,6 +147,94 @@ namespace Roadie.Api.Services
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or updates a bookmark (a position within a media file). Bookmarks are personal and not visible to other users.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> CreateBookmark(subsonic.Request request, User roadieUser, int position, string comment)
|
||||
{
|
||||
if (!request.TrackId.HasValue)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.id }]");
|
||||
}
|
||||
var track = this.GetTrack(request.TrackId.Value);
|
||||
if (track == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.TrackId.Value }]");
|
||||
}
|
||||
var userBookmark = this.DbContext.Bookmarks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == Library.Enums.BookmarkType.Track);
|
||||
var createdBookmark = false;
|
||||
if (userBookmark == null)
|
||||
{
|
||||
userBookmark = new data.Bookmark
|
||||
{
|
||||
BookmarkTargetId = track.Id,
|
||||
BookmarkType = Library.Enums.BookmarkType.Track,
|
||||
UserId = roadieUser.Id,
|
||||
Comment = comment,
|
||||
Position = position
|
||||
};
|
||||
this.DbContext.Bookmarks.Add(userBookmark);
|
||||
createdBookmark = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
userBookmark.LastUpdated = DateTime.UtcNow;
|
||||
userBookmark.Position = position;
|
||||
userBookmark.Comment = comment;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
|
||||
this.Logger.LogInformation($"{ (createdBookmark ? "Created" : "Updated") } Bookmark `{ userBookmark}` for User `{ roadieUser }`");
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = new subsonic.Response
|
||||
{
|
||||
version = SubsonicService.SubsonicVersion,
|
||||
status = subsonic.ResponseStatus.ok
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the bookmark for a given file.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> DeleteBookmark(subsonic.Request request, User roadieUser)
|
||||
{
|
||||
if (!request.TrackId.HasValue)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.id }]");
|
||||
}
|
||||
var track = this.GetTrack(request.TrackId.Value);
|
||||
if (track == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.TrackId.Value }]");
|
||||
}
|
||||
var userBookmark = this.DbContext.Bookmarks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == Library.Enums.BookmarkType.Track);
|
||||
if (userBookmark != null)
|
||||
{
|
||||
this.DbContext.Bookmarks.Remove(userBookmark);
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
|
||||
this.Logger.LogInformation($"Subsonic: Deleted Bookmark `{ userBookmark}` for User `{ roadieUser }`");
|
||||
}
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = new subsonic.Response
|
||||
{
|
||||
version = SubsonicService.SubsonicVersion,
|
||||
status = subsonic.ResponseStatus.ok
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns details for an album, including a list of songs. This method organizes music according to ID3 tags.
|
||||
/// </summary>
|
||||
|
@ -414,7 +503,11 @@ namespace Roadie.Api.Services
|
|||
pagedRequest.SkipValue = 0;
|
||||
pagedRequest.Limit = int.MaxValue;
|
||||
pagedRequest.Sort = "Artist.Text";
|
||||
var artistList = await this.ArtistService.List(roadieUser, pagedRequest);
|
||||
var artistList = await this.ArtistService.List(roadieUser: roadieUser,
|
||||
request:pagedRequest,
|
||||
doRandomize: false,
|
||||
onlyIncludeWithReleases: true,
|
||||
doArtistCounts: false);
|
||||
foreach (var artistGroup in artistList.Rows.GroupBy(x => x.Artist.Text.Substring(0, 1)))
|
||||
{
|
||||
indexes.Add(new subsonic.IndexID3
|
||||
|
@ -440,6 +533,33 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all bookmarks for this user. A bookmark is a position within a certain media file.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetBookmarks(subsonic.Request request, User roadieUser)
|
||||
{
|
||||
var pagedRequest = request.PagedRequest;
|
||||
pagedRequest.Sort = "LastUpdated";
|
||||
pagedRequest.Order = "DESC";
|
||||
var userBookmarkResult = await this.BookmarkService.List(roadieUser, pagedRequest, false, Library.Enums.BookmarkType.Track);
|
||||
pagedRequest.FilterToTrackIds = userBookmarkResult.Rows.Select(x => SafeParser.ToGuid(x.Bookmark.Value)).ToArray();
|
||||
var trackListResult = await this.TrackService.List(roadieUser, pagedRequest);
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = new subsonic.Response
|
||||
{
|
||||
version = SubsonicService.SubsonicVersion,
|
||||
status = subsonic.ResponseStatus.ok,
|
||||
ItemElementName = subsonic.ItemChoiceType.bookmarks,
|
||||
Item = new subsonic.Bookmarks
|
||||
{
|
||||
bookmark = this.SubsonicBookmarksForBookmarks(userBookmarkResult.Rows, trackListResult.Rows)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cover art image.
|
||||
/// </summary>
|
||||
|
@ -569,14 +689,11 @@ namespace Roadie.Api.Services
|
|||
/// <param name="ifModifiedSince">If specified, only return a result if the artist collection has changed since the given time (in milliseconds since 1 Jan 1970).</param>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetIndexes(subsonic.Request request, User roadieUser, long? ifModifiedSince = null)
|
||||
{
|
||||
if (roadieUser != null)
|
||||
{
|
||||
return await this.GetIndexesAction(request, roadieUser, ifModifiedSince);
|
||||
}
|
||||
var cacheKey = string.Format("urn:subsonic_indexes");
|
||||
return await this.CacheManager.GetAsync<subsonic.SubsonicOperationResult<subsonic.Response>>(cacheKey, async () =>
|
||||
{
|
||||
return await this.GetIndexesAction(request, roadieUser, ifModifiedSince);
|
||||
// Dont send the user to get index list as user data (likes, dislikes, etc.) aren't used in this list and dont need performance hit
|
||||
return await this.GetIndexesAction(request, null, ifModifiedSince);
|
||||
}, CacheManagerBase.SystemCacheRegionUrn);
|
||||
}
|
||||
|
||||
|
@ -1008,12 +1125,20 @@ namespace Roadie.Api.Services
|
|||
/// <summary>
|
||||
/// Returns top songs for the given artist, using data from last.fm.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetTopSongs(subsonic.Request request, User roadieUser, string artistName, int? count = 50)
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetTopSongs(subsonic.Request request, User roadieUser, int? count = 50)
|
||||
{
|
||||
var artist = base.GetArtist(artistName);
|
||||
data.Artist artist = null;
|
||||
if (!string.IsNullOrEmpty(request.ArtistName))
|
||||
{
|
||||
artist = base.GetArtist(request.ArtistName);
|
||||
}
|
||||
else if(request.ArtistId.HasValue)
|
||||
{
|
||||
artist = this.GetArtist(request.ArtistId.Value);
|
||||
}
|
||||
if (artist == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Unknown Artist [{ artistName }]");
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Unknown Artist [{ request.ArtistName }]");
|
||||
}
|
||||
var pagedRequest = request.PagedRequest;
|
||||
pagedRequest.FilterToArtistId = artist.RoadieId;
|
||||
|
@ -1308,73 +1433,117 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all bookmarks for this user. A bookmark is a position within a certain media file.
|
||||
/// Creates (or updates) a playlist.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetBookmarks(subsonic.Request request, User roadieUser)
|
||||
/// <param name="request">Populated Subsonic Request</param>
|
||||
/// <param name="roadieUser">Populated Roadie User</param>
|
||||
/// <param name="name">The human-readable name of the playlist.</param>
|
||||
/// <param name="songIds">ID of a song in the playlist. Use one songId parameter for each song in the playlist.</param>
|
||||
/// <param name="playlistId">The playlist ID. (if updating else blank is adding)</param>
|
||||
/// <returns></returns>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> CreatePlaylist(subsonic.Request request, User roadieUser, string name, string[] songIds, string playlistId = null)
|
||||
{
|
||||
var pagedRequest = request.PagedRequest;
|
||||
pagedRequest.Sort = "LastUpdated";
|
||||
pagedRequest.Order = "DESC";
|
||||
var userBookmarkResult = await this.BookmarkService.List(roadieUser, pagedRequest, false, Library.Enums.BookmarkType.Track);
|
||||
pagedRequest.FilterToTrackIds = userBookmarkResult.Rows.Select(x => SafeParser.ToGuid(x.Bookmark.Value)).ToArray();
|
||||
var trackListResult = await this.TrackService.List(roadieUser, pagedRequest);
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
data.Playlist playlist = null;
|
||||
|
||||
Guid?[] songRoadieIds = new Guid?[0];
|
||||
IQueryable<data.Track> submittedTracks = new data.Track[0].AsQueryable();
|
||||
|
||||
if (songIds != null && songIds.Any())
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = new subsonic.Response
|
||||
songRoadieIds = songIds.Select(x => SafeParser.ToGuid(x)).ToArray();
|
||||
// Add (if not already) given tracks to Playlist
|
||||
submittedTracks = (from t in this.DbContext.Tracks
|
||||
where songRoadieIds.Contains(t.RoadieId)
|
||||
select t);
|
||||
}
|
||||
var didCreate = false;
|
||||
if (!string.IsNullOrEmpty(playlistId))
|
||||
{
|
||||
request.id = playlistId;
|
||||
playlist = this.DbContext.Playlists.Include(x => x.Tracks).FirstOrDefault(x => x.RoadieId == request.PlaylistId);
|
||||
if(playlist == null)
|
||||
{
|
||||
version = SubsonicService.SubsonicVersion,
|
||||
status = subsonic.ResponseStatus.ok,
|
||||
ItemElementName = subsonic.ItemChoiceType.bookmarks,
|
||||
Item = new subsonic.Bookmarks
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid PlaylistId [{ playlistId }]");
|
||||
}
|
||||
// When Create is called again on an existing delete all existing tracks and add given
|
||||
if (playlist.Tracks != null && playlist.Tracks.Any())
|
||||
{
|
||||
this.DbContext.PlaylistTracks.RemoveRange(playlist.Tracks);
|
||||
}
|
||||
var listNumber = playlist.Tracks != null && playlist.Tracks.Any() ? playlist.Tracks?.Max(x => x.ListNumber) ?? 0 : 0;
|
||||
foreach (var submittedTrack in submittedTracks)
|
||||
{
|
||||
if (playlist.Tracks == null || !playlist.Tracks.Any(x => x.TrackId == submittedTrack.Id))
|
||||
{
|
||||
bookmark = this.SubsonicBookmarksForBookmarks(userBookmarkResult.Rows, trackListResult.Rows)
|
||||
listNumber++;
|
||||
this.DbContext.PlaylistTracks.Add(new data.PlaylistTrack
|
||||
{
|
||||
PlayListId = playlist.Id,
|
||||
ListNumber = listNumber,
|
||||
TrackId = submittedTrack.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or updates a bookmark (a position within a media file). Bookmarks are personal and not visible to other users.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> CreateBookmark(subsonic.Request request, User roadieUser, int position, string comment)
|
||||
{
|
||||
if(!request.TrackId.HasValue)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.id }]");
|
||||
}
|
||||
var track = this.GetTrack(request.TrackId.Value);
|
||||
if(track == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.TrackId.Value }]");
|
||||
}
|
||||
var userBookmark = this.DbContext.Bookmarks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == Library.Enums.BookmarkType.Track);
|
||||
var createdBookmark = false;
|
||||
if(userBookmark == null)
|
||||
{
|
||||
userBookmark = new data.Bookmark
|
||||
{
|
||||
BookmarkTargetId = track.Id,
|
||||
BookmarkType = Library.Enums.BookmarkType.Track,
|
||||
UserId = roadieUser.Id,
|
||||
Comment = comment,
|
||||
Position = position
|
||||
};
|
||||
this.DbContext.Bookmarks.Add(userBookmark);
|
||||
createdBookmark = true;
|
||||
playlist.Name = name ?? playlist.Name;
|
||||
playlist.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
userBookmark.LastUpdated = DateTime.UtcNow;
|
||||
userBookmark.Position = position;
|
||||
userBookmark.Comment = comment;
|
||||
var tracks = new List<data.PlaylistTrack>();
|
||||
var listNumber = 0;
|
||||
foreach (var submittedTrack in submittedTracks)
|
||||
{
|
||||
listNumber++;
|
||||
tracks.Add(new data.PlaylistTrack
|
||||
{
|
||||
PlayListId = playlist.Id,
|
||||
ListNumber = listNumber,
|
||||
TrackId = submittedTrack.Id
|
||||
});
|
||||
}
|
||||
|
||||
playlist = new data.Playlist
|
||||
{
|
||||
IsPublic = false,
|
||||
Name = name,
|
||||
UserId = roadieUser.Id,
|
||||
Tracks = tracks
|
||||
};
|
||||
didCreate = true;
|
||||
this.DbContext.Playlists.Add(playlist);
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
this.Logger.LogInformation($"Subsonic: User `{ roadieUser }` { (didCreate ? "created": "modified") } Playlist `{ playlist }` added [{ songRoadieIds.Count() }] Tracks.");
|
||||
request.id = subsonic.Request.PlaylistdIdentifier + playlist.RoadieId.ToString();
|
||||
return await this.GetPlaylist(request, roadieUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a saved playlist.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> DeletePlaylist(subsonic.Request request, User roadieUser)
|
||||
{
|
||||
if (!request.PlaylistId.HasValue)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.id }]");
|
||||
}
|
||||
var playlist = this.GetPlaylist(request.PlaylistId.Value);
|
||||
if (playlist == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.TrackId.Value }]");
|
||||
}
|
||||
if(playlist.UserId != roadieUser.Id && !roadieUser.IsAdmin)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, "User is not allowed to delete playlist.");
|
||||
}
|
||||
this.DbContext.Playlists.Remove(playlist);
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
|
||||
this.Logger.LogInformation($"{ (createdBookmark ? "Created" : "Updated") } Bookmark `{ userBookmark}` for User `{ roadieUser }`");
|
||||
this.Logger.LogInformation($"Subsonic: Deleted Playlist `{ playlist}` for User `{ roadieUser }`");
|
||||
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
{
|
||||
IsSuccess = true,
|
||||
|
@ -1387,31 +1556,82 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the bookmark for a given file.
|
||||
/// Updates a playlist. Only the owner of a playlist is allowed to update it.
|
||||
/// </summary>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> DeleteBookmark(subsonic.Request request, User roadieUser)
|
||||
/// <param name="request">Populated Subsonic Request</param>
|
||||
/// <param name="roadieUser">Populated Roadie User</param>
|
||||
/// <param name="name">The human-readable name of the playlist.</param>
|
||||
/// <param name="comment">The playlist comment.</param>
|
||||
/// <param name="isPublic">true if the playlist should be visible to all users, false otherwise.</param>
|
||||
/// <param name="songIdsToAdd">Add this song with this ID to the playlist. Multiple parameters allowed</param>
|
||||
/// <param name="songIndexesToRemove">Remove the song at this position in the playlist. Multiple parameters allowed.</param>
|
||||
public async Task<subsonic.SubsonicOperationResult<subsonic.Response>> UpdatePlaylist(subsonic.Request request, User roadieUser, string playListId, string name = null, string comment =null, bool? isPublic = null, string[] songIdsToAdd = null, int[] songIndexesToRemove = null)
|
||||
{
|
||||
if (!request.TrackId.HasValue)
|
||||
request.id = playListId ?? request.id;
|
||||
if (!request.PlaylistId.HasValue)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.id }]");
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.id }]");
|
||||
}
|
||||
var track = this.GetTrack(request.TrackId.Value);
|
||||
if (track == null)
|
||||
var playlist = this.GetPlaylist(request.PlaylistId.Value);
|
||||
if (playlist == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ request.TrackId.Value }]");
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Playlist Id [{ request.TrackId.Value }]");
|
||||
}
|
||||
var userBookmark = this.DbContext.Bookmarks.FirstOrDefault(x => x.UserId == roadieUser.Id && x.BookmarkTargetId == track.Id && x.BookmarkType == Library.Enums.BookmarkType.Track);
|
||||
if (userBookmark != null)
|
||||
if (playlist.UserId != roadieUser.Id && !roadieUser.IsAdmin)
|
||||
{
|
||||
this.DbContext.Bookmarks.Remove(userBookmark);
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
|
||||
this.Logger.LogInformation($"Deleted Bookmark `{ userBookmark}` for User `{ roadieUser }`");
|
||||
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>(subsonic.ErrorCodes.UserIsNotAuthorizedForGivenOperation, "User is not allowed to update playlist.");
|
||||
}
|
||||
|
||||
playlist.Name = name ?? playlist.Name;
|
||||
playlist.IsPublic = isPublic ?? playlist.IsPublic;
|
||||
playlist.LastUpdated = DateTime.UtcNow;
|
||||
|
||||
if(songIdsToAdd != null && songIdsToAdd.Any())
|
||||
{
|
||||
// Add new if not already on Playlist
|
||||
var songIdsToAddRoadieIds = songIdsToAdd.Select(x => SafeParser.ToGuid(x)).ToArray();
|
||||
var submittedTracks = (from t in this.DbContext.Tracks
|
||||
where songIdsToAddRoadieIds.Contains(t.RoadieId)
|
||||
select t);
|
||||
|
||||
var listNumber = playlist.Tracks?.Max(x => x.ListNumber) ?? 0;
|
||||
foreach (var submittedTrack in submittedTracks)
|
||||
{
|
||||
if (playlist.Tracks == null || playlist.Tracks == null || !playlist.Tracks.Any(x => x.TrackId == submittedTrack.Id))
|
||||
{
|
||||
listNumber++;
|
||||
this.DbContext.PlaylistTracks.Add(new data.PlaylistTrack
|
||||
{
|
||||
PlayListId = playlist.Id,
|
||||
ListNumber = listNumber,
|
||||
TrackId = submittedTrack.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if(songIndexesToRemove != null && songIndexesToRemove.Any())
|
||||
{
|
||||
// Remove tracks from playlist
|
||||
//foreach (var submittedTrack in submittedTracks)
|
||||
//{
|
||||
// if (playlist.Tracks == null || !playlist.Tracks.Any(x => x.TrackId == submittedTrack.Id))
|
||||
// {
|
||||
// listNumber++;
|
||||
// this.DbContext.PlaylistTracks.Add(new data.PlaylistTrack
|
||||
// {
|
||||
// PlayListId = playlist.Id,
|
||||
// ListNumber = listNumber,
|
||||
// TrackId = submittedTrack.Id
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<subsonic.Response>
|
||||
{
|
||||
IsSuccess = true,
|
||||
|
@ -1426,6 +1646,19 @@ namespace Roadie.Api.Services
|
|||
|
||||
#region Privates
|
||||
|
||||
private string[] AllowedUsers()
|
||||
{
|
||||
return this.CacheManager.Get<string[]>(CacheManagerBase.SystemCacheRegionUrn + ":active_usernames", () =>
|
||||
{
|
||||
return this.DbContext.Users.Where(x => x.IsActive ?? false).Select(x => x.UserName).ToArray();
|
||||
}, CacheManagerBase.SystemCacheRegionUrn);
|
||||
}
|
||||
|
||||
private subsonic.MusicFolder CollectionMusicFolder()
|
||||
{
|
||||
return this.MusicFolders().First(x => x.id == 1);
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<subsonic.Response>> GetIndexesAction(subsonic.Request request, User roadieUser, long? ifModifiedSince = null)
|
||||
{
|
||||
var modifiedSinceFilter = ifModifiedSince.HasValue ? (DateTime?)ifModifiedSince.Value.FromUnixTime() : null;
|
||||
|
@ -1465,13 +1698,17 @@ namespace Roadie.Api.Services
|
|||
pagedRequest.SkipValue = 0;
|
||||
pagedRequest.Limit = int.MaxValue;
|
||||
pagedRequest.Sort = "Artist.Text";
|
||||
var artistList = await this.ArtistService.List(roadieUser, pagedRequest);
|
||||
var artistList = await this.ArtistService.List(roadieUser:roadieUser,
|
||||
request: pagedRequest,
|
||||
doRandomize: false,
|
||||
onlyIncludeWithReleases: true,
|
||||
doArtistCounts: false);
|
||||
foreach (var artistGroup in artistList.Rows.GroupBy(x => x.Artist.Text.Substring(0, 1)))
|
||||
{
|
||||
indexes.Add(new subsonic.Index
|
||||
{
|
||||
name = artistGroup.Key,
|
||||
artist = this.SubsonicArtistsForArtists(artistGroup)
|
||||
artist = this.SubsonicArtistsForArtists(artistGroup)
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -1485,13 +1722,27 @@ namespace Roadie.Api.Services
|
|||
ItemElementName = subsonic.ItemChoiceType.indexes,
|
||||
Item = new subsonic.Indexes
|
||||
{
|
||||
lastModified = DateTime.UtcNow.ToUnixTime(),
|
||||
index = indexes.ToArray()
|
||||
// TODO child
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private List<subsonic.MusicFolder> MusicFolders()
|
||||
{
|
||||
return new List<subsonic.MusicFolder>
|
||||
{
|
||||
new subsonic.MusicFolder { id = 1, name = "Collections"},
|
||||
new subsonic.MusicFolder { id = 2, name = "Music"}
|
||||
};
|
||||
}
|
||||
|
||||
private subsonic.MusicFolder MusicMusicFolder()
|
||||
{
|
||||
return this.MusicFolders().First(x => x.id == 2);
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> SetArtistRating(Guid artistId, ApplicationUser user, short rating)
|
||||
{
|
||||
var artist = this.GetArtist(artistId);
|
||||
|
@ -1600,177 +1851,6 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> ToggleArtistStar(Guid artistId, ApplicationUser user, bool starred)
|
||||
{
|
||||
var artist = this.GetArtist(artistId);
|
||||
if (artist == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Artist Id [{ artistId }]");
|
||||
}
|
||||
var userArtist = user.ArtistRatings.FirstOrDefault(x => x.ArtistId == artist.Id);
|
||||
if (userArtist == null)
|
||||
{
|
||||
userArtist = new data.UserArtist
|
||||
{
|
||||
IsFavorite = true,
|
||||
UserId = user.Id,
|
||||
ArtistId = artist.Id
|
||||
};
|
||||
this.DbContext.UserArtists.Add(userArtist);
|
||||
}
|
||||
else
|
||||
{
|
||||
userArtist.IsFavorite = starred;
|
||||
userArtist.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> ToggleReleaseStar(Guid releaseId, ApplicationUser user, bool starred)
|
||||
{
|
||||
var release = this.GetRelease(releaseId);
|
||||
if (release == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release Id [{ releaseId }]");
|
||||
}
|
||||
var userRelease = user.ReleaseRatings.FirstOrDefault(x => x.ReleaseId == release.Id);
|
||||
if (userRelease == null)
|
||||
{
|
||||
userRelease = new data.UserRelease
|
||||
{
|
||||
IsFavorite = true,
|
||||
UserId = user.Id,
|
||||
ReleaseId = release.Id
|
||||
};
|
||||
this.DbContext.UserReleases.Add(userRelease);
|
||||
}
|
||||
else
|
||||
{
|
||||
userRelease.IsFavorite = starred;
|
||||
userRelease.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.Artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> ToggleTrackStar(Guid trackId, ApplicationUser user, bool starred)
|
||||
{
|
||||
var track = this.GetTrack(trackId);
|
||||
if (track == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ trackId }]");
|
||||
}
|
||||
var userTrack = user.TrackRatings.FirstOrDefault(x => x.TrackId == track.Id);
|
||||
if (userTrack == null)
|
||||
{
|
||||
userTrack = new data.UserTrack
|
||||
{
|
||||
IsFavorite = true,
|
||||
UserId = user.Id,
|
||||
TrackId = track.Id
|
||||
};
|
||||
this.DbContext.UserTracks.Add(userTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
userTrack.IsFavorite = starred;
|
||||
userTrack.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
private string[] AllowedUsers()
|
||||
{
|
||||
return this.CacheManager.Get<string[]>(CacheManagerBase.SystemCacheRegionUrn + ":active_usernames", () =>
|
||||
{
|
||||
return this.DbContext.Users.Where(x => x.IsActive ?? false).Select(x => x.UserName).ToArray();
|
||||
}, CacheManagerBase.SystemCacheRegionUrn);
|
||||
}
|
||||
|
||||
private subsonic.MusicFolder CollectionMusicFolder()
|
||||
{
|
||||
return this.MusicFolders().First(x => x.id == 1);
|
||||
}
|
||||
|
||||
private List<subsonic.MusicFolder> MusicFolders()
|
||||
{
|
||||
return new List<subsonic.MusicFolder>
|
||||
{
|
||||
new subsonic.MusicFolder { id = 1, name = "Collections"},
|
||||
new subsonic.MusicFolder { id = 2, name = "Music"}
|
||||
};
|
||||
}
|
||||
|
||||
private subsonic.MusicFolder MusicMusicFolder()
|
||||
{
|
||||
return this.MusicFolders().First(x => x.id == 2);
|
||||
}
|
||||
|
||||
private subsonic.Bookmark[] SubsonicBookmarksForBookmarks(IEnumerable<BookmarkList> bb, IEnumerable<TrackList> childTracks)
|
||||
{
|
||||
if (bb == null || !bb.Any())
|
||||
{
|
||||
return new subsonic.Bookmark[0];
|
||||
}
|
||||
var result = new List<subsonic.Bookmark>();
|
||||
foreach(var bookmark in bb)
|
||||
{
|
||||
subsonic.Child child = null;
|
||||
switch (bookmark.Type.Value)
|
||||
{
|
||||
case Library.Enums.BookmarkType.Track:
|
||||
child = this.SubsonicChildForTrack(childTracks.FirstOrDefault(x => x.Id == SafeParser.ToGuid(bookmark.Bookmark.Value)));
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Wrong Bookmark type to convert to Subsonic media Bookmark");
|
||||
}
|
||||
result.Add(this.SubsonicBookmarkForBookmark(bookmark, child));
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private subsonic.Bookmark SubsonicBookmarkForBookmark(BookmarkList b, subsonic.Child entry)
|
||||
{
|
||||
return new subsonic.Bookmark
|
||||
{
|
||||
changed = b.LastUpdated ?? b.CreatedDate.Value,
|
||||
comment = b.Comment,
|
||||
created = b.CreatedDate.Value,
|
||||
position = b.Position ?? 0,
|
||||
username = b.User.Text,
|
||||
entry = entry
|
||||
};
|
||||
}
|
||||
|
||||
private subsonic.AlbumID3 SubsonicAlbumID3ForRelease(ReleaseList r)
|
||||
{
|
||||
return new subsonic.AlbumID3
|
||||
|
@ -1893,6 +1973,43 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
private subsonic.Bookmark SubsonicBookmarkForBookmark(BookmarkList b, subsonic.Child entry)
|
||||
{
|
||||
return new subsonic.Bookmark
|
||||
{
|
||||
changed = b.LastUpdated ?? b.CreatedDate.Value,
|
||||
comment = b.Comment,
|
||||
created = b.CreatedDate.Value,
|
||||
position = b.Position ?? 0,
|
||||
username = b.User.Text,
|
||||
entry = entry
|
||||
};
|
||||
}
|
||||
|
||||
private subsonic.Bookmark[] SubsonicBookmarksForBookmarks(IEnumerable<BookmarkList> bb, IEnumerable<TrackList> childTracks)
|
||||
{
|
||||
if (bb == null || !bb.Any())
|
||||
{
|
||||
return new subsonic.Bookmark[0];
|
||||
}
|
||||
var result = new List<subsonic.Bookmark>();
|
||||
foreach (var bookmark in bb)
|
||||
{
|
||||
subsonic.Child child = null;
|
||||
switch (bookmark.Type.Value)
|
||||
{
|
||||
case Library.Enums.BookmarkType.Track:
|
||||
child = this.SubsonicChildForTrack(childTracks.FirstOrDefault(x => x.Id == SafeParser.ToGuid(bookmark.Bookmark.Value)));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("Wrong Bookmark type to convert to Subsonic media Bookmark");
|
||||
}
|
||||
result.Add(this.SubsonicBookmarkForBookmark(bookmark, child));
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private subsonic.Child SubsonicChildForRelease(ReleaseList r, string parent, string path)
|
||||
{
|
||||
return new subsonic.Child
|
||||
|
@ -2035,7 +2152,7 @@ namespace Roadie.Api.Services
|
|||
podcastRole = false, // Disable podcast nonsense
|
||||
scrobblingEnabled = false, // Disable scrobbling
|
||||
settingsRole = isAdmin,
|
||||
shareRole = false, // TODO enabled when sharing is implmeneted
|
||||
shareRole = false, // TODO enabled when sharing is implemented
|
||||
streamRole = true,
|
||||
uploadRole = true,
|
||||
username = user.UserName,
|
||||
|
@ -2044,6 +2161,114 @@ namespace Roadie.Api.Services
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> ToggleArtistStar(Guid artistId, ApplicationUser user, bool starred)
|
||||
{
|
||||
var artist = this.GetArtist(artistId);
|
||||
if (artist == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Artist Id [{ artistId }]");
|
||||
}
|
||||
var userArtist = user.ArtistRatings.FirstOrDefault(x => x.ArtistId == artist.Id);
|
||||
if (userArtist == null)
|
||||
{
|
||||
userArtist = new data.UserArtist
|
||||
{
|
||||
IsFavorite = true,
|
||||
UserId = user.Id,
|
||||
ArtistId = artist.Id
|
||||
};
|
||||
this.DbContext.UserArtists.Add(userArtist);
|
||||
}
|
||||
else
|
||||
{
|
||||
userArtist.IsFavorite = starred;
|
||||
userArtist.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> ToggleReleaseStar(Guid releaseId, ApplicationUser user, bool starred)
|
||||
{
|
||||
var release = this.GetRelease(releaseId);
|
||||
if (release == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release Id [{ releaseId }]");
|
||||
}
|
||||
var userRelease = user.ReleaseRatings.FirstOrDefault(x => x.ReleaseId == release.Id);
|
||||
if (userRelease == null)
|
||||
{
|
||||
userRelease = new data.UserRelease
|
||||
{
|
||||
IsFavorite = true,
|
||||
UserId = user.Id,
|
||||
ReleaseId = release.Id
|
||||
};
|
||||
this.DbContext.UserReleases.Add(userRelease);
|
||||
}
|
||||
else
|
||||
{
|
||||
userRelease.IsFavorite = starred;
|
||||
userRelease.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.Artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> ToggleTrackStar(Guid trackId, ApplicationUser user, bool starred)
|
||||
{
|
||||
var track = this.GetTrack(trackId);
|
||||
if (track == null)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ trackId }]");
|
||||
}
|
||||
var userTrack = user.TrackRatings.FirstOrDefault(x => x.TrackId == track.Id);
|
||||
if (userTrack == null)
|
||||
{
|
||||
userTrack = new data.UserTrack
|
||||
{
|
||||
IsFavorite = true,
|
||||
UserId = user.Id,
|
||||
TrackId = track.Id
|
||||
};
|
||||
this.DbContext.UserTracks.Add(userTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
userTrack.IsFavorite = starred;
|
||||
userTrack.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Privates
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
}
|
||||
},
|
||||
"Serilog": {
|
||||
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.RollingFileAlternate", "Serilog.Sinks.SQLite" ],
|
||||
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.RollingFileAlternate" ],
|
||||
"MinimumLevel": {
|
||||
"Default": "Verbose",
|
||||
"Override": {
|
||||
|
@ -31,13 +31,6 @@
|
|||
"fileSizeLimitBytes": 26214400,
|
||||
"buffered": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "SQLite",
|
||||
"Args": {
|
||||
"restrictedToMinimumLevel": "Error",
|
||||
"sqliteDbPath": "logs\\errors.sqlite"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "WithExceptionDetails" ],
|
||||
|
|
|
@ -66,6 +66,8 @@ namespace Roadie.Library.Data
|
|||
[MaxLength(100)]
|
||||
public string SpotifyId { get; set; }
|
||||
|
||||
[Column("releaseCount")]
|
||||
public int? ReleaseCount { get; set; } // TODO update this on artist folder scan
|
||||
|
||||
}
|
||||
}
|
|
@ -23,5 +23,10 @@ namespace Roadie.Library.Data
|
|||
return Playlist.CacheUrn(this.RoadieId);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Id [{this.Id}], Name [{this.Name}], RoadieId [{ this.RoadieId}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Roadie.Library.Data
|
|||
public ICollection<CollectionRelease> Collections { get; set; }
|
||||
|
||||
[Column("duration")]
|
||||
public int? Duration { get; set; }
|
||||
public int? Duration { get; set; } // TODO update this on release scan
|
||||
|
||||
[Column("discogsId")]
|
||||
[MaxLength(50)]
|
||||
|
@ -69,7 +69,7 @@ namespace Roadie.Library.Data
|
|||
public string MusicBrainzId { get; set; }
|
||||
|
||||
[Column("playedCount")]
|
||||
public int? PlayedCount { get; set; }
|
||||
public int? PlayedCount { get; set; }
|
||||
|
||||
[Column("profile", TypeName = "text")]
|
||||
[MaxLength(65535)]
|
||||
|
@ -104,7 +104,7 @@ namespace Roadie.Library.Data
|
|||
public string Title { get; set; }
|
||||
|
||||
[Column("trackCount")]
|
||||
public short TrackCount { get; set; }
|
||||
public short TrackCount { get; set; }
|
||||
|
||||
[Column("urls", TypeName = "text")]
|
||||
[MaxLength(65535)]
|
||||
|
|
|
@ -211,6 +211,12 @@ namespace Roadie.Library.Models.ThirdPartyApi.Subsonic
|
|||
/// </summary>
|
||||
public int? ArtistOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The artist name.
|
||||
/// <see cref="getTopSongs"/>
|
||||
/// </summary>
|
||||
public string ArtistName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The first year in the range. If fromYear > toYear a reverse chronological list is returned.
|
||||
/// </summary>
|
||||
|
|
|
@ -145,10 +145,11 @@ namespace Roadie.Library.Processors
|
|||
var extension = file.Extension.ToLower();
|
||||
if (extension.Equals(".mp3") || extension.Equals(".flac"))
|
||||
{
|
||||
var tagFile = TagLib.File.Create(file.FullName);
|
||||
tagFile.Tag.Performers = null;
|
||||
tagFile.Tag.Performers = new[] { artist };
|
||||
tagFile.Save();
|
||||
// TODO
|
||||
//var tagFile = TagLib.File.Create(file.FullName);
|
||||
//tagFile.Tag.Performers = null;
|
||||
//tagFile.Tag.Performers = new[] { artist };
|
||||
//tagFile.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="FluentFTP" Version="19.2.2" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.8.10" />
|
||||
<PackageReference Include="ID3" Version="0.5.0-beta.1" />
|
||||
<PackageReference Include="ID3Tag.Core" Version="0.1.3" />
|
||||
<PackageReference Include="Inflatable.Lastfm" Version="1.1.0.339" />
|
||||
<PackageReference Include="Mapster" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.1.2" />
|
||||
<PackageReference Include="MimeMapping" Version="1.0.1.12" />
|
||||
<PackageReference Include="Orthogonal.NTagLite" Version="2.0.9" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.2" />
|
||||
<PackageReference Include="RestSharp" Version="106.5.4" />
|
||||
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0006" />
|
||||
|
@ -22,7 +23,6 @@
|
|||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0005" />
|
||||
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0007" />
|
||||
<PackageReference Include="System.Runtime.Caching" Version="4.5.0" />
|
||||
<PackageReference Include="taglib" Version="2.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Orthogonal.NTagLite;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Extensions;
|
||||
|
@ -70,28 +69,30 @@ namespace Roadie.Library.MetaData.ID3Tags
|
|||
{
|
||||
try
|
||||
{
|
||||
var tagFile = TagLib.File.Create(filename);
|
||||
tagFile.Tag.AlbumArtists = null;
|
||||
tagFile.Tag.AlbumArtists = new[] { metaData.Artist };
|
||||
tagFile.Tag.Performers = null;
|
||||
if (metaData.TrackArtists.Any())
|
||||
{
|
||||
tagFile.Tag.Performers = metaData.TrackArtists.ToArray();
|
||||
}
|
||||
tagFile.Tag.Album = metaData.Release;
|
||||
tagFile.Tag.Title = metaData.Title;
|
||||
tagFile.Tag.Year = force ? (uint)(metaData.Year ?? 0) : tagFile.Tag.Year > 0 ? tagFile.Tag.Year : (uint)(metaData.Year ?? 0);
|
||||
tagFile.Tag.Track = force ? (uint)(metaData.TrackNumber ?? 0) : tagFile.Tag.Track > 0 ? tagFile.Tag.Track : (uint)(metaData.TrackNumber ?? 0);
|
||||
tagFile.Tag.TrackCount = force ? (uint)(metaData.TotalTrackNumbers ?? 0) : tagFile.Tag.TrackCount > 0 ? tagFile.Tag.TrackCount : (uint)(metaData.TotalTrackNumbers ?? 0);
|
||||
tagFile.Tag.Disc = force ? (uint)(metaData.Disk ?? 0) : tagFile.Tag.Disc > 0 ? tagFile.Tag.Disc : (uint)(metaData.Disk ?? 0);
|
||||
tagFile.Tag.Pictures = metaData.Images == null ? null : metaData.Images.Select(x => new TagLib.Picture
|
||||
{
|
||||
Data = new TagLib.ByteVector(x.Data),
|
||||
Description = x.Description,
|
||||
MimeType = x.MimeType,
|
||||
Type = (TagLib.PictureType)x.Type
|
||||
}).ToArray();
|
||||
tagFile.Save();
|
||||
// TODO
|
||||
|
||||
//var tagFile = TagLib.File.Create(filename);
|
||||
//tagFile.Tag.AlbumArtists = null;
|
||||
//tagFile.Tag.AlbumArtists = new[] { metaData.Artist };
|
||||
//tagFile.Tag.Performers = null;
|
||||
//if (metaData.TrackArtists.Any())
|
||||
//{
|
||||
// tagFile.Tag.Performers = metaData.TrackArtists.ToArray();
|
||||
//}
|
||||
//tagFile.Tag.Album = metaData.Release;
|
||||
//tagFile.Tag.Title = metaData.Title;
|
||||
//tagFile.Tag.Year = force ? (uint)(metaData.Year ?? 0) : tagFile.Tag.Year > 0 ? tagFile.Tag.Year : (uint)(metaData.Year ?? 0);
|
||||
//tagFile.Tag.Track = force ? (uint)(metaData.TrackNumber ?? 0) : tagFile.Tag.Track > 0 ? tagFile.Tag.Track : (uint)(metaData.TrackNumber ?? 0);
|
||||
//tagFile.Tag.TrackCount = force ? (uint)(metaData.TotalTrackNumbers ?? 0) : tagFile.Tag.TrackCount > 0 ? tagFile.Tag.TrackCount : (uint)(metaData.TotalTrackNumbers ?? 0);
|
||||
//tagFile.Tag.Disc = force ? (uint)(metaData.Disk ?? 0) : tagFile.Tag.Disc > 0 ? tagFile.Tag.Disc : (uint)(metaData.Disk ?? 0);
|
||||
//tagFile.Tag.Pictures = metaData.Images == null ? null : metaData.Images.Select(x => new TagLib.Picture
|
||||
//{
|
||||
// Data = new TagLib.ByteVector(x.Data),
|
||||
// Description = x.Description,
|
||||
// MimeType = x.MimeType,
|
||||
// Type = (TagLib.PictureType)x.Type
|
||||
//}).ToArray();
|
||||
//tagFile.Save();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -109,31 +110,33 @@ namespace Roadie.Library.MetaData.ID3Tags
|
|||
var isSuccess = false;
|
||||
try
|
||||
{
|
||||
var file = LiteFile.LoadFromFile(fileName);
|
||||
var tpos = file.Tag.FindFirstFrameById(FrameId.TPOS);
|
||||
Picture[] pics = file.Tag.FindFramesById(FrameId.APIC).Select(f => f.GetPicture()).ToArray();
|
||||
result.Release = file.Tag.Album;
|
||||
result.Artist = file.Tag.Artist;
|
||||
result.ArtistRaw = file.Tag.Artist;
|
||||
result.Genres = (file.Tag.Genre ?? string.Empty).Split(';');
|
||||
result.TrackArtist = file.Tag.OriginalArtist;
|
||||
result.TrackArtistRaw = file.Tag.OriginalArtist;
|
||||
result.AudioBitrate = file.Bitrate;
|
||||
result.AudioChannels = file.AudioMode.HasValue ? (int?)file.AudioMode.Value : null;
|
||||
result.AudioSampleRate = file.Frequency;
|
||||
result.Disk = tpos != null ? SafeParser.ToNumber<int?>(tpos.Text) : null;
|
||||
result.Images = pics.Select(x => new AudioMetaDataImage
|
||||
{
|
||||
Data = x.Data,
|
||||
Description = x.Description,
|
||||
MimeType = x.MimeType,
|
||||
Type = (AudioMetaDataImageType)x.PictureType
|
||||
}).ToArray();
|
||||
result.Time = file.Duration;
|
||||
result.Title = file.Tag.Title.ToTitleCase(false);
|
||||
result.TotalTrackNumbers = file.Tag.TrackCount;
|
||||
result.TrackNumber = file.Tag.TrackNumber;
|
||||
result.Year = file.Tag.Year;
|
||||
// TODO
|
||||
|
||||
//var file = LiteFile.LoadFromFile(fileName);
|
||||
//var tpos = file.Tag.FindFirstFrameById(FrameId.TPOS);
|
||||
//Picture[] pics = file.Tag.FindFramesById(FrameId.APIC).Select(f => f.GetPicture()).ToArray();
|
||||
//result.Release = file.Tag.Album;
|
||||
//result.Artist = file.Tag.Artist;
|
||||
//result.ArtistRaw = file.Tag.Artist;
|
||||
//result.Genres = (file.Tag.Genre ?? string.Empty).Split(';');
|
||||
//result.TrackArtist = file.Tag.OriginalArtist;
|
||||
//result.TrackArtistRaw = file.Tag.OriginalArtist;
|
||||
//result.AudioBitrate = file.Bitrate;
|
||||
//result.AudioChannels = file.AudioMode.HasValue ? (int?)file.AudioMode.Value : null;
|
||||
//result.AudioSampleRate = file.Frequency;
|
||||
//result.Disk = tpos != null ? SafeParser.ToNumber<int?>(tpos.Text) : null;
|
||||
//result.Images = pics.Select(x => new AudioMetaDataImage
|
||||
//{
|
||||
// Data = x.Data,
|
||||
// Description = x.Description,
|
||||
// MimeType = x.MimeType,
|
||||
// Type = (AudioMetaDataImageType)x.PictureType
|
||||
//}).ToArray();
|
||||
//result.Time = file.Duration;
|
||||
//result.Title = file.Tag.Title.ToTitleCase(false);
|
||||
//result.TotalTrackNumbers = file.Tag.TrackCount;
|
||||
//result.TrackNumber = file.Tag.TrackNumber;
|
||||
//result.Year = file.Tag.Year;
|
||||
isSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -157,29 +160,30 @@ namespace Roadie.Library.MetaData.ID3Tags
|
|||
var isSuccess = false;
|
||||
try
|
||||
{
|
||||
var tagFile = TagLib.File.Create(fileName);
|
||||
result.Release = tagFile.Tag.Album;
|
||||
result.Artist = !string.IsNullOrEmpty(tagFile.Tag.JoinedAlbumArtists) ? tagFile.Tag.JoinedAlbumArtists : tagFile.Tag.JoinedPerformers;
|
||||
result.ArtistRaw = !string.IsNullOrEmpty(tagFile.Tag.JoinedAlbumArtists) ? tagFile.Tag.JoinedAlbumArtists : tagFile.Tag.JoinedPerformers;
|
||||
result.Genres = tagFile.Tag.Genres != null ? tagFile.Tag.Genres : new string[0];
|
||||
result.TrackArtist = tagFile.Tag.JoinedPerformers;
|
||||
result.TrackArtistRaw = tagFile.Tag.JoinedPerformers;
|
||||
result.AudioBitrate = (tagFile.Properties.AudioBitrate > 0 ? (int?)tagFile.Properties.AudioBitrate : null);
|
||||
result.AudioChannels = (tagFile.Properties.AudioChannels > 0 ? (int?)tagFile.Properties.AudioChannels : null);
|
||||
result.AudioSampleRate = (tagFile.Properties.AudioSampleRate > 0 ? (int?)tagFile.Properties.AudioSampleRate : null);
|
||||
result.Disk = (tagFile.Tag.Disc > 0 ? (int?)tagFile.Tag.Disc : null);
|
||||
result.Images = (tagFile.Tag.Pictures != null ? tagFile.Tag.Pictures.Select(x => new AudioMetaDataImage
|
||||
{
|
||||
Data = x.Data.Data,
|
||||
Description = x.Description,
|
||||
MimeType = x.MimeType,
|
||||
Type = (AudioMetaDataImageType)x.Type
|
||||
}).ToArray() : null);
|
||||
result.Time = (tagFile.Properties.Duration.TotalMinutes > 0 ? (TimeSpan?)tagFile.Properties.Duration : null);
|
||||
result.Title = tagFile.Tag.Title.ToTitleCase(false);
|
||||
result.TotalTrackNumbers = (tagFile.Tag.TrackCount > 0 ? (int?)tagFile.Tag.TrackCount : null);
|
||||
result.TrackNumber = (tagFile.Tag.Track > 0 ? (short?)tagFile.Tag.Track : null);
|
||||
result.Year = (tagFile.Tag.Year > 0 ? (int?)tagFile.Tag.Year : null);
|
||||
// TODO
|
||||
//var tagFile = TagLib.File.Create(fileName);
|
||||
//result.Release = tagFile.Tag.Album;
|
||||
//result.Artist = !string.IsNullOrEmpty(tagFile.Tag.JoinedAlbumArtists) ? tagFile.Tag.JoinedAlbumArtists : tagFile.Tag.JoinedPerformers;
|
||||
//result.ArtistRaw = !string.IsNullOrEmpty(tagFile.Tag.JoinedAlbumArtists) ? tagFile.Tag.JoinedAlbumArtists : tagFile.Tag.JoinedPerformers;
|
||||
//result.Genres = tagFile.Tag.Genres != null ? tagFile.Tag.Genres : new string[0];
|
||||
//result.TrackArtist = tagFile.Tag.JoinedPerformers;
|
||||
//result.TrackArtistRaw = tagFile.Tag.JoinedPerformers;
|
||||
//result.AudioBitrate = (tagFile.Properties.AudioBitrate > 0 ? (int?)tagFile.Properties.AudioBitrate : null);
|
||||
//result.AudioChannels = (tagFile.Properties.AudioChannels > 0 ? (int?)tagFile.Properties.AudioChannels : null);
|
||||
//result.AudioSampleRate = (tagFile.Properties.AudioSampleRate > 0 ? (int?)tagFile.Properties.AudioSampleRate : null);
|
||||
//result.Disk = (tagFile.Tag.Disc > 0 ? (int?)tagFile.Tag.Disc : null);
|
||||
//result.Images = (tagFile.Tag.Pictures != null ? tagFile.Tag.Pictures.Select(x => new AudioMetaDataImage
|
||||
//{
|
||||
// Data = x.Data.Data,
|
||||
// Description = x.Description,
|
||||
// MimeType = x.MimeType,
|
||||
// Type = (AudioMetaDataImageType)x.Type
|
||||
//}).ToArray() : null);
|
||||
//result.Time = (tagFile.Properties.Duration.TotalMinutes > 0 ? (TimeSpan?)tagFile.Properties.Duration : null);
|
||||
//result.Title = tagFile.Tag.Title.ToTitleCase(false);
|
||||
//result.TotalTrackNumbers = (tagFile.Tag.TrackCount > 0 ? (int?)tagFile.Tag.TrackCount : null);
|
||||
//result.TrackNumber = (tagFile.Tag.Track > 0 ? (short?)tagFile.Tag.Track : null);
|
||||
//result.Year = (tagFile.Tag.Year > 0 ? (int?)tagFile.Tag.Year : null);
|
||||
isSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
Loading…
Reference in a new issue