parent
3504cbe9cd
commit
821aa90b14
@ -1,4 +1,4 @@
|
|||||||
.playlistInputWrapper {
|
.bookshelfInputWrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ImportLists.Goodreads
|
||||||
|
{
|
||||||
|
public class GoodreadsBookshelfImportListSettingsValidator : GoodreadsSettingsBaseValidator<GoodreadsBookshelfImportListSettings>
|
||||||
|
{
|
||||||
|
public GoodreadsBookshelfImportListSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.BookshelfIds).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GoodreadsBookshelfImportListSettings : GoodreadsSettingsBase<GoodreadsBookshelfImportListSettings>
|
||||||
|
{
|
||||||
|
public GoodreadsBookshelfImportListSettings()
|
||||||
|
{
|
||||||
|
BookshelfIds = new string[] { };
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Bookshelves", Type = FieldType.Bookshelf)]
|
||||||
|
public IEnumerable<string> BookshelfIds { get; set; }
|
||||||
|
|
||||||
|
protected override AbstractValidator<GoodreadsBookshelfImportListSettings> Validator => new GoodreadsBookshelfImportListSettingsValidator();
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.Annotations;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.ImportLists.Goodreads
|
|
||||||
{
|
|
||||||
public class GoodreadsBookshelfSettingsValidator : GoodreadsSettingsBaseValidator<GoodreadsBookshelfSettings>
|
|
||||||
{
|
|
||||||
public GoodreadsBookshelfSettingsValidator()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
RuleFor(c => c.PlaylistIds).NotEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GoodreadsBookshelfSettings : GoodreadsSettingsBase<GoodreadsBookshelfSettings>
|
|
||||||
{
|
|
||||||
public GoodreadsBookshelfSettings()
|
|
||||||
{
|
|
||||||
PlaylistIds = new string[] { };
|
|
||||||
}
|
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "Bookshelves", Type = FieldType.Playlist)]
|
|
||||||
public IEnumerable<string> PlaylistIds { get; set; }
|
|
||||||
|
|
||||||
protected override AbstractValidator<GoodreadsBookshelfSettings> Validator => new GoodreadsBookshelfSettingsValidator();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,136 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.MetadataSource.Goodreads;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Goodreads
|
||||||
|
{
|
||||||
|
public class GoodreadsBookshelf : GoodreadsNotificationBase<GoodreadsBookshelfNotificationSettings>
|
||||||
|
{
|
||||||
|
public GoodreadsBookshelf(IHttpClient httpClient,
|
||||||
|
Logger logger)
|
||||||
|
: base(httpClient, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "Goodreads Bookshelves";
|
||||||
|
public override string Link => "https://goodreads.com/";
|
||||||
|
|
||||||
|
public override void OnReleaseImport(BookDownloadMessage message)
|
||||||
|
{
|
||||||
|
var bookId = message.Book.Editions.Value.Single(x => x.Monitored).ForeignEditionId;
|
||||||
|
RemoveBookFromShelves(bookId, Settings.RemoveIds);
|
||||||
|
AddToShelves(bookId, Settings.AddIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||||
|
{
|
||||||
|
if (action == "getBookshelves")
|
||||||
|
{
|
||||||
|
if (Settings.AccessToken.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
shelves = new List<object>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings.Validate().Filter("AccessToken").ThrowOnError();
|
||||||
|
|
||||||
|
var shelves = new List<UserShelfResource>();
|
||||||
|
var page = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var curr = GetShelfList(++page);
|
||||||
|
if (curr == null || curr.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
shelves.AddRange(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Trace($"Name: {query["name"]} {query["name"] == "removeIds"}");
|
||||||
|
|
||||||
|
var helptext = new
|
||||||
|
{
|
||||||
|
addIds = $"Add imported book to {Settings.UserName}'s shelves:",
|
||||||
|
removeIds = $"Remove imported book from {Settings.UserName}'s shelves:"
|
||||||
|
};
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
options = new
|
||||||
|
{
|
||||||
|
helptext,
|
||||||
|
user = Settings.UserName,
|
||||||
|
shelves = shelves.OrderBy(p => p.Name)
|
||||||
|
.Select(p => new
|
||||||
|
{
|
||||||
|
id = p.Name,
|
||||||
|
name = p.Name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return base.RequestAction(action, query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<UserShelfResource> GetShelfList(int page)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var builder = RequestBuilder()
|
||||||
|
.SetSegment("route", $"shelf/list.xml")
|
||||||
|
.AddQueryParam("user_id", Settings.UserId)
|
||||||
|
.AddQueryParam("page", page);
|
||||||
|
|
||||||
|
var httpResponse = OAuthExecute(builder);
|
||||||
|
|
||||||
|
return httpResponse.Deserialize<PaginatedList<UserShelfResource>>("shelves").List;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Error fetching bookshelves from Goodreads");
|
||||||
|
return new List<UserShelfResource>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveBookFromShelves(string bookId, IEnumerable<string> shelves)
|
||||||
|
{
|
||||||
|
foreach (var shelf in shelves)
|
||||||
|
{
|
||||||
|
var req = RequestBuilder()
|
||||||
|
.Post()
|
||||||
|
.SetSegment("route", "shelf/add_to_shelf.xml")
|
||||||
|
.AddFormParameter("name", shelf)
|
||||||
|
.AddFormParameter("book_id", bookId)
|
||||||
|
.AddFormParameter("a", "remove");
|
||||||
|
|
||||||
|
// in case not found in shelf
|
||||||
|
req.SuppressHttpError = true;
|
||||||
|
|
||||||
|
OAuthExecute(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddToShelves(string bookId, IEnumerable<string> shelves)
|
||||||
|
{
|
||||||
|
var req = RequestBuilder()
|
||||||
|
.Post()
|
||||||
|
.SetSegment("route", "shelf/add_books_to_shelves.xml")
|
||||||
|
.AddFormParameter("bookids", bookId)
|
||||||
|
.AddFormParameter("shelves", shelves.ConcatToString());
|
||||||
|
|
||||||
|
OAuthExecute(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Goodreads
|
||||||
|
{
|
||||||
|
public class GoodreadsBookshelfNotificationSettingsValidator : GoodreadsSettingsBaseValidator<GoodreadsBookshelfNotificationSettings>
|
||||||
|
{
|
||||||
|
public GoodreadsBookshelfNotificationSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.RemoveIds).NotEmpty().When(c => !c.AddIds.Any());
|
||||||
|
RuleFor(c => c.AddIds).NotEmpty().When(c => !c.RemoveIds.Any());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GoodreadsBookshelfNotificationSettings : GoodreadsSettingsBase<GoodreadsBookshelfNotificationSettings>
|
||||||
|
{
|
||||||
|
private static readonly GoodreadsBookshelfNotificationSettingsValidator Validator = new GoodreadsBookshelfNotificationSettingsValidator();
|
||||||
|
|
||||||
|
public GoodreadsBookshelfNotificationSettings()
|
||||||
|
{
|
||||||
|
RemoveIds = new string[] { };
|
||||||
|
AddIds = new string[] { };
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Remove from Bookshelves", Type = FieldType.Bookshelf)]
|
||||||
|
public IEnumerable<string> RemoveIds { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Add to Bookshelves", Type = FieldType.Bookshelf)]
|
||||||
|
public IEnumerable<string> AddIds { get; set; }
|
||||||
|
|
||||||
|
public override NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,198 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Web;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Xml.XPath;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Common.OAuth;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
using NzbDrone.Core.Exceptions;
|
||||||
|
using NzbDrone.Core.ImportLists.Goodreads;
|
||||||
|
using NzbDrone.Core.MetadataSource.Goodreads;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Goodreads
|
||||||
|
{
|
||||||
|
public abstract class GoodreadsNotificationBase<TSettings> : NotificationBase<TSettings>
|
||||||
|
where TSettings : GoodreadsSettingsBase<TSettings>, new()
|
||||||
|
{
|
||||||
|
protected readonly IHttpClient _httpClient;
|
||||||
|
protected readonly Logger _logger;
|
||||||
|
|
||||||
|
protected GoodreadsNotificationBase(IHttpClient httpClient,
|
||||||
|
Logger logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Link => "https://goodreads.com/";
|
||||||
|
|
||||||
|
public override ValidationResult Test()
|
||||||
|
{
|
||||||
|
var failures = new List<ValidationFailure>();
|
||||||
|
|
||||||
|
failures.AddIfNotNull(TestConnection());
|
||||||
|
|
||||||
|
return new ValidationResult(failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||||
|
{
|
||||||
|
if (action == "startOAuth")
|
||||||
|
{
|
||||||
|
if (query["callbackUrl"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam callbackUrl invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var oAuthRequest = OAuthRequest.ForRequestToken(null, null, query["callbackUrl"]);
|
||||||
|
oAuthRequest.RequestUrl = Settings.OAuthRequestTokenUrl;
|
||||||
|
var qscoll = OAuthQuery(oAuthRequest);
|
||||||
|
|
||||||
|
var url = string.Format("{0}?oauth_token={1}&oauth_callback={2}", Settings.OAuthUrl, qscoll["oauth_token"], query["callbackUrl"]);
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
OauthUrl = url,
|
||||||
|
RequestTokenSecret = qscoll["oauth_token_secret"]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (action == "getOAuthToken")
|
||||||
|
{
|
||||||
|
if (query["oauth_token"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("QueryParam oauth_token invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query["requestTokenSecret"].IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Missing requestTokenSecret.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var oAuthRequest = OAuthRequest.ForAccessToken(null, null, query["oauth_token"], query["requestTokenSecret"], "");
|
||||||
|
oAuthRequest.RequestUrl = Settings.OAuthAccessTokenUrl;
|
||||||
|
var qscoll = OAuthQuery(oAuthRequest);
|
||||||
|
|
||||||
|
Settings.AccessToken = qscoll["oauth_token"];
|
||||||
|
Settings.AccessTokenSecret = qscoll["oauth_token_secret"];
|
||||||
|
|
||||||
|
var user = GetUser();
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
Settings.AccessToken,
|
||||||
|
Settings.AccessTokenSecret,
|
||||||
|
RequestTokenSecret = "",
|
||||||
|
UserId = user.Item1,
|
||||||
|
UserName = user.Item2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new { };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpRequestBuilder RequestBuilder()
|
||||||
|
{
|
||||||
|
return new HttpRequestBuilder("https://www.goodreads.com/{route}").KeepAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Common.Http.HttpResponse OAuthExecute(HttpRequestBuilder builder)
|
||||||
|
{
|
||||||
|
var auth = OAuthRequest.ForProtectedResource(builder.Method.ToString(), null, null, Settings.AccessToken, Settings.AccessTokenSecret);
|
||||||
|
|
||||||
|
var request = builder.Build();
|
||||||
|
request.LogResponseContent = true;
|
||||||
|
|
||||||
|
// we need the url without the query to sign
|
||||||
|
auth.RequestUrl = request.Url.SetQuery(null).FullUri;
|
||||||
|
|
||||||
|
if (builder.Method == HttpMethod.GET)
|
||||||
|
{
|
||||||
|
auth.Parameters = builder.QueryParams.ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
}
|
||||||
|
else if (builder.Method == HttpMethod.POST)
|
||||||
|
{
|
||||||
|
auth.Parameters = builder.FormData.ToDictionary(x => x.Name, x => Encoding.UTF8.GetString(x.ContentData));
|
||||||
|
}
|
||||||
|
|
||||||
|
var header = GetAuthorizationHeader(auth);
|
||||||
|
request.Headers.Add("Authorization", header);
|
||||||
|
|
||||||
|
return _httpClient.Execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValidationFailure TestConnection()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GetUser();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (Common.Http.HttpException ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Goodreads Authentication Error");
|
||||||
|
return new ValidationFailure(string.Empty, $"Goodreads authentication error: {ex.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Unable to connect to Goodreads");
|
||||||
|
|
||||||
|
return new ValidationFailure(string.Empty, "Unable to connect to Goodreads, check the log for more details");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tuple<string, string> GetUser()
|
||||||
|
{
|
||||||
|
var builder = RequestBuilder().SetSegment("route", "api/auth_user");
|
||||||
|
|
||||||
|
var httpResponse = OAuthExecute(builder);
|
||||||
|
|
||||||
|
string userId = null;
|
||||||
|
string userName = null;
|
||||||
|
|
||||||
|
var content = httpResponse.Content;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(content))
|
||||||
|
{
|
||||||
|
var user = XDocument.Parse(content).XPathSelectElement("GoodreadsResponse/user");
|
||||||
|
userId = user.AttributeAsString("id");
|
||||||
|
userName = user.ElementAsString("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tuple.Create(userId, userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAuthorizationHeader(OAuthRequest oAuthRequest)
|
||||||
|
{
|
||||||
|
var request = new Common.Http.HttpRequest(Settings.SigningUrl)
|
||||||
|
{
|
||||||
|
Method = HttpMethod.POST,
|
||||||
|
};
|
||||||
|
request.Headers.Set("Content-Type", "application/json");
|
||||||
|
|
||||||
|
var payload = oAuthRequest.ToJson();
|
||||||
|
_logger.Trace(payload);
|
||||||
|
request.SetContent(payload);
|
||||||
|
|
||||||
|
var response = _httpClient.Post<AuthorizationHeader>(request).Resource;
|
||||||
|
|
||||||
|
return response.Authorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NameValueCollection OAuthQuery(OAuthRequest oAuthRequest)
|
||||||
|
{
|
||||||
|
var auth = GetAuthorizationHeader(oAuthRequest);
|
||||||
|
var request = new Common.Http.HttpRequest(oAuthRequest.RequestUrl);
|
||||||
|
request.Headers.Add("Authorization", auth);
|
||||||
|
var response = _httpClient.Get(request);
|
||||||
|
|
||||||
|
return HttpUtility.ParseQueryString(response.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Goodreads
|
||||||
|
{
|
||||||
|
public class GoodreadsSettingsBaseValidator<TSettings> : AbstractValidator<TSettings>
|
||||||
|
where TSettings : GoodreadsSettingsBase<TSettings>
|
||||||
|
{
|
||||||
|
public GoodreadsSettingsBaseValidator()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.AccessToken).NotEmpty();
|
||||||
|
RuleFor(c => c.AccessTokenSecret).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class GoodreadsSettingsBase<TSettings> : IProviderConfig
|
||||||
|
where TSettings : GoodreadsSettingsBase<TSettings>
|
||||||
|
{
|
||||||
|
public GoodreadsSettingsBase()
|
||||||
|
{
|
||||||
|
SignIn = "startOAuth";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SigningUrl => "https://auth.servarr.com/v1/goodreads/sign";
|
||||||
|
public string OAuthUrl => "https://www.goodreads.com/oauth/authorize";
|
||||||
|
public string OAuthRequestTokenUrl => "https://www.goodreads.com/oauth/request_token";
|
||||||
|
public string OAuthAccessTokenUrl => "https://www.goodreads.com/oauth/access_token";
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Access Token Secret", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string AccessTokenSecret { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Request Token Secret", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string RequestTokenSecret { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "User Id", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string UserId { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "User Name", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(99, Label = "Authenticate with Goodreads", Type = FieldType.OAuth)]
|
||||||
|
public string SignIn { get; set; }
|
||||||
|
|
||||||
|
public bool IsValid => !string.IsNullOrWhiteSpace(AccessTokenSecret);
|
||||||
|
|
||||||
|
public abstract NzbDroneValidationResult Validate();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Goodreads
|
||||||
|
{
|
||||||
|
public class GoodreadsOwnedBooks : GoodreadsNotificationBase<GoodreadsOwnedBooksNotificationSettings>
|
||||||
|
{
|
||||||
|
public GoodreadsOwnedBooks(IHttpClient httpClient,
|
||||||
|
Logger logger)
|
||||||
|
: base(httpClient, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "Goodreads Owned Books";
|
||||||
|
public override string Link => "https://goodreads.com/";
|
||||||
|
|
||||||
|
public override void OnReleaseImport(BookDownloadMessage message)
|
||||||
|
{
|
||||||
|
var bookId = message.Book.Editions.Value.Single(x => x.Monitored).ForeignEditionId;
|
||||||
|
AddOwnedBook(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddOwnedBook(string bookId)
|
||||||
|
{
|
||||||
|
var req = RequestBuilder()
|
||||||
|
.Post()
|
||||||
|
.SetSegment("route", "owned_books.xml")
|
||||||
|
.AddFormParameter("owned_book[book_id]", bookId)
|
||||||
|
.AddFormParameter("owned_book[condition_code]", Settings.Condition)
|
||||||
|
.AddFormParameter("owned_book[original_purchase_date]", DateTime.Now.ToString("O"));
|
||||||
|
|
||||||
|
if (Settings.Description.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
req.AddFormParameter("owned_book[condition_description]", Settings.Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.Location.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
req.AddFormParameter("owned_book[original_purchase_location]", Settings.Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuthExecute(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Goodreads
|
||||||
|
{
|
||||||
|
public enum OwnedBookCondition
|
||||||
|
{
|
||||||
|
BrandNew = 10,
|
||||||
|
LikeNew = 20,
|
||||||
|
VeryGood = 30,
|
||||||
|
Good = 40,
|
||||||
|
Acceptable = 50,
|
||||||
|
Poor = 60
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GoodreadsOwnedBooksNotificationSettings : GoodreadsSettingsBase<GoodreadsOwnedBooksNotificationSettings>
|
||||||
|
{
|
||||||
|
private static readonly GoodreadsSettingsBaseValidator<GoodreadsOwnedBooksNotificationSettings> Validator = new GoodreadsSettingsBaseValidator<GoodreadsOwnedBooksNotificationSettings>();
|
||||||
|
|
||||||
|
public GoodreadsOwnedBooksNotificationSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Condition", Type = FieldType.Select, SelectOptions = typeof(OwnedBookCondition))]
|
||||||
|
public int Condition { get; set; } = (int)OwnedBookCondition.BrandNew;
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Condition Description", Type = FieldType.Textbox)]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Purchase Location", HelpText = "Will be displayed on Goodreads website", Type = FieldType.Textbox)]
|
||||||
|
public string Location { get; set; }
|
||||||
|
|
||||||
|
public override NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue