2018-11-23 04:18:48 +00:00
|
|
|
|
using Mapster;
|
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2018-11-19 23:51:58 +00:00
|
|
|
|
using Newtonsoft.Json;
|
2018-11-23 04:18:48 +00:00
|
|
|
|
using Roadie.Api.ModelBinding;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
using Roadie.Api.Services;
|
|
|
|
|
using Roadie.Library.Caching;
|
2018-11-22 13:48:32 +00:00
|
|
|
|
using Roadie.Library.Extensions;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
using Roadie.Library.Identity;
|
2018-11-19 23:51:58 +00:00
|
|
|
|
using Roadie.Library.Models.ThirdPartyApi.Subsonic;
|
2018-11-22 17:31:59 +00:00
|
|
|
|
using System;
|
2018-11-20 04:47:12 +00:00
|
|
|
|
using System.Net;
|
2018-11-19 23:51:58 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
|
|
|
|
|
namespace Roadie.Api.Controllers
|
|
|
|
|
{
|
2018-11-19 23:51:58 +00:00
|
|
|
|
[Route("subsonic/rest")]
|
2018-11-15 15:10:29 +00:00
|
|
|
|
[ApiController]
|
|
|
|
|
public class SubsonicController : EntityControllerBase
|
|
|
|
|
{
|
2018-11-20 14:36:07 +00:00
|
|
|
|
private IPlayActivityService PlayActivityService { get; }
|
2018-11-21 06:34:53 +00:00
|
|
|
|
private ISubsonicService SubsonicService { get; }
|
2018-11-23 04:18:48 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This is the user authenticated via the Subsonic methods - NOT the current API Identity User.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private Library.Models.Users.User SubsonicUser { get; set; }
|
|
|
|
|
|
2018-11-20 14:36:07 +00:00
|
|
|
|
private ITrackService TrackService { get; }
|
2018-11-15 15:10:29 +00:00
|
|
|
|
|
2018-11-20 14:36:07 +00:00
|
|
|
|
public SubsonicController(ISubsonicService subsonicService, ITrackService trackService, IPlayActivityService playActivityService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration, UserManager<ApplicationUser> userManager)
|
2018-11-15 15:10:29 +00:00
|
|
|
|
: base(cacheManager, configuration, userManager)
|
|
|
|
|
{
|
2018-11-21 06:34:53 +00:00
|
|
|
|
this.Logger = logger.CreateLogger("RoadieApi.Controllers.SubsonicController");
|
2018-11-15 15:10:29 +00:00
|
|
|
|
this.SubsonicService = subsonicService;
|
2018-11-20 14:36:07 +00:00
|
|
|
|
this.TrackService = trackService;
|
|
|
|
|
this.PlayActivityService = playActivityService;
|
2018-11-15 15:10:29 +00:00
|
|
|
|
}
|
2018-11-19 23:51:58 +00:00
|
|
|
|
|
2018-11-22 13:48:32 +00:00
|
|
|
|
[HttpGet("getAlbum.view")]
|
|
|
|
|
[HttpPost("getAlbum.view")]
|
2018-11-19 23:51:58 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetAlbum(SubsonicRequest request)
|
2018-11-19 23:51:58 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetAlbum(request, this.SubsonicUser);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "album");
|
2018-11-19 23:51:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 13:48:32 +00:00
|
|
|
|
[HttpGet("getAlbumInfo.view")]
|
|
|
|
|
[HttpPost("getAlbumInfo.view")]
|
2018-11-21 18:19:38 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetAlbumInfo(SubsonicRequest request)
|
2018-11-21 18:19:38 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetAlbumInfo(request, this.SubsonicUser, AlbumInfoVersion.One);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "albumInfo");
|
2018-11-21 18:19:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 13:48:32 +00:00
|
|
|
|
[HttpGet("getAlbumInfo2.view")]
|
|
|
|
|
[HttpPost("getAlbumInfo2.view")]
|
2018-11-21 18:19:38 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetAlbumInfo2(SubsonicRequest request)
|
2018-11-21 18:19:38 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetAlbumInfo(request, this.SubsonicUser, AlbumInfoVersion.Two);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "albumInfo");
|
2018-11-21 18:19:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 13:48:32 +00:00
|
|
|
|
[HttpGet("getAlbumList.view")]
|
|
|
|
|
[HttpPost("getAlbumList.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetAlbumList(SubsonicRequest request)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetAlbumList(request, this.SubsonicUser, AlbumListVersions.One);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "albumList");
|
2018-11-21 18:19:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpGet("getAlbumList2.view")]
|
|
|
|
|
[HttpPost("getAlbumList2.view")]
|
2018-11-19 23:51:58 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetAlbumList2(SubsonicRequest request)
|
2018-11-19 23:51:58 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetAlbumList(request, this.SubsonicUser, AlbumListVersions.Two);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "albumList");
|
2018-11-21 06:34:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 17:31:59 +00:00
|
|
|
|
[HttpGet("getArtist.view")]
|
|
|
|
|
[HttpPost("getArtist.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetArtist(SubsonicRequest request)
|
2018-11-22 17:31:59 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetArtist(request, this.SubsonicUser);
|
2018-11-22 17:31:59 +00:00
|
|
|
|
return this.BuildResponse(request, result, "artist");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 15:22:55 +00:00
|
|
|
|
[HttpGet("getArtistInfo.view")]
|
|
|
|
|
[HttpPost("getArtistInfo.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetArtistInfo(SubsonicRequest request, int? count, bool? includeNotPresent)
|
2018-11-21 15:22:55 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
2018-11-22 13:48:32 +00:00
|
|
|
|
var result = await this.SubsonicService.GetArtistInfo(request, count, includeNotPresent ?? false, ArtistInfoVersion.One);
|
|
|
|
|
return this.BuildResponse(request, result, "artistInfo");
|
2018-11-21 15:22:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getArtistInfo2.view")]
|
|
|
|
|
[HttpPost("getArtistInfo2.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetArtistInfo2(SubsonicRequest request, int? count, bool? includeNotPresent)
|
2018-11-21 15:22:55 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
2018-11-22 13:48:32 +00:00
|
|
|
|
var result = await this.SubsonicService.GetArtistInfo(request, count, includeNotPresent ?? false, ArtistInfoVersion.Two);
|
|
|
|
|
return this.BuildResponse(request, result, "artistInfo2");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getArtists.view")]
|
|
|
|
|
[HttpPost("getArtists.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetArtists(SubsonicRequest request)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetArtists(request, this.SubsonicUser);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "artists");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getAvatar.view")]
|
|
|
|
|
[HttpPost("getAvatar.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetAvatar(SubsonicRequest request, string username)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
|
|
|
|
var user = await this.UserManager.FindByNameAsync(username);
|
|
|
|
|
return Redirect($"/images/user/{ user.RoadieId }/{this.RoadieSettings.ThumbnailImageSize.Width}/{this.RoadieSettings.ThumbnailImageSize.Height}");
|
2018-11-21 15:22:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpGet("getCoverArt.view")]
|
|
|
|
|
[HttpPost("getCoverArt.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetCoverArt(SubsonicRequest request, int? size)
|
2018-11-21 06:34:53 +00:00
|
|
|
|
{
|
|
|
|
|
var result = await this.SubsonicService.GetCoverArt(request, size);
|
|
|
|
|
if (result == null || result.IsNotFoundResult)
|
|
|
|
|
{
|
|
|
|
|
return NotFound();
|
|
|
|
|
}
|
|
|
|
|
if (!result.IsSuccess)
|
|
|
|
|
{
|
|
|
|
|
this.Logger.LogWarning($"GetCoverArt Failed For [{ JsonConvert.SerializeObject(request) }]");
|
|
|
|
|
return StatusCode((int)HttpStatusCode.InternalServerError);
|
|
|
|
|
}
|
|
|
|
|
return File(fileContents: result.Data.Bytes,
|
|
|
|
|
contentType: result.ContentType,
|
|
|
|
|
fileDownloadName: $"{ result.Data.Caption ?? request.id.ToString()}.jpg",
|
|
|
|
|
lastModified: result.LastModified,
|
|
|
|
|
entityTag: result.ETag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getGenres.view")]
|
|
|
|
|
[HttpPost("getGenres.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetGenres(SubsonicRequest request)
|
2018-11-21 06:34:53 +00:00
|
|
|
|
{
|
|
|
|
|
var result = await this.SubsonicService.GetGenres(request);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "genres");
|
2018-11-21 06:34:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getIndexes.view")]
|
|
|
|
|
[HttpPost("getIndexes.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetIndexes(SubsonicRequest request, long? ifModifiedSince = null)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetIndexes(request, this.SubsonicUser, ifModifiedSince);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "indexes");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getLicense.view")]
|
|
|
|
|
[HttpPost("getLicense.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public IActionResult GetLicense(SubsonicRequest request)
|
2018-11-21 06:34:53 +00:00
|
|
|
|
{
|
2018-11-22 13:48:32 +00:00
|
|
|
|
var result = this.SubsonicService.GetLicense(request);
|
|
|
|
|
return this.BuildResponse(request, result, "license");
|
2018-11-19 23:51:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 17:31:59 +00:00
|
|
|
|
[HttpGet("getLyrics.view")]
|
|
|
|
|
[HttpPost("getLyrics.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public IActionResult GetLyrics(SubsonicRequest request, string artist, string title)
|
2018-11-22 17:31:59 +00:00
|
|
|
|
{
|
|
|
|
|
var result = this.SubsonicService.GetLyrics(request, artist, title);
|
|
|
|
|
return this.BuildResponse(request, result, "lyrics ");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-20 04:47:12 +00:00
|
|
|
|
[HttpGet("getMusicDirectory.view")]
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpPost("getMusicDirectory.view")]
|
2018-11-20 04:47:12 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetMusicDirectory(SubsonicRequest request)
|
2018-11-20 04:47:12 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetMusicDirectory(request, this.SubsonicUser);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "directory");
|
2018-11-20 04:47:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpGet("getMusicFolders.view")]
|
|
|
|
|
[HttpPost("getMusicFolders.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetMusicFolders(SubsonicRequest request)
|
2018-11-21 06:34:53 +00:00
|
|
|
|
{
|
|
|
|
|
var result = await this.SubsonicService.GetMusicFolders(request);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "musicFolders");
|
2018-11-21 06:34:53 +00:00
|
|
|
|
}
|
2018-11-20 04:47:12 +00:00
|
|
|
|
|
2018-11-21 18:19:38 +00:00
|
|
|
|
[HttpGet("getPlaylist.view")]
|
|
|
|
|
[HttpPost("getPlaylist.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetPlaylist(SubsonicRequest request)
|
2018-11-21 18:19:38 +00:00
|
|
|
|
{
|
2018-11-23 15:10:33 +00:00
|
|
|
|
this.Logger.Log(LogLevel.Critical, ":: Critial");
|
|
|
|
|
this.Logger.Log(LogLevel.Debug, ":: Debug");
|
|
|
|
|
this.Logger.Log(LogLevel.Error, ":: Error");
|
|
|
|
|
this.Logger.Log(LogLevel.Information, ":: Information");
|
|
|
|
|
this.Logger.Log(LogLevel.None, ":: None");
|
|
|
|
|
this.Logger.Log(LogLevel.Trace, ":: Trace");
|
|
|
|
|
this.Logger.Log(LogLevel.Warning, ":: Warning");
|
|
|
|
|
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetPlaylist(request, this.SubsonicUser);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "playlist");
|
2018-11-21 18:19:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 23:51:58 +00:00
|
|
|
|
[HttpGet("getPlaylists.view")]
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpPost("getPlaylists.view")]
|
2018-11-19 23:51:58 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetPlaylists(SubsonicRequest request, string username)
|
2018-11-19 23:51:58 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var result = await this.SubsonicService.GetPlaylists(request, this.SubsonicUser, username);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "playlists");
|
2018-11-19 23:51:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-20 04:47:12 +00:00
|
|
|
|
[HttpGet("getPodcasts.view")]
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpPost("getPodcasts.view")]
|
2018-11-20 04:47:12 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetPodcasts(SubsonicRequest request, bool includeEpisodes)
|
2018-11-20 04:47:12 +00:00
|
|
|
|
{
|
|
|
|
|
var result = await this.SubsonicService.GetPodcasts(request);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "podcasts");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getRandomSongs.view")]
|
|
|
|
|
[HttpPost("getRandomSongs.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetRandomSongs(SubsonicRequest request)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetRandomSongs(request, this.SubsonicUser);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "randomSongs");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-23 04:18:48 +00:00
|
|
|
|
[HttpGet("getSimilarSongs.view")]
|
|
|
|
|
[HttpPost("getSimilarSongs.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
|
|
|
|
public async Task<IActionResult> GetSimilarSongs(SubsonicRequest request, int? count = 50)
|
|
|
|
|
{
|
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetSimliarSongs(request, this.SubsonicUser, SimilarSongsVersion.One, count);
|
|
|
|
|
return this.BuildResponse(request, result, "similarSongs");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getSimilarSongs2.view")]
|
|
|
|
|
[HttpPost("getSimilarSongs2.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
|
|
|
|
public async Task<IActionResult> GetSimilarSongs2(SubsonicRequest request, int? count = 50)
|
|
|
|
|
{
|
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetSimliarSongs(request, this.SubsonicUser, SimilarSongsVersion.Two, count);
|
|
|
|
|
return this.BuildResponse(request, result, "similarSongs2");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 17:31:59 +00:00
|
|
|
|
[HttpGet("getSong.view")]
|
|
|
|
|
[HttpPost("getSong.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetSong(SubsonicRequest request)
|
2018-11-22 17:31:59 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetSong(request, this.SubsonicUser);
|
2018-11-22 17:31:59 +00:00
|
|
|
|
return this.BuildResponse(request, result, "song");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-23 04:18:48 +00:00
|
|
|
|
[HttpGet("getSongsByGenre.view")]
|
|
|
|
|
[HttpPost("getSongsByGenre.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
|
|
|
|
public async Task<IActionResult> GetSongsByGenre(SubsonicRequest request)
|
|
|
|
|
{
|
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetSongsByGenre(request, this.SubsonicUser);
|
|
|
|
|
return this.BuildResponse(request, result, "songsByGenre");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 13:48:32 +00:00
|
|
|
|
[HttpGet("getStarred.view")]
|
|
|
|
|
[HttpPost("getStarred.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetStarred(SubsonicRequest request)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetStarred(request, this.SubsonicUser, StarredVersion.One);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "starred");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet("getStarred2.view")]
|
|
|
|
|
[HttpPost("getStarred2.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetStarred2(SubsonicRequest request)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetStarred(request, this.SubsonicUser, StarredVersion.Two);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "starred");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 17:31:59 +00:00
|
|
|
|
[HttpGet("getTopSongs.view")]
|
|
|
|
|
[HttpPost("getTopSongs.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetTopSongs(SubsonicRequest request, string artist, int? count = 50)
|
2018-11-22 17:31:59 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.GetTopSongs(request, this.SubsonicUser, artist, count);
|
2018-11-22 17:31:59 +00:00
|
|
|
|
return this.BuildResponse(request, result, "topSongs");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 13:48:32 +00:00
|
|
|
|
[HttpGet("getUser.view")]
|
|
|
|
|
[HttpPost("getUser.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> GetUser(SubsonicRequest request, string username)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
2018-11-22 17:31:59 +00:00
|
|
|
|
var result = await this.SubsonicService.GetUser(request, username ?? request.u);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "user");
|
2018-11-20 04:47:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 17:31:59 +00:00
|
|
|
|
[HttpGet("getVideos.view")]
|
|
|
|
|
[HttpPost("getVideos.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public IActionResult GetVideos(SubsonicRequest request)
|
2018-11-22 17:31:59 +00:00
|
|
|
|
{
|
|
|
|
|
var result = this.SubsonicService.GetVideos(request);
|
|
|
|
|
return this.BuildResponse(request, result, "videos");
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpGet("ping.view")]
|
|
|
|
|
[HttpPost("ping.view")]
|
2018-11-20 14:36:07 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public IActionResult Ping(SubsonicRequest request)
|
2018-11-20 14:36:07 +00:00
|
|
|
|
{
|
2018-11-22 13:48:32 +00:00
|
|
|
|
if (request.IsJSONRequest)
|
2018-11-20 14:36:07 +00:00
|
|
|
|
{
|
2018-11-21 06:34:53 +00:00
|
|
|
|
var result = this.SubsonicService.Ping(request);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result);
|
2018-11-20 14:36:07 +00:00
|
|
|
|
}
|
2018-11-21 06:34:53 +00:00
|
|
|
|
return Content("<subsonic-response xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://subsonic.org/restapi\" status=\"ok\" version=\"1.16.0\" />", "application/xml");
|
2018-11-20 14:36:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 06:34:53 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns albums, artists and songs matching the given search criteria. Supports paging through the result.
|
|
|
|
|
/// </summary>
|
2018-11-21 18:19:38 +00:00
|
|
|
|
[HttpGet("search.view")]
|
|
|
|
|
[HttpPost("search.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> Search(SubsonicRequest request)
|
2018-11-21 18:19:38 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.Search(request, this.SubsonicUser, SearchVersion.One);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "searchResult");
|
2018-11-21 18:19:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpGet("search2.view")]
|
|
|
|
|
[HttpPost("search2.view")]
|
2018-11-22 13:48:32 +00:00
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> Search2(SubsonicRequest request)
|
2018-11-19 23:51:58 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.Search(request, this.SubsonicUser, SearchVersion.Two);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "searchResult2");
|
2018-11-19 23:51:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 18:19:38 +00:00
|
|
|
|
[HttpGet("search3.view")]
|
|
|
|
|
[HttpPost("search3.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<IActionResult> Search3(SubsonicRequest request)
|
2018-11-21 18:19:38 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
return authResult;
|
|
|
|
|
}
|
|
|
|
|
var result = await this.SubsonicService.Search(request, this.SubsonicUser, SearchVersion.Three);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
return this.BuildResponse(request, result, "searchResult3");
|
2018-11-21 18:19:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-21 06:34:53 +00:00
|
|
|
|
[HttpGet("stream.view")]
|
|
|
|
|
[HttpPost("stream.view")]
|
|
|
|
|
[ProducesResponseType(200)]
|
2018-11-22 23:12:57 +00:00
|
|
|
|
public async Task<FileStreamResult> StreamTrack(SubsonicRequest request)
|
2018-11-21 06:34:53 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
var authResult = await this.AuthenticateUser(request);
|
|
|
|
|
if (authResult != null)
|
|
|
|
|
{
|
|
|
|
|
Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
|
|
|
|
}
|
2018-11-21 06:34:53 +00:00
|
|
|
|
var trackId = request.TrackId;
|
|
|
|
|
if (trackId == null)
|
|
|
|
|
{
|
|
|
|
|
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
|
|
|
|
}
|
2018-11-23 04:18:48 +00:00
|
|
|
|
return await base.StreamTrack(trackId.Value, this.TrackService, this.PlayActivityService, this.SubsonicUser);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<IActionResult> AuthenticateUser(SubsonicRequest request)
|
|
|
|
|
{
|
|
|
|
|
var appUser = await this.SubsonicService.Authenticate(request);
|
|
|
|
|
if (!(appUser?.IsSuccess ?? false) || (appUser?.IsNotFoundResult ?? false))
|
|
|
|
|
{
|
|
|
|
|
return this.BuildResponse(request, appUser.Adapt<SubsonicOperationResult<Response>>());
|
|
|
|
|
}
|
|
|
|
|
this.SubsonicUser = this.UserModelForUser(appUser.Data.User);
|
|
|
|
|
return null;
|
2018-11-21 06:34:53 +00:00
|
|
|
|
}
|
2018-11-20 14:36:07 +00:00
|
|
|
|
|
2018-11-19 23:51:58 +00:00
|
|
|
|
#region Response Builder Methods
|
|
|
|
|
|
2018-11-22 23:12:57 +00:00
|
|
|
|
private IActionResult BuildResponse(SubsonicRequest request, SubsonicOperationResult<Response> response = null, string responseType = null)
|
2018-11-19 23:51:58 +00:00
|
|
|
|
{
|
2018-11-22 17:31:59 +00:00
|
|
|
|
var acceptHeader = this.Request.Headers["Accept"];
|
2018-11-23 15:10:33 +00:00
|
|
|
|
this.Logger.LogTrace($"Subsonic Request: Method [{ this.Request.Method }], Accept Header [{ acceptHeader }], Path [{ this.Request.Path }], Query String [{ this.Request.QueryString }], Response Error Code [{ response?.ErrorCode }], Request [{ JsonConvert.SerializeObject(request) }] ResponseType [{ responseType }]");
|
2018-11-22 17:31:59 +00:00
|
|
|
|
if (response?.ErrorCode.HasValue ?? false)
|
2018-11-19 23:51:58 +00:00
|
|
|
|
{
|
2018-11-23 04:18:48 +00:00
|
|
|
|
return this.SendError(request, response);
|
2018-11-22 17:31:59 +00:00
|
|
|
|
}
|
|
|
|
|
if (request.IsJSONRequest)
|
|
|
|
|
{
|
|
|
|
|
this.Response.ContentType = "application/json";
|
|
|
|
|
var status = response?.Data?.status.ToString();
|
|
|
|
|
var version = response?.Data?.version ?? Roadie.Api.Services.SubsonicService.SubsonicVersion;
|
|
|
|
|
string jsonResult = null;
|
|
|
|
|
if (responseType == null)
|
|
|
|
|
{
|
|
|
|
|
jsonResult = "{ \"subsonic-response\": { \"status\":\"" + status + "\", \"version\": \"" + version + "\" }}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
jsonResult = "{ \"subsonic-response\": { \"status\":\"" + status + "\", \"version\": \"" + version + "\", \"" + responseType + "\":" + (response?.Data != null ? JsonConvert.SerializeObject(response.Data.Item) : string.Empty) + "}}";
|
|
|
|
|
}
|
2018-11-23 04:18:48 +00:00
|
|
|
|
if ((request?.f ?? string.Empty).Equals("jsonp", StringComparison.OrdinalIgnoreCase))
|
2018-11-22 17:31:59 +00:00
|
|
|
|
{
|
|
|
|
|
jsonResult = request.callback + "(" + jsonResult + ");";
|
|
|
|
|
}
|
|
|
|
|
return Content(jsonResult);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
}
|
2018-11-22 17:31:59 +00:00
|
|
|
|
this.Response.ContentType = "application/xml";
|
|
|
|
|
return Ok(response.Data);
|
2018-11-22 13:48:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-23 04:18:48 +00:00
|
|
|
|
private IActionResult SendError(SubsonicRequest request, SubsonicOperationResult<Response> response = null)
|
2018-11-22 13:48:32 +00:00
|
|
|
|
{
|
|
|
|
|
var version = response?.Data?.version ?? Roadie.Api.Services.SubsonicService.SubsonicVersion;
|
|
|
|
|
string errorDescription = response?.ErrorCode?.DescriptionAttr();
|
|
|
|
|
int? errorCode = (int?)response?.ErrorCode;
|
|
|
|
|
if (request.IsJSONRequest)
|
|
|
|
|
{
|
|
|
|
|
this.Response.ContentType = "application/json";
|
|
|
|
|
return Content("{ \"subsonic-response\": { \"status\":\"failed\", \"version\": \"" + version + "\", \"error\":{\"code\":\"" + errorCode + "\",\"message\":\"" + errorDescription + "\"}}}");
|
2018-11-19 23:51:58 +00:00
|
|
|
|
}
|
2018-11-22 13:48:32 +00:00
|
|
|
|
this.Response.ContentType = "application/xml";
|
|
|
|
|
return Content($"<?xml version=\"1.0\" encoding=\"UTF-8\"?><subsonic-response xmlns=\"http://subsonic.org/restapi\" status=\"failed\" version=\"{ version }\"><error code=\"{ errorCode }\" message=\"{ errorDescription }\"/></subsonic-response>");
|
2018-11-19 23:51:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion Response Builder Methods
|
2018-11-15 15:10:29 +00:00
|
|
|
|
}
|
2018-11-16 03:37:00 +00:00
|
|
|
|
}
|