Finished the bulk work for Headphones. Needs testing #32

pull/140/head
tidusjar 9 years ago
parent 9211f3db4f
commit 39a5032c3e

@ -33,6 +33,7 @@ namespace PlexRequests.Api.Interfaces
public interface IApiRequest
{
T Execute<T>(IRestRequest request, Uri baseUri) where T : new();
IRestResponse Execute(IRestRequest request, Uri baseUri);
T ExecuteXml<T>(IRestRequest request, Uri baseUri) where T : class;
T ExecuteJson<T>(IRestRequest request, Uri baseUri) where T : new();
}

@ -25,13 +25,19 @@
// ************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Api.Models.Music;
namespace PlexRequests.Api.Interfaces
{
public interface IHeadphonesApi
{
bool AddAlbum(string apiKey, Uri baseUrl, string albumId);
Task<bool> AddAlbum(string apiKey, Uri baseUrl, string albumId);
HeadphonesVersion GetVersion(string apiKey, Uri baseUrl);
Task<bool> AddArtist(string apiKey, Uri baseUrl, string artistId);
Task<bool> QueueAlbum(string apiKey, Uri baseUrl, string albumId);
Task<List<HeadphonesGetIndex>> GetIndex(string apiKey, Uri baseUrl);
}
}

@ -0,0 +1,49 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: HeadphonesGetIndex.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
namespace PlexRequests.Api.Models.Music
{
public class HeadphonesGetIndex
{
public string Status { get; set; }
public string ThumbURL { get; set; }
public string DateAdded { get; set; }
public string MetaCritic { get; set; }
public int? TotalTracks { get; set; }
public object Type { get; set; }
public int? IncludeExtras { get; set; }
public string ArtistName { get; set; }
public string LastUpdated { get; set; }
public string ReleaseDate { get; set; }
public string AlbumID { get; set; }
public string ArtistID { get; set; }
public string ArtworkURL { get; set; }
public string Extras { get; set; }
public int? HaveTracks { get; set; }
public string LatestAlbum { get; set; }
public string ArtistSortName { get; set; }
}
}

@ -42,6 +42,8 @@ namespace PlexRequests.Api.Models.Music
public class MusicBrainzReleaseInfo
{
[JsonProperty(PropertyName = "artist-credit")]
public List<ArtistCredit> ArtistCredits { get; set; }
public string date { get; set; }
public string status { get; set; }
public string asin { get; set; }

@ -63,6 +63,8 @@ namespace PlexRequests.Api.Models.Music
public class ArtistCredit
{
public Artist artist { get; set; }
public string name { get; set; }
public string joinphrase { get; set; }
}
public class ReleaseGroup

@ -50,6 +50,7 @@
<Compile Include="Movie\CouchPotatoStatus.cs" />
<Compile Include="Music\HeadphonesAlbumSearchResult.cs" />
<Compile Include="Music\HeadphonesArtistSearchResult.cs" />
<Compile Include="Music\HeadphonesGetIndex.cs" />
<Compile Include="Music\HeadphonesVersion.cs" />
<Compile Include="Music\MusicBrainzCoverArt.cs" />
<Compile Include="Music\MusicBrainzReleaseInfo.cs" />

@ -64,6 +64,21 @@ namespace PlexRequests.Api
}
public IRestResponse Execute(IRestRequest request, Uri baseUri)
{
var client = new RestClient { BaseUrl = baseUri };
var response = client.Execute(request);
if (response.ErrorException != null)
{
var message = "Error retrieving response. Check inner details for more info.";
throw new ApplicationException(message, response.ErrorException);
}
return response;
}
public T ExecuteXml<T>(IRestRequest request, Uri baseUri) where T : class
{
var client = new RestClient { BaseUrl = baseUri };

@ -26,6 +26,7 @@
#endregion
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
@ -33,6 +34,7 @@ using NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Music;
using PlexRequests.Helpers;
using RestSharp;
@ -47,7 +49,7 @@ namespace PlexRequests.Api
private ApiRequest Api { get; }
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public bool AddAlbum(string apiKey, Uri baseUrl, string albumId)
public async Task<bool> AddAlbum(string apiKey, Uri baseUrl, string albumId)
{
Log.Trace("Adding album: {0}", albumId);
var request = new RestRequest
@ -61,12 +63,91 @@ namespace PlexRequests.Api
try
{
//var result = Api.Execute<string>(request, baseUrl);
return false;
var result = await Task.Run(() => Api.Execute(request, baseUrl)).ConfigureAwait(false);
Log.Trace("Add Album Result: {0}", result.DumpJson());
var albumResult = result.Content.Equals("OK", StringComparison.CurrentCultureIgnoreCase);
Log.Info("Album add result {0}", albumResult);
return albumResult;
}
catch (JsonSerializationException jse)
{
Log.Error(jse);
return false; // If there is no matching result we do not get returned a JSON string, it just returns "false".
}
}
public async Task<List<HeadphonesGetIndex>> GetIndex(string apiKey, Uri baseUrl)
{
var request = new RestRequest
{
Resource = "/api",
Method = Method.GET
};
request.AddQueryParameter("apikey", apiKey);
request.AddQueryParameter("cmd", "getIndex");
try
{
var result = await Task.Run(() => Api.ExecuteJson<List<HeadphonesGetIndex>>(request, baseUrl)).ConfigureAwait(false);
return result;
}
catch (JsonSerializationException jse)
{
Log.Error(jse);
return new List<HeadphonesGetIndex>();
}
}
public async Task<bool> AddArtist(string apiKey, Uri baseUrl, string artistId)
{
Log.Trace("Adding Artist: {0}", artistId);
var request = new RestRequest
{
Resource = "/api",
Method = Method.GET
};
request.AddQueryParameter("apikey", apiKey);
request.AddQueryParameter("cmd", "addArtist");
request.AddQueryParameter("id", artistId);
try
{
var result = await Task.Run(() => Api.Execute(request, baseUrl)).ConfigureAwait(false);
Log.Info("Add Artist Result: {0}", result.Content);
Log.Trace("Add Artist Result: {0}", result.DumpJson());
return result.Content.Equals("OK", StringComparison.CurrentCultureIgnoreCase);
}
catch (JsonSerializationException jse)
{
Log.Error(jse);
return false; // If there is no matching result we do not get returned a JSON string, it just returns "false".
}
}
public async Task<bool> QueueAlbum(string apiKey, Uri baseUrl, string albumId)
{
Log.Trace("Queing album: {0}", albumId);
var request = new RestRequest
{
Resource = "/api?cmd=queueAlbum&id={albumId}",
Method = Method.GET
};
request.AddQueryParameter("apikey", apiKey);
request.AddUrlSegment("albumId", albumId);
try
{
var result = await Task.Run(() => Api.Execute(request, baseUrl)).ConfigureAwait(false);
Log.Info("Queue Result: {0}", result.Content);
Log.Trace("Queue Result: {0}", result.DumpJson());
return result.Content.Equals("OK", StringComparison.CurrentCultureIgnoreCase);
}
catch (JsonSerializationException jse)
{
Log.Warn(jse);
Log.Error(jse);
return false; // If there is no matching result we do not get returned a JSON string, it just returns "false".
}
}
@ -88,7 +169,7 @@ namespace PlexRequests.Api
}
catch (JsonSerializationException jse)
{
Log.Warn(jse);
Log.Error(jse);
return new HeadphonesVersion(); // If there is no matching result we do not get returned a JSON string, it just returns "false".
}
}

@ -73,10 +73,12 @@ namespace PlexRequests.Api
Log.Trace("Getting album: {0}", releaseId);
var request = new RestRequest
{
Resource = "release/{albumId}?fmt=json",
Resource = "release/{albumId}",
Method = Method.GET
};
request.AddUrlSegment("albumId", releaseId);
request.AddQueryParameter("fmt", "json");
request.AddQueryParameter("inc", "artists");
try
{

@ -38,7 +38,8 @@ namespace PlexRequests.Store
public string SeasonsRequested { get; set; }
public string MusicBrainzId { get; set; }
public List<string> RequestedUsers { get; set; }
public string Artist { get; set; }
public string ArtistName { get; set; }
public string ArtistId { get; set; }
[JsonIgnore]
public List<string> AllUsers

@ -0,0 +1,151 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: HeadphonesSender.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Store;
namespace PlexRequests.UI.Helpers
{
public class HeadphonesSender
{
public HeadphonesSender(IHeadphonesApi api, HeadphonesSettings settings, IRequestService request)
{
Api = api;
Settings = settings;
RequestService = request;
}
private int WaitTime => 1000;
private int CounterMax => 30;
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
private IHeadphonesApi Api { get; }
private IRequestService RequestService { get; }
private HeadphonesSettings Settings { get; }
public async Task<bool> AddAlbum(RequestedModel request)
{
var addArtistResult = await AddArtist(request);
if (!addArtistResult)
{
return false;
}
// Artist is now active
// Add album
var albumResult = await Api.AddAlbum(Settings.ApiKey, Settings.FullUri, request.MusicBrainzId);
if (!albumResult)
{
Log.Error("Couldn't add the album to headphones");
}
// Set the status to wanted and search
var status = await SetAlbumStatus(request);
if (!status)
{
return false;
}
// Approve it
request.Approved = true;
// Update the record
var updated = RequestService.UpdateRequest(request);
return updated;
}
private async Task<bool> AddArtist(RequestedModel request)
{
var index = await Api.GetIndex(Settings.ApiKey, Settings.FullUri);
var artistExists = index.Any(x => x.ArtistID == request.ArtistId);
if (!artistExists)
{
var artistAdd = Api.AddArtist(Settings.ApiKey, Settings.FullUri, request.ArtistId);
Log.Info("Artist add result : {0}", artistAdd);
}
var counter = 0;
while (index.All(x => x.ArtistID != request.ArtistId))
{
Thread.Sleep(WaitTime);
counter++;
Log.Trace("Artist is still not present in the index. Counter = {0}", counter);
index = await Api.GetIndex(Settings.ApiKey, Settings.FullUri);
if (counter > CounterMax)
{
Log.Trace("Artist is still not present in the index. Counter = {0}. Returning false", counter);
return false;
}
}
counter = 0;
var artistStatus = index.Where(x => x.ArtistID == request.ArtistId).Select(x => x.Status).FirstOrDefault();
while (artistStatus != "Active")
{
Thread.Sleep(WaitTime);
counter++;
Log.Trace("Artist status {1}. Counter = {0}", counter, artistStatus);
index = await Api.GetIndex(Settings.ApiKey, Settings.FullUri);
artistStatus = index.Where(x => x.ArtistID == request.ArtistId).Select(x => x.Status).FirstOrDefault();
if (counter > CounterMax)
{
Log.Trace("Artist status is still not active. Counter = {0}. Returning false", counter);
return false;
}
}
return true;
}
private async Task<bool> SetAlbumStatus(RequestedModel request)
{
var counter = 0;
var setStatus = await Api.QueueAlbum(Settings.ApiKey, Settings.FullUri, request.MusicBrainzId);
while (!setStatus)
{
Thread.Sleep(WaitTime);
counter++;
Log.Trace("Setting Album status. Counter = {0}", counter);
setStatus = await Api.QueueAlbum(Settings.ApiKey, Settings.FullUri, request.MusicBrainzId);
if (counter > CounterMax)
{
Log.Trace("Album status is still not active. Counter = {0}. Returning false", counter);
return false;
}
}
return true;
}
}
}

@ -47,7 +47,8 @@ namespace PlexRequests.UI.Modules
{
public ApprovalModule(IRequestService service, ISettingsService<CouchPotatoSettings> cpService, ICouchPotatoApi cpApi, ISonarrApi sonarrApi,
ISettingsService<SonarrSettings> sonarrSettings, ISickRageApi srApi, ISettingsService<SickRageSettings> srSettings) : base("approval")
ISettingsService<SonarrSettings> sonarrSettings, ISickRageApi srApi, ISettingsService<SickRageSettings> srSettings,
ISettingsService<HeadphonesSettings> hpSettings, IHeadphonesApi hpApi) : base("approval")
{
this.RequiresAuthentication();
@ -58,6 +59,8 @@ namespace PlexRequests.UI.Modules
SonarrSettings = sonarrSettings;
SickRageApi = srApi;
SickRageSettings = srSettings;
HeadphonesSettings = hpSettings;
HeadphoneApi = hpApi;
Post["/approve"] = parameters => Approve((int)Request.Form.requestid, (string)Request.Form.qualityId);
Post["/approveall"] = x => ApproveAll();
@ -71,9 +74,11 @@ namespace PlexRequests.UI.Modules
private ISettingsService<SonarrSettings> SonarrSettings { get; }
private ISettingsService<SickRageSettings> SickRageSettings { get; }
private ISettingsService<CouchPotatoSettings> CpService { get; }
private ISettingsService<HeadphonesSettings> HeadphonesSettings { get; }
private ISonarrApi SonarrApi { get; }
private ISickRageApi SickRageApi { get; }
private ICouchPotatoApi CpApi { get; }
private IHeadphonesApi HeadphoneApi { get; }
/// <summary>
/// Approves the specified request identifier.
@ -102,6 +107,8 @@ namespace PlexRequests.UI.Modules
return RequestMovieAndUpdateStatus(request, qualityId);
case RequestType.TvShow:
return RequestTvAndUpdateStatus(request, qualityId);
case RequestType.Album:
return RequestAlbumAndUpdateStatus(request);
default:
throw new ArgumentOutOfRangeException(nameof(request));
}
@ -190,7 +197,7 @@ namespace PlexRequests.UI.Modules
Message = "We could not approve this request. Please try again or check the logs."
});
}
var result = cp.AddMovie(request.ImdbId, cpSettings.ApiKey, request.Title, cpSettings.FullUri, string.IsNullOrEmpty(qualityId) ? cpSettings.ProfileId : qualityId);
Log.Trace("Adding movie to CP result {0}", result);
if (result)
@ -219,6 +226,34 @@ namespace PlexRequests.UI.Modules
});
}
private Response RequestAlbumAndUpdateStatus(RequestedModel request)
{
var hpSettings = HeadphonesSettings.GetSettings();
Log.Info("Adding album to Headphones : {0}", request.Title);
if (!hpSettings.Enabled)
{
// Approve it
request.Approved = true;
Log.Warn("We approved Album: {0} but could not add it to Headphones because it has not been setup", request.Title);
// Update the record
var inserted = Service.UpdateRequest(request);
return Response.AsJson(inserted
? new JsonResponseModel { Result = true, Message = "This has been approved, but It has not been sent to Headphones because it has not been configured." }
: new JsonResponseModel
{
Result = false,
Message = "We could not approve this request. Please try again or check the logs."
});
}
var sender = new HeadphonesSender(HeadphoneApi, hpSettings, Service);
var result = sender.AddAlbum(request);
return Response.AsJson( new JsonResponseModel { Result = true, Message = "We have sent the approval to Headphones for processing, This can take a few minutes."} );
}
private Response ApproveAllMovies()
{
if (!Context.CurrentUser.IsAuthenticated())
@ -358,10 +393,10 @@ namespace PlexRequests.UI.Modules
{
var result = Service.BatchUpdate(updatedRequests);
return Response.AsJson(result
? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." });
}
return Response.AsJson(result
? new JsonResponseModel { Result = true }
: new JsonResponseModel { Result = false, Message = "We could not approve all of the requests. Please try again or check the logs." });
}
catch (Exception e)
{
Log.Fatal(e);

@ -505,6 +505,12 @@ namespace PlexRequests.UI.Modules
DateTime release;
DateTimeHelper.CustomParse(albumInfo.ReleaseEvents?.FirstOrDefault()?.date, out release);
var artist = albumInfo.ArtistCredits?.FirstOrDefault()?.artist;
if (artist == null)
{
return Response.AsJson("We could not find the artist on MusicBrainz. Please try again later or contact your admin");
}
var model = new RequestedModel
{
Title = albumInfo.title,
@ -513,12 +519,13 @@ namespace PlexRequests.UI.Modules
PosterPath = img,
Type = RequestType.Album,
ProviderId = 0,
RequestedUsers = new List<string>() { Username },
RequestedUsers = new List<string> { Username },
Status = albumInfo.status,
Issues = IssueState.None,
RequestedDate = DateTime.UtcNow,
ReleaseDate = release,
// Artist = albumInfo.
ArtistName = artist.name,
ArtistId = artist.id
};
@ -542,24 +549,14 @@ namespace PlexRequests.UI.Modules
});
}
var headphonesResult = HeadphonesApi.AddAlbum(hpSettings.ApiKey, hpSettings.FullUri, model.MusicBrainzId);
Log.Info("Result from adding album to Headphones = {0}", headphonesResult);
RequestService.AddRequest(model);
if (headphonesResult)
{
return
Response.AsJson(new JsonResponseModel
{
Result = true,
Message = $"{model.Title} was successfully added!"
});
}
var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService);
sender.AddAlbum(model);
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"There was a problem adding {model.Title}. Please contact your admin!"
Result = true,
Message = $"{model.Title} was successfully added!"
});
}

@ -168,6 +168,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Bootstrapper.cs" />
<Compile Include="Helpers\HeadphonesSender.cs" />
<Compile Include="Helpers\TvSender.cs" />
<Compile Include="Helpers\ValidationHelper.cs" />
<Compile Include="Models\DatatablesModel.cs" />

@ -54,11 +54,11 @@
}
@if (Context.Request.Path == "/admin/headphones")
{
<a class="list-group-item active" href="/admin/headphones">Headphones</a>
<a class="list-group-item active" href="/admin/headphones">Headphones (Beta)</a>
}
else
{
<a class="list-group-item" href="/admin/headphones">Headphones</a>
<a class="list-group-item" href="/admin/headphones">Headphones (Beta)</a>
}
@if (Context.Request.Path == "/admin/emailnotification")

Loading…
Cancel
Save