Method, Variable, Class Renames in Readarr.Api

pull/33/head
Qstick 4 years ago committed by ta264
parent 8080d375d0
commit ee4e44b81a

@ -21,7 +21,7 @@ namespace NzbDrone.Automation.Test
{
_page.LibraryNavIcon.Click();
_page.WaitForNoSpinner();
_page.Find(By.CssSelector("div[class*='ArtistIndex']")).Should().NotBeNull();
_page.Find(By.CssSelector("div[class*='AuthorIndex']")).Should().NotBeNull();
}
[Test]

@ -80,7 +80,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
private void GivenUnmonitorDeletedTracks(bool enabled)
{
Mocker.GetMock<IConfigService>()
.SetupGet(v => v.AutoUnmonitorPreviouslyDownloadedTracks)
.SetupGet(v => v.AutoUnmonitorPreviouslyDownloadedBooks)
.Returns(enabled);
}

@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
.Returns(_artist);
Mocker.GetMock<ISearchForNzb>()
.Setup(s => s.ArtistSearch(_artist.Id, false, true, false))
.Setup(s => s.AuthorSearch(_artist.Id, false, true, false))
.Returns(new List<DownloadDecision>());
Mocker.GetMock<IProcessDownloadDecisions>()
@ -46,7 +46,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
Subject.Execute(new AuthorSearchCommand { AuthorId = _artist.Id, Trigger = CommandTrigger.Manual });
Mocker.GetMock<ISearchForNzb>()
.Verify(v => v.ArtistSearch(_artist.Id, false, true, false),
.Verify(v => v.AuthorSearch(_artist.Id, false, true, false),
Times.Exactly(_artist.Books.Value.Count(s => s.Monitored)));
}
}

@ -105,7 +105,7 @@ namespace NzbDrone.Core.AuthorStats
public void Handle(BookEditedEvent message)
{
_cache.Remove("AllAuthors");
_cache.Remove(message.Album.AuthorId.ToString());
_cache.Remove(message.Book.AuthorId.ToString());
}
[EventHandleOrder(EventHandleOrder.First)]

@ -4,12 +4,12 @@ namespace NzbDrone.Core.Books.Events
{
public class BookEditedEvent : IEvent
{
public Book Album { get; private set; }
public Book Book { get; private set; }
public Book OldAlbum { get; private set; }
public BookEditedEvent(Book book, Book oldAlbum)
{
Album = book;
Book = book;
OldAlbum = oldAlbum;
}
}

@ -78,7 +78,7 @@ namespace NzbDrone.Core.Configuration
return _repository.Get(key.ToLower()) != null;
}
public bool AutoUnmonitorPreviouslyDownloadedTracks
public bool AutoUnmonitorPreviouslyDownloadedBooks
{
get { return GetValueBoolean("AutoUnmonitorPreviouslyDownloadedTracks"); }
set { SetValue("AutoUnmonitorPreviouslyDownloadedTracks", value); }
@ -158,7 +158,7 @@ namespace NzbDrone.Core.Configuration
set { SetValue("RemoveFailedDownloads", value); }
}
public bool CreateEmptyArtistFolders
public bool CreateEmptyAuthorFolders
{
get { return GetValueBoolean("CreateEmptyArtistFolders", false); }

@ -24,11 +24,11 @@ namespace NzbDrone.Core.Configuration
bool RemoveFailedDownloads { get; set; }
//Media Management
bool AutoUnmonitorPreviouslyDownloadedTracks { get; set; }
bool AutoUnmonitorPreviouslyDownloadedBooks { get; set; }
string RecycleBin { get; set; }
int RecycleBinCleanupDays { get; set; }
ProperDownloadTypes DownloadPropersAndRepacks { get; set; }
bool CreateEmptyArtistFolders { get; set; }
bool CreateEmptyAuthorFolders { get; set; }
bool DeleteEmptyFolders { get; set; }
FileDateType FileDate { get; set; }
bool SkipFreeSpaceCheckWhenImporting { get; set; }

@ -33,7 +33,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
public virtual Decision IsSatisfiedBy(RemoteBook subject, SearchCriteriaBase searchCriteria)
{
if (!_configService.AutoUnmonitorPreviouslyDownloadedTracks)
if (!_configService.AutoUnmonitorPreviouslyDownloadedBooks)
{
return Decision.Accept();
}

@ -33,7 +33,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
return false;
}
return new[] { HistoryEventType.DownloadImported, HistoryEventType.TrackFileImported }.Contains(lastHistoryItem.EventType);
return new[] { HistoryEventType.DownloadImported, HistoryEventType.BookFileImported }.Contains(lastHistoryItem.EventType);
});
return allAlbumsImportedInHistory;

@ -257,7 +257,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
}
// Since DownloadComplete is a new event type, we can't assume it exists for old downloads
if (history.EventType == HistoryEventType.TrackFileImported)
if (history.EventType == HistoryEventType.BookFileImported)
{
return DateTime.UtcNow.Subtract(history.Date).TotalSeconds < 60 ? TrackedDownloadState.Importing : TrackedDownloadState.Imported;
}

@ -32,14 +32,14 @@ namespace NzbDrone.Core.History
{
Unknown = 0,
Grabbed = 1,
ArtistFolderImported = 2,
TrackFileImported = 3,
AuthorFolderImported = 2,
BookFileImported = 3,
DownloadFailed = 4,
TrackFileDeleted = 5,
TrackFileRenamed = 6,
BookFileDeleted = 5,
BookFileRenamed = 6,
BookImportIncomplete = 7,
DownloadImported = 8,
TrackFileRetagged = 9,
BookFileRetagged = 9,
DownloadIgnored = 10
}
}

@ -90,7 +90,7 @@ namespace NzbDrone.Core.History
public List<History> FindDownloadHistory(int idAuthorId, QualityModel quality)
{
var allowed = new[] { HistoryEventType.Grabbed, HistoryEventType.DownloadFailed, HistoryEventType.TrackFileImported };
var allowed = new[] { HistoryEventType.Grabbed, HistoryEventType.DownloadFailed, HistoryEventType.BookFileImported };
return Query(h => h.AuthorId == idAuthorId &&
h.Quality == quality &&

@ -215,7 +215,7 @@ namespace NzbDrone.Core.History
var history = new History
{
EventType = HistoryEventType.TrackFileImported,
EventType = HistoryEventType.BookFileImported,
Date = DateTime.UtcNow,
Quality = message.BookInfo.Quality,
SourceTitle = message.ImportedBook.SceneName ?? Path.GetFileNameWithoutExtension(message.BookInfo.Path),
@ -289,7 +289,7 @@ namespace NzbDrone.Core.History
var history = new History
{
EventType = HistoryEventType.TrackFileDeleted,
EventType = HistoryEventType.BookFileDeleted,
Date = DateTime.UtcNow,
Quality = message.BookFile.Quality,
SourceTitle = message.BookFile.Path,
@ -309,7 +309,7 @@ namespace NzbDrone.Core.History
var history = new History
{
EventType = HistoryEventType.TrackFileRenamed,
EventType = HistoryEventType.BookFileRenamed,
Date = DateTime.UtcNow,
Quality = message.BookFile.Quality,
SourceTitle = message.OriginalPath,
@ -329,7 +329,7 @@ namespace NzbDrone.Core.History
var history = new History
{
EventType = HistoryEventType.TrackFileRetagged,
EventType = HistoryEventType.BookFileRetagged,
Date = DateTime.UtcNow,
Quality = message.BookFile.Quality,
SourceTitle = path,

@ -22,7 +22,7 @@ namespace NzbDrone.Core.IndexerSearch
public void Execute(AuthorSearchCommand message)
{
var decisions = _nzbSearchService.ArtistSearch(message.AuthorId, false, message.Trigger == CommandTrigger.Manual, false);
var decisions = _nzbSearchService.AuthorSearch(message.AuthorId, false, message.Trigger == CommandTrigger.Manual, false);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Author search completed. {0} reports downloaded.", processed.Grabbed.Count);

@ -47,7 +47,7 @@ namespace NzbDrone.Core.IndexerSearch
foreach (var book in books)
{
List<DownloadDecision> decisions;
decisions = _nzbSearchService.AlbumSearch(book.Id, false, userInvokedSearch, false);
decisions = _nzbSearchService.BookSearch(book.Id, false, userInvokedSearch, false);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
downloadedCount += processed.Grabbed.Count;
@ -61,7 +61,7 @@ namespace NzbDrone.Core.IndexerSearch
foreach (var bookId in message.BookIds)
{
var decisions =
_nzbSearchService.AlbumSearch(bookId, false, message.Trigger == CommandTrigger.Manual, false);
_nzbSearchService.BookSearch(bookId, false, message.Trigger == CommandTrigger.Manual, false);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Book search completed. {0} reports downloaded.", processed.Grabbed.Count);

@ -16,8 +16,8 @@ namespace NzbDrone.Core.IndexerSearch
{
public interface ISearchForNzb
{
List<DownloadDecision> AlbumSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
List<DownloadDecision> ArtistSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
List<DownloadDecision> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
List<DownloadDecision> AuthorSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
}
public class NzbSearchService : ISearchForNzb
@ -41,13 +41,13 @@ namespace NzbDrone.Core.IndexerSearch
_logger = logger;
}
public List<DownloadDecision> AlbumSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
public List<DownloadDecision> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
{
var book = _bookService.GetBook(bookId);
return AlbumSearch(book, missingOnly, userInvokedSearch, interactiveSearch);
}
public List<DownloadDecision> ArtistSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
public List<DownloadDecision> AuthorSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch)
{
var author = _authorService.GetAuthor(authorId);
return ArtistSearch(author, missingOnly, userInvokedSearch, interactiveSearch);

@ -15,13 +15,13 @@ using NzbDrone.Core.Organizer;
namespace NzbDrone.Core.MediaFiles
{
public interface IRenameTrackFileService
public interface IRenameBookFileService
{
List<RenameBookFilePreview> GetRenamePreviews(int authorId);
List<RenameBookFilePreview> GetRenamePreviews(int authorId, int bookId);
}
public class RenameBookFileService : IRenameTrackFileService, IExecute<RenameFilesCommand>, IExecute<RenameAuthorCommand>
public class RenameBookFileService : IRenameBookFileService, IExecute<RenameFilesCommand>, IExecute<RenameAuthorCommand>
{
private readonly IAuthorService _authorService;
private readonly IMediaFileService _mediaFileService;

@ -3,11 +3,11 @@ using NzbDrone.Core.Books;
namespace NzbDrone.Core.Validation.Paths
{
public class ArtistExistsValidator : PropertyValidator
public class AuthorExistsValidator : PropertyValidator
{
private readonly IAuthorService _authorService;
public ArtistExistsValidator(IAuthorService authorService)
public AuthorExistsValidator(IAuthorService authorService)
: base("This author has already been added.")
{
_authorService = authorService;

@ -1,45 +0,0 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Test.Common;
using Readarr.Api.V1.Artist;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class ArtistEditorFixture : IntegrationTest
{
private void GivenExistingArtist()
{
foreach (var name in new[] { "Alien Ant Farm", "Kiss" })
{
var newArtist = Artist.Lookup(name).First();
newArtist.QualityProfileId = 1;
newArtist.MetadataProfileId = 1;
newArtist.Path = string.Format(@"C:\Test\{0}", name).AsOsAgnostic();
Artist.Post(newArtist);
}
}
[Test]
public void should_be_able_to_update_multiple_artist()
{
GivenExistingArtist();
var artist = Artist.All();
var artistEditor = new ArtistEditorResource
{
QualityProfileId = 2,
AuthorIds = artist.Select(o => o.Id).ToList()
};
var result = Artist.Editor(artistEditor);
result.Should().HaveCount(2);
result.TrueForAll(s => s.QualityProfileId == 2).Should().BeTrue();
}
}
}

@ -1,28 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class ArtistLookupFixture : IntegrationTest
{
[TestCase("Robert Harris", "Robert Harris")]
[TestCase("J.K. Rowling", "J.K. Rowling")]
public void lookup_new_artist_by_name(string term, string name)
{
var artist = Artist.Lookup(term);
artist.Should().NotBeEmpty();
artist.Should().Contain(c => c.ArtistName == name);
}
[Test]
public void lookup_new_artist_by_goodreads_book_id()
{
var artist = Artist.Lookup("readarr:1");
artist.Should().NotBeEmpty();
artist.Should().Contain(c => c.ArtistName == "J.K. Rowling");
}
}
}

@ -0,0 +1,45 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Test.Common;
using Readarr.Api.V1.Author;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class AuthorEditorFixture : IntegrationTest
{
private void GivenExistingAuthor()
{
foreach (var name in new[] { "Alien Ant Farm", "Kiss" })
{
var newAuthor = Author.Lookup(name).First();
newAuthor.QualityProfileId = 1;
newAuthor.MetadataProfileId = 1;
newAuthor.Path = string.Format(@"C:\Test\{0}", name).AsOsAgnostic();
Author.Post(newAuthor);
}
}
[Test]
public void should_be_able_to_update_multiple_artist()
{
GivenExistingAuthor();
var author = Author.All();
var artistEditor = new AuthorEditorResource
{
QualityProfileId = 2,
AuthorIds = author.Select(o => o.Id).ToList()
};
var result = Author.Editor(artistEditor);
result.Should().HaveCount(2);
result.TrueForAll(s => s.QualityProfileId == 2).Should().BeTrue();
}
}
}

@ -7,7 +7,7 @@ using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class ArtistFixture : IntegrationTest
public class AuthorFixture : IntegrationTest
{
[Test]
[Order(0)]
@ -16,15 +16,15 @@ namespace NzbDrone.Integration.Test.ApiTests
EnsureNoArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "J.K. Rowling");
var tag = EnsureTag("abc");
var artist = Artist.Lookup("readarr:1").Single();
var author = Author.Lookup("readarr:1").Single();
artist.QualityProfileId = 1;
artist.MetadataProfileId = 1;
artist.Path = Path.Combine(ArtistRootFolder, artist.ArtistName);
artist.Tags = new HashSet<int>();
artist.Tags.Add(tag.Id);
author.QualityProfileId = 1;
author.MetadataProfileId = 1;
author.Path = Path.Combine(AuthorRootFolder, author.AuthorName);
author.Tags = new HashSet<int>();
author.Tags.Add(tag.Id);
var result = Artist.Post(artist);
var result = Author.Post(author);
result.Should().NotBeNull();
result.Tags.Should().Equal(tag.Id);
@ -38,11 +38,11 @@ namespace NzbDrone.Integration.Test.ApiTests
EnsureNoArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "J.K. Rowling");
var artist = Artist.Lookup("readarr:1").Single();
var artist = Author.Lookup("readarr:1").Single();
artist.Path = Path.Combine(ArtistRootFolder, artist.ArtistName);
artist.Path = Path.Combine(AuthorRootFolder, artist.AuthorName);
Artist.InvalidPost(artist);
Author.InvalidPost(artist);
}
[Test]
@ -53,11 +53,11 @@ namespace NzbDrone.Integration.Test.ApiTests
EnsureNoArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "J.K. Rowling");
var artist = Artist.Lookup("readarr:1").Single();
var artist = Author.Lookup("readarr:1").Single();
artist.QualityProfileId = 1;
Artist.InvalidPost(artist);
Author.InvalidPost(artist);
}
[Test]
@ -66,29 +66,29 @@ namespace NzbDrone.Integration.Test.ApiTests
{
EnsureNoArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "J.K. Rowling");
var artist = Artist.Lookup("readarr:1").Single();
var artist = Author.Lookup("readarr:1").Single();
artist.QualityProfileId = 1;
artist.MetadataProfileId = 1;
artist.Path = Path.Combine(ArtistRootFolder, artist.ArtistName);
artist.Path = Path.Combine(AuthorRootFolder, artist.AuthorName);
var result = Artist.Post(artist);
var result = Author.Post(artist);
result.Should().NotBeNull();
result.Id.Should().NotBe(0);
result.QualityProfileId.Should().Be(1);
result.MetadataProfileId.Should().Be(1);
result.Path.Should().Be(Path.Combine(ArtistRootFolder, artist.ArtistName));
result.Path.Should().Be(Path.Combine(AuthorRootFolder, artist.AuthorName));
}
[Test]
[Order(2)]
public void get_all_artist()
{
EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
EnsureArtist("amzn1.gr.author.v1.qTrNu9-PIaaBj5gYRDmN4Q", "34497", "Terry Pratchett");
EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
EnsureAuthor("amzn1.gr.author.v1.qTrNu9-PIaaBj5gYRDmN4Q", "34497", "Terry Pratchett");
var artists = Artist.All();
var artists = Author.All();
artists.Should().NotBeNullOrEmpty();
artists.Should().Contain(v => v.ForeignAuthorId == "amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ");
@ -99,9 +99,9 @@ namespace NzbDrone.Integration.Test.ApiTests
[Order(2)]
public void get_artist_by_id()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
var result = Artist.Get(artist.Id);
var result = Author.Get(artist.Id);
result.ForeignAuthorId.Should().Be("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ");
}
@ -111,14 +111,14 @@ namespace NzbDrone.Integration.Test.ApiTests
{
IgnoreOnMonoVersions("5.12", "5.14");
var result = Artist.InvalidGet(1000000);
var result = Author.InvalidGet(1000000);
}
[Test]
[Order(2)]
public void update_artist_profile_id()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
var profileId = 1;
if (artist.QualityProfileId == profileId)
@ -128,16 +128,16 @@ namespace NzbDrone.Integration.Test.ApiTests
artist.QualityProfileId = profileId;
var result = Artist.Put(artist);
var result = Author.Put(artist);
Artist.Get(artist.Id).QualityProfileId.Should().Be(profileId);
Author.Get(artist.Id).QualityProfileId.Should().Be(profileId);
}
[Test]
[Order(3)]
public void update_artist_monitored()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
artist.Monitored.Should().BeFalse();
@ -148,7 +148,7 @@ namespace NzbDrone.Integration.Test.ApiTests
//{
// season.Monitored = true;
//});
var result = Artist.Put(artist);
var result = Author.Put(artist);
result.Monitored.Should().BeTrue();
@ -159,22 +159,22 @@ namespace NzbDrone.Integration.Test.ApiTests
[Order(3)]
public void update_artist_tags()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
var tag = EnsureTag("abc");
if (artist.Tags.Contains(tag.Id))
{
artist.Tags.Remove(tag.Id);
var result = Artist.Put(artist);
Artist.Get(artist.Id).Tags.Should().NotContain(tag.Id);
var result = Author.Put(artist);
Author.Get(artist.Id).Tags.Should().NotContain(tag.Id);
}
else
{
artist.Tags.Add(tag.Id);
var result = Artist.Put(artist);
Artist.Get(artist.Id).Tags.Should().Contain(tag.Id);
var result = Author.Put(artist);
Author.Get(artist.Id).Tags.Should().Contain(tag.Id);
}
}
@ -182,13 +182,13 @@ namespace NzbDrone.Integration.Test.ApiTests
[Order(4)]
public void delete_artist()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
Artist.Get(artist.Id).Should().NotBeNull();
Author.Get(artist.Id).Should().NotBeNull();
Artist.Delete(artist.Id);
Author.Delete(artist.Id);
Artist.All().Should().NotContain(v => v.ForeignAuthorId == "amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ");
Author.All().Should().NotContain(v => v.ForeignAuthorId == "amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ");
}
}
}

@ -0,0 +1,28 @@
using FluentAssertions;
using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class AuthorLookupFixture : IntegrationTest
{
[TestCase("Robert Harris", "Robert Harris")]
[TestCase("J.K. Rowling", "J.K. Rowling")]
public void lookup_new_author_by_name(string term, string name)
{
var author = Author.Lookup(term);
author.Should().NotBeEmpty();
author.Should().Contain(c => c.AuthorName == name);
}
[Test]
public void lookup_new_author_by_goodreads_book_id()
{
var author = Author.Lookup("readarr:1");
author.Should().NotBeEmpty();
author.Should().Contain(c => c.AuthorName == "J.K. Rowling");
}
}
}

@ -1,6 +1,6 @@
using FluentAssertions;
using NUnit.Framework;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Blacklist;
namespace NzbDrone.Integration.Test.ApiTests
@ -8,13 +8,13 @@ namespace NzbDrone.Integration.Test.ApiTests
[TestFixture]
public class BlacklistFixture : IntegrationTest
{
private ArtistResource _artist;
private AuthorResource _artist;
[Test]
[Ignore("Adding to blacklist not supported")]
public void should_be_able_to_add_to_blacklist()
{
_artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
_artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling");
Blacklist.Post(new BlacklistResource
{

@ -1,12 +1,12 @@
using NUnit.Framework;
using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class TrackFileFixture : IntegrationTest
public class BookFileFixture : IntegrationTest
{
[Test]
public void get_all_trackfiles()
public void get_all_bookfiles()
{
Assert.Ignore("TODO");
}

@ -4,31 +4,31 @@ using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Integration.Test.Client;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Books;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class CalendarFixture : IntegrationTest
{
public ClientBase<AlbumResource> Calendar;
public ClientBase<BookResource> Calendar;
protected override void InitRestClients()
{
base.InitRestClients();
Calendar = new ClientBase<AlbumResource>(RestClient, ApiKey, "calendar");
Calendar = new ClientBase<BookResource>(RestClient, ApiKey, "calendar");
}
[Test]
public void should_be_able_to_get_albums()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
var request = Calendar.BuildRequest();
request.AddParameter("start", new DateTime(2003, 06, 20).ToString("s") + "Z");
request.AddParameter("end", new DateTime(2003, 06, 22).ToString("s") + "Z");
var items = Calendar.Get<List<AlbumResource>>(request);
var items = Calendar.Get<List<BookResource>>(request);
items = items.Where(v => v.AuthorId == artist.Id).ToList();
@ -39,13 +39,13 @@ namespace NzbDrone.Integration.Test.ApiTests
[Test]
public void should_not_be_able_to_get_unmonitored_albums()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
var request = Calendar.BuildRequest();
request.AddParameter("start", new DateTime(2003, 06, 20).ToString("s") + "Z");
request.AddParameter("end", new DateTime(2003, 06, 22).ToString("s") + "Z");
request.AddParameter("unmonitored", "false");
var items = Calendar.Get<List<AlbumResource>>(request);
var items = Calendar.Get<List<BookResource>>(request);
items = items.Where(v => v.AuthorId == artist.Id).ToList();
@ -55,13 +55,13 @@ namespace NzbDrone.Integration.Test.ApiTests
[Test]
public void should_be_able_to_get_unmonitored_albums()
{
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
var request = Calendar.BuildRequest();
request.AddParameter("start", new DateTime(2003, 06, 20).ToString("s") + "Z");
request.AddParameter("end", new DateTime(2003, 06, 22).ToString("s") + "Z");
request.AddParameter("unmonitored", "true");
var items = Calendar.Get<List<AlbumResource>>(request);
var items = Calendar.Get<List<BookResource>>(request);
items = items.Where(v => v.AuthorId == artist.Id).ToList();

@ -47,7 +47,7 @@ namespace NzbDrone.Integration.Test.ApiTests
releaseResource.Age.Should().BeGreaterOrEqualTo(-1);
releaseResource.Title.Should().NotBeNullOrWhiteSpace();
releaseResource.DownloadUrl.Should().NotBeNullOrWhiteSpace();
releaseResource.ArtistName.Should().NotBeNullOrWhiteSpace();
releaseResource.AuthorName.Should().NotBeNullOrWhiteSpace();
//TODO: uncomment these after moving to restsharp for rss
//releaseResource.NzbInfoUrl.Should().NotBeNullOrWhiteSpace();

@ -17,7 +17,7 @@ namespace NzbDrone.Integration.Test.ApiTests
RootFolders.Post(new RootFolderResource
{
Name = "TestLibrary",
Path = ArtistRootFolder,
Path = AuthorRootFolder,
DefaultMetadataProfileId = 1,
DefaultQualityProfileId = 1,
DefaultMonitorOption = MonitorTypes.All
@ -39,7 +39,7 @@ namespace NzbDrone.Integration.Test.ApiTests
[Order(1)]
public void missing_should_have_monitored_items()
{
EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
var result = WantedMissing.GetPaged(0, 15, "releaseDate", "desc");
@ -50,12 +50,12 @@ namespace NzbDrone.Integration.Test.ApiTests
[Order(1)]
public void missing_should_have_artist()
{
EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
var result = WantedMissing.GetPaged(0, 15, "releaseDate", "desc");
result.Records.First().Artist.Should().NotBeNull();
result.Records.First().Artist.ArtistName.Should().Be("J.K. Rowling");
result.Records.First().Author.Should().NotBeNull();
result.Records.First().Author.AuthorName.Should().Be("J.K. Rowling");
}
[Test]
@ -63,8 +63,8 @@ namespace NzbDrone.Integration.Test.ApiTests
public void cutoff_should_have_monitored_items()
{
EnsureProfileCutoff(1, Quality.AZW3);
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
EnsureTrackFile(artist, 1, Quality.MOBI);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
EnsureBookFile(artist, 1, Quality.MOBI);
var result = WantedCutoffUnmet.GetPaged(0, 15, "releaseDate", "desc");
@ -75,7 +75,7 @@ namespace NzbDrone.Integration.Test.ApiTests
[Order(1)]
public void missing_should_not_have_unmonitored_items()
{
EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
var result = WantedMissing.GetPaged(0, 15, "releaseDate", "desc");
@ -87,8 +87,8 @@ namespace NzbDrone.Integration.Test.ApiTests
public void cutoff_should_not_have_unmonitored_items()
{
EnsureProfileCutoff(1, Quality.AZW3);
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
EnsureTrackFile(artist, 1, Quality.MOBI);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
EnsureBookFile(artist, 1, Quality.MOBI);
var result = WantedCutoffUnmet.GetPaged(0, 15, "releaseDate", "desc");
@ -100,20 +100,20 @@ namespace NzbDrone.Integration.Test.ApiTests
public void cutoff_should_have_artist()
{
EnsureProfileCutoff(1, Quality.AZW3);
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
EnsureTrackFile(artist, 1, Quality.MOBI);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", true);
EnsureBookFile(artist, 1, Quality.MOBI);
var result = WantedCutoffUnmet.GetPaged(0, 15, "releaseDate", "desc");
result.Records.First().Artist.Should().NotBeNull();
result.Records.First().Artist.ArtistName.Should().Be("J.K. Rowling");
result.Records.First().Author.Should().NotBeNull();
result.Records.First().Author.AuthorName.Should().Be("J.K. Rowling");
}
[Test]
[Order(2)]
public void missing_should_have_unmonitored_items()
{
EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
var result = WantedMissing.GetPaged(0, 15, "releaseDate", "desc", "monitored", "false");
@ -125,8 +125,8 @@ namespace NzbDrone.Integration.Test.ApiTests
public void cutoff_should_have_unmonitored_items()
{
EnsureProfileCutoff(1, Quality.AZW3);
var artist = EnsureArtist("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
EnsureTrackFile(artist, 1, Quality.MOBI);
var artist = EnsureAuthor("amzn1.gr.author.v1.SHA8asP5mFyLIP9NlujvLQ", "1", "J.K. Rowling", false);
EnsureBookFile(artist, 1, Quality.MOBI);
var result = WantedCutoffUnmet.GetPaged(0, 15, "releaseDate", "desc", "monitored", "false");

@ -1,20 +0,0 @@
using System.Collections.Generic;
using Readarr.Api.V1.Albums;
using RestSharp;
namespace NzbDrone.Integration.Test.Client
{
public class AlbumClient : ClientBase<AlbumResource>
{
public AlbumClient(IRestClient restClient, string apiKey)
: base(restClient, apiKey, "album")
{
}
public List<AlbumResource> GetAlbumsInArtist(int authorId)
{
var request = BuildRequest("?authorId=" + authorId.ToString());
return Get<List<AlbumResource>>(request);
}
}
}

@ -1,39 +1,39 @@
using System.Collections.Generic;
using System.Net;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using RestSharp;
namespace NzbDrone.Integration.Test.Client
{
public class ArtistClient : ClientBase<ArtistResource>
public class AuthorClient : ClientBase<AuthorResource>
{
public ArtistClient(IRestClient restClient, string apiKey)
public AuthorClient(IRestClient restClient, string apiKey)
: base(restClient, apiKey)
{
}
public List<ArtistResource> Lookup(string term)
public List<AuthorResource> Lookup(string term)
{
var request = BuildRequest("lookup");
request.AddQueryParameter("term", term);
return Get<List<ArtistResource>>(request);
return Get<List<AuthorResource>>(request);
}
public List<ArtistResource> Editor(ArtistEditorResource artist)
public List<AuthorResource> Editor(AuthorEditorResource artist)
{
var request = BuildRequest("editor");
request.AddJsonBody(artist);
return Put<List<ArtistResource>>(request);
return Put<List<AuthorResource>>(request);
}
public ArtistResource Get(string slug, HttpStatusCode statusCode = HttpStatusCode.OK)
public AuthorResource Get(string slug, HttpStatusCode statusCode = HttpStatusCode.OK)
{
var request = BuildRequest(slug);
return Get<ArtistResource>(request, statusCode);
return Get<AuthorResource>(request, statusCode);
}
}
public class SystemInfoClient : ClientBase<ArtistResource>
public class SystemInfoClient : ClientBase<AuthorResource>
{
public SystemInfoClient(IRestClient restClient, string apiKey)
: base(restClient, apiKey)

@ -0,0 +1,20 @@
using System.Collections.Generic;
using Readarr.Api.V1.Books;
using RestSharp;
namespace NzbDrone.Integration.Test.Client
{
public class BookClient : ClientBase<BookResource>
{
public BookClient(IRestClient restClient, string apiKey)
: base(restClient, apiKey, "book")
{
}
public List<BookResource> GetBooksInAuthor(int authorId)
{
var request = BuildRequest("?authorId=" + authorId.ToString());
return Get<List<BookResource>>(request);
}
}
}

@ -8,7 +8,7 @@ namespace NzbDrone.Integration.Test
[TestFixture]
public class CorsFixture : IntegrationTest
{
private RestRequest BuildGet(string route = "artist")
private RestRequest BuildGet(string route = "author")
{
var request = new RestRequest(route, Method.GET);
request.AddHeader(AccessControlHeaders.RequestMethod, "POST");
@ -16,7 +16,7 @@ namespace NzbDrone.Integration.Test
return request;
}
private RestRequest BuildOptions(string route = "artist")
private RestRequest BuildOptions(string route = "author")
{
var request = new RestRequest(route, Method.OPTIONS);

@ -17,20 +17,20 @@ namespace NzbDrone.Integration.Test
config.LogLevel = "Trace";
HostConfig.Put(config);
var resultGet = Artist.All();
var resultGet = Author.All();
var logFile = "Readarr.trace.txt";
var logLines = Logs.GetLogFileLines(logFile);
var result = Artist.InvalidPost(new Readarr.Api.V1.Artist.ArtistResource());
var result = Author.InvalidPost(new Readarr.Api.V1.Author.AuthorResource());
// Skip 2 and 1 to ignore the logs endpoint
logLines = Logs.GetLogFileLines(logFile).Skip(logLines.Length + 2).ToArray();
Array.Resize(ref logLines, logLines.Length - 1);
logLines.Should().Contain(v => v.Contains("|Trace|Http|Req") && v.Contains("/api/v1/artist/"));
logLines.Should().Contain(v => v.Contains("|Trace|Http|Res") && v.Contains("/api/v1/artist/: 400.BadRequest"));
logLines.Should().Contain(v => v.Contains("|Debug|Api|") && v.Contains("/api/v1/artist/: 400.BadRequest"));
logLines.Should().Contain(v => v.Contains("|Trace|Http|Req") && v.Contains("/api/v1/author/"));
logLines.Should().Contain(v => v.Contains("|Trace|Http|Res") && v.Contains("/api/v1/author/: 400.BadRequest"));
logLines.Should().Contain(v => v.Contains("|Debug|Api|") && v.Contains("/api/v1/author/: 400.BadRequest"));
}
}
}

@ -9,7 +9,7 @@ namespace NzbDrone.Integration.Test
{
protected NzbDroneRunner _runner;
public override string ArtistRootFolder => GetTempDirectory("ArtistRootFolder");
public override string AuthorRootFolder => GetTempDirectory("AuthorRootFolder");
protected override string RootUrl => "http://localhost:8787/";

@ -17,9 +17,9 @@ using NzbDrone.Integration.Test.Client;
using NzbDrone.SignalR;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.Categories;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Blacklist;
using Readarr.Api.V1.Books;
using Readarr.Api.V1.Config;
using Readarr.Api.V1.DownloadClient;
using Readarr.Api.V1.History;
@ -38,7 +38,7 @@ namespace NzbDrone.Integration.Test
public ClientBase<BlacklistResource> Blacklist;
public CommandClient Commands;
public DownloadClientClient DownloadClients;
public AlbumClient Albums;
public BookClient Books;
public ClientBase<HistoryResource> History;
public ClientBase<HostConfigResource> HostConfig;
public IndexerClient Indexers;
@ -49,10 +49,10 @@ namespace NzbDrone.Integration.Test
public ReleaseClient Releases;
public ReleasePushClient ReleasePush;
public ClientBase<RootFolderResource> RootFolders;
public ArtistClient Artist;
public AuthorClient Author;
public ClientBase<TagResource> Tags;
public ClientBase<AlbumResource> WantedMissing;
public ClientBase<AlbumResource> WantedCutoffUnmet;
public ClientBase<BookResource> WantedMissing;
public ClientBase<BookResource> WantedCutoffUnmet;
private List<SignalRMessage> _signalRReceived;
@ -72,7 +72,7 @@ namespace NzbDrone.Integration.Test
public string TempDirectory { get; private set; }
public abstract string ArtistRootFolder { get; }
public abstract string AuthorRootFolder { get; }
protected abstract string RootUrl { get; }
@ -101,7 +101,7 @@ namespace NzbDrone.Integration.Test
Blacklist = new ClientBase<BlacklistResource>(RestClient, ApiKey);
Commands = new CommandClient(RestClient, ApiKey);
DownloadClients = new DownloadClientClient(RestClient, ApiKey);
Albums = new AlbumClient(RestClient, ApiKey);
Books = new BookClient(RestClient, ApiKey);
History = new ClientBase<HistoryResource>(RestClient, ApiKey);
HostConfig = new ClientBase<HostConfigResource>(RestClient, ApiKey, "config/host");
Indexers = new IndexerClient(RestClient, ApiKey);
@ -112,10 +112,10 @@ namespace NzbDrone.Integration.Test
Releases = new ReleaseClient(RestClient, ApiKey);
ReleasePush = new ReleasePushClient(RestClient, ApiKey);
RootFolders = new ClientBase<RootFolderResource>(RestClient, ApiKey);
Artist = new ArtistClient(RestClient, ApiKey);
Author = new AuthorClient(RestClient, ApiKey);
Tags = new ClientBase<TagResource>(RestClient, ApiKey);
WantedMissing = new ClientBase<AlbumResource>(RestClient, ApiKey, "wanted/missing");
WantedCutoffUnmet = new ClientBase<AlbumResource>(RestClient, ApiKey, "wanted/cutoff");
WantedMissing = new ClientBase<BookResource>(RestClient, ApiKey, "wanted/missing");
WantedCutoffUnmet = new ClientBase<BookResource>(RestClient, ApiKey, "wanted/cutoff");
}
[OneTimeTearDown]
@ -249,33 +249,33 @@ namespace NzbDrone.Integration.Test
Assert.Fail("Timed on wait");
}
public ArtistResource EnsureArtist(string authorId, string goodreadsBookId, string artistName, bool? monitored = null)
public AuthorResource EnsureAuthor(string authorId, string goodreadsBookId, string authorName, bool? monitored = null)
{
var result = Artist.All().FirstOrDefault(v => v.ForeignAuthorId == authorId);
var result = Author.All().FirstOrDefault(v => v.ForeignAuthorId == authorId);
if (result == null)
{
var lookup = Artist.Lookup("readarr:" + goodreadsBookId);
var artist = lookup.First();
artist.QualityProfileId = 1;
artist.MetadataProfileId = 1;
artist.Path = Path.Combine(ArtistRootFolder, artist.ArtistName);
artist.Monitored = true;
artist.AddOptions = new Core.Books.AddAuthorOptions();
Directory.CreateDirectory(artist.Path);
result = Artist.Post(artist);
var lookup = Author.Lookup("readarr:" + goodreadsBookId);
var author = lookup.First();
author.QualityProfileId = 1;
author.MetadataProfileId = 1;
author.Path = Path.Combine(AuthorRootFolder, author.AuthorName);
author.Monitored = true;
author.AddOptions = new Core.Books.AddAuthorOptions();
Directory.CreateDirectory(author.Path);
result = Author.Post(author);
Commands.WaitAll();
WaitForCompletion(() => Albums.GetAlbumsInArtist(result.Id).Count > 0);
WaitForCompletion(() => Books.GetBooksInAuthor(result.Id).Count > 0);
}
var changed = false;
if (result.RootFolderPath != ArtistRootFolder)
if (result.RootFolderPath != AuthorRootFolder)
{
changed = true;
result.RootFolderPath = ArtistRootFolder;
result.Path = Path.Combine(ArtistRootFolder, result.ArtistName);
result.RootFolderPath = AuthorRootFolder;
result.Path = Path.Combine(AuthorRootFolder, result.AuthorName);
}
if (monitored.HasValue)
@ -289,7 +289,7 @@ namespace NzbDrone.Integration.Test
if (changed)
{
Artist.Put(result);
Author.Put(result);
}
return result;
@ -297,22 +297,22 @@ namespace NzbDrone.Integration.Test
public void EnsureNoArtist(string readarrId, string artistTitle)
{
var result = Artist.All().FirstOrDefault(v => v.ForeignAuthorId == readarrId);
var result = Author.All().FirstOrDefault(v => v.ForeignAuthorId == readarrId);
if (result != null)
{
Artist.Delete(result.Id);
Author.Delete(result.Id);
}
}
public void EnsureTrackFile(ArtistResource artist, int bookId, Quality quality)
public void EnsureBookFile(AuthorResource artist, int bookId, Quality quality)
{
var result = Albums.GetAlbumsInArtist(artist.Id).Single(v => v.Id == bookId);
var result = Books.GetBooksInAuthor(artist.Id).Single(v => v.Id == bookId);
// if (result.BookFile == null)
if (true)
{
var path = Path.Combine(ArtistRootFolder, artist.ArtistName, "Track.mp3");
var path = Path.Combine(AuthorRootFolder, artist.AuthorName, "Track.mp3");
Directory.CreateDirectory(Path.GetDirectoryName(path));
File.WriteAllText(path, "Fake Track");
@ -332,7 +332,7 @@ namespace NzbDrone.Integration.Test
});
Commands.WaitAll();
var track = Albums.GetAlbumsInArtist(artist.Id).Single(x => x.Id == bookId);
var track = Books.GetBooksInAuthor(artist.Id).Single(x => x.Id == bookId);
// track.BookFileId.Should().NotBe(0);
}

@ -1,12 +0,0 @@
using System.Collections.Generic;
using Readarr.Api.V1.Albums;
namespace Readarr.Api.V1.AlbumStudio
{
public class AlbumStudioArtistResource
{
public int Id { get; set; }
public bool? Monitored { get; set; }
public List<AlbumResource> Albums { get; set; }
}
}

@ -1,47 +0,0 @@
using System.Linq;
using Nancy;
using NzbDrone.Core.Books;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.AlbumStudio
{
public class AlbumStudioModule : ReadarrV1Module
{
private readonly IAuthorService _authorService;
private readonly IBookMonitoredService _albumMonitoredService;
public AlbumStudioModule(IAuthorService authorService, IBookMonitoredService albumMonitoredService)
: base("/albumstudio")
{
_authorService = authorService;
_albumMonitoredService = albumMonitoredService;
Post("/", artist => UpdateAll());
}
private object UpdateAll()
{
//Read from request
var request = Request.Body.FromJson<AlbumStudioResource>();
var artistToUpdate = _authorService.GetAuthors(request.Artist.Select(s => s.Id));
foreach (var s in request.Artist)
{
var artist = artistToUpdate.Single(c => c.Id == s.Id);
if (s.Monitored.HasValue)
{
artist.Monitored = s.Monitored.Value;
}
if (request.MonitoringOptions != null && request.MonitoringOptions.Monitor == MonitorTypes.None)
{
artist.Monitored = false;
}
_albumMonitoredService.SetBookMonitoredStatus(artist, request.MonitoringOptions);
}
return ResponseWithCode("ok", HttpStatusCode.Accepted);
}
}
}

@ -1,133 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.AuthorStats;
using NzbDrone.Core.Books;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaCover;
using NzbDrone.SignalR;
using Readarr.Api.V1.Artist;
using Readarr.Http;
namespace Readarr.Api.V1.Albums
{
public abstract class AlbumModuleWithSignalR : ReadarrRestModuleWithSignalR<AlbumResource, Book>
{
protected readonly IBookService _bookService;
protected readonly IAuthorStatisticsService _artistStatisticsService;
protected readonly IUpgradableSpecification _qualityUpgradableSpecification;
protected readonly IMapCoversToLocal _coverMapper;
protected AlbumModuleWithSignalR(IBookService bookService,
IAuthorStatisticsService artistStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(signalRBroadcaster)
{
_bookService = bookService;
_artistStatisticsService = artistStatisticsService;
_coverMapper = coverMapper;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetAlbum;
}
protected AlbumModuleWithSignalR(IBookService bookService,
IAuthorStatisticsService artistStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster,
string resource)
: base(signalRBroadcaster, resource)
{
_bookService = bookService;
_artistStatisticsService = artistStatisticsService;
_coverMapper = coverMapper;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetAlbum;
}
protected AlbumResource GetAlbum(int id)
{
var album = _bookService.GetBook(id);
var resource = MapToResource(album, true);
return resource;
}
protected AlbumResource MapToResource(Book album, bool includeArtist)
{
var resource = album.ToResource();
if (includeArtist)
{
var artist = album.Author.Value;
resource.Artist = artist.ToResource();
}
FetchAndLinkAlbumStatistics(resource);
MapCoversToLocal(resource);
return resource;
}
protected List<AlbumResource> MapToResource(List<Book> albums, bool includeArtist)
{
var result = albums.ToResource();
if (includeArtist)
{
var artistDict = new Dictionary<int, NzbDrone.Core.Books.Author>();
for (var i = 0; i < albums.Count; i++)
{
var album = albums[i];
var resource = result[i];
var artist = artistDict.GetValueOrDefault(albums[i].AuthorMetadataId) ?? album.Author?.Value;
artistDict[artist.AuthorMetadataId] = artist;
resource.Artist = artist.ToResource();
}
}
var artistStats = _artistStatisticsService.AuthorStatistics();
LinkArtistStatistics(result, artistStats);
MapCoversToLocal(result.ToArray());
return result;
}
private void FetchAndLinkAlbumStatistics(AlbumResource resource)
{
LinkArtistStatistics(resource, _artistStatisticsService.AuthorStatistics(resource.AuthorId));
}
private void LinkArtistStatistics(List<AlbumResource> resources, List<AuthorStatistics> artistStatistics)
{
foreach (var album in resources)
{
var stats = artistStatistics.SingleOrDefault(ss => ss.AuthorId == album.AuthorId);
LinkArtistStatistics(album, stats);
}
}
private void LinkArtistStatistics(AlbumResource resource, AuthorStatistics artistStatistics)
{
if (artistStatistics?.BookStatistics != null)
{
var dictAlbumStats = artistStatistics.BookStatistics.ToDictionary(v => v.BookId);
resource.Statistics = dictAlbumStats.GetValueOrDefault(resource.Id).ToResource();
}
}
private void MapCoversToLocal(params AlbumResource[] albums)
{
foreach (var albumResource in albums)
{
_coverMapper.ConvertToLocalUrls(albumResource.Id, MediaCoverEntity.Book, albumResource.Images);
}
}
}
}

@ -1,43 +0,0 @@
using NzbDrone.Core.AuthorStats;
namespace Readarr.Api.V1.Albums
{
public class AlbumStatisticsResource
{
public int TrackFileCount { get; set; }
public int TrackCount { get; set; }
public int TotalTrackCount { get; set; }
public long SizeOnDisk { get; set; }
public decimal PercentOfTracks
{
get
{
if (TrackCount == 0)
{
return 0;
}
return TrackFileCount / (decimal)TrackCount * 100;
}
}
}
public static class AlbumStatisticsResourceMapper
{
public static AlbumStatisticsResource ToResource(this BookStatistics model)
{
if (model == null)
{
return null;
}
return new AlbumStatisticsResource
{
TrackFileCount = model.BookFileCount,
TrackCount = model.BookCount,
SizeOnDisk = model.SizeOnDisk
};
}
}
}

@ -1,4 +1,4 @@
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class AlternateTitleResource
{

@ -1,8 +1,8 @@
using System.Collections.Generic;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class ArtistEditorDeleteResource
public class AuthorEditorDeleteResource
{
public List<int> AuthorIds { get; set; }
public bool DeleteFiles { get; set; }

@ -7,52 +7,52 @@ using NzbDrone.Core.Books.Commands;
using NzbDrone.Core.Messaging.Commands;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class ArtistEditorModule : ReadarrV1Module
public class AuthorEditorModule : ReadarrV1Module
{
private readonly IAuthorService _authorService;
private readonly IManageCommandQueue _commandQueueManager;
public ArtistEditorModule(IAuthorService authorService, IManageCommandQueue commandQueueManager)
: base("/artist/editor")
public AuthorEditorModule(IAuthorService authorService, IManageCommandQueue commandQueueManager)
: base("/author/editor")
{
_authorService = authorService;
_commandQueueManager = commandQueueManager;
Put("/", artist => SaveAll());
Delete("/", artist => DeleteArtist());
Put("/", author => SaveAll());
Delete("/", author => DeleteAuthor());
}
private object SaveAll()
{
var resource = Request.Body.FromJson<ArtistEditorResource>();
var artistToUpdate = _authorService.GetAuthors(resource.AuthorIds);
var artistToMove = new List<BulkMoveAuthor>();
var resource = Request.Body.FromJson<AuthorEditorResource>();
var authorsToUpdate = _authorService.GetAuthors(resource.AuthorIds);
var authorsToMove = new List<BulkMoveAuthor>();
foreach (var artist in artistToUpdate)
foreach (var author in authorsToUpdate)
{
if (resource.Monitored.HasValue)
{
artist.Monitored = resource.Monitored.Value;
author.Monitored = resource.Monitored.Value;
}
if (resource.QualityProfileId.HasValue)
{
artist.QualityProfileId = resource.QualityProfileId.Value;
author.QualityProfileId = resource.QualityProfileId.Value;
}
if (resource.MetadataProfileId.HasValue)
{
artist.MetadataProfileId = resource.MetadataProfileId.Value;
author.MetadataProfileId = resource.MetadataProfileId.Value;
}
if (resource.RootFolderPath.IsNotNullOrWhiteSpace())
{
artist.RootFolderPath = resource.RootFolderPath;
artistToMove.Add(new BulkMoveAuthor
author.RootFolderPath = resource.RootFolderPath;
authorsToMove.Add(new BulkMoveAuthor
{
AuthorId = artist.Id,
SourcePath = artist.Path
AuthorId = author.Id,
SourcePath = author.Path
});
}
@ -64,35 +64,35 @@ namespace Readarr.Api.V1.Artist
switch (applyTags)
{
case ApplyTags.Add:
newTags.ForEach(t => artist.Tags.Add(t));
newTags.ForEach(t => author.Tags.Add(t));
break;
case ApplyTags.Remove:
newTags.ForEach(t => artist.Tags.Remove(t));
newTags.ForEach(t => author.Tags.Remove(t));
break;
case ApplyTags.Replace:
artist.Tags = new HashSet<int>(newTags);
author.Tags = new HashSet<int>(newTags);
break;
}
}
}
if (resource.MoveFiles && artistToMove.Any())
if (resource.MoveFiles && authorsToMove.Any())
{
_commandQueueManager.Push(new BulkMoveAuthorCommand
{
DestinationRootFolder = resource.RootFolderPath,
Author = artistToMove
Author = authorsToMove
});
}
return ResponseWithCode(_authorService.UpdateAuthors(artistToUpdate, !resource.MoveFiles)
return ResponseWithCode(_authorService.UpdateAuthors(authorsToUpdate, !resource.MoveFiles)
.ToResource(),
HttpStatusCode.Accepted);
}
private object DeleteArtist()
private object DeleteAuthor()
{
var resource = Request.Body.FromJson<ArtistEditorResource>();
var resource = Request.Body.FromJson<AuthorEditorResource>();
foreach (var authorId in resource.AuthorIds)
{

@ -1,14 +1,13 @@
using System.Collections.Generic;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class ArtistEditorResource
public class AuthorEditorResource
{
public List<int> AuthorIds { get; set; }
public bool? Monitored { get; set; }
public int? QualityProfileId { get; set; }
public int? MetadataProfileId { get; set; }
public bool? AlbumFolder { get; set; }
public string RootFolderPath { get; set; }
public List<int> Tags { get; set; }
public ApplyTags ApplyTags { get; set; }

@ -4,14 +4,14 @@ using NzbDrone.Core.Books;
using Readarr.Http;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class ArtistImportModule : ReadarrRestModule<ArtistResource>
public class AuthorImportModule : ReadarrRestModule<AuthorResource>
{
private readonly IAddAuthorService _addAuthorService;
public ArtistImportModule(IAddAuthorService addAuthorService)
: base("/artist/import")
public AuthorImportModule(IAddAuthorService addAuthorService)
: base("/author/import")
{
_addAuthorService = addAuthorService;
Post("/", x => Import());
@ -19,10 +19,10 @@ namespace Readarr.Api.V1.Artist
private object Import()
{
var resource = Request.Body.FromJson<List<ArtistResource>>();
var newArtists = resource.ToModel();
var resource = Request.Body.FromJson<List<AuthorResource>>();
var newAuthors = resource.ToModel();
return _addAuthorService.AddAuthors(newArtists).ToResource();
return _addAuthorService.AddAuthors(newAuthors).ToResource();
}
}
}

@ -5,14 +5,14 @@ using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using Readarr.Http;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class ArtistLookupModule : ReadarrRestModule<ArtistResource>
public class AuthorLookupModule : ReadarrRestModule<AuthorResource>
{
private readonly ISearchForNewAuthor _searchProxy;
public ArtistLookupModule(ISearchForNewAuthor searchProxy)
: base("/artist/lookup")
public AuthorLookupModule(ISearchForNewAuthor searchProxy)
: base("/author/lookup")
{
_searchProxy = searchProxy;
Get("/", x => Search());
@ -24,12 +24,12 @@ namespace Readarr.Api.V1.Artist
return MapToResource(searchResults).ToList();
}
private static IEnumerable<ArtistResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Author> artist)
private static IEnumerable<AuthorResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Author> author)
{
foreach (var currentArtist in artist)
foreach (var currentAuthor in author)
{
var resource = currentArtist.ToResource();
var poster = currentArtist.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
var resource = currentAuthor.ToResource();
var poster = currentAuthor.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.RemotePoster = poster.Url;

@ -19,9 +19,9 @@ using NzbDrone.SignalR;
using Readarr.Http;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class ArtistModule : ReadarrRestModuleWithSignalR<ArtistResource, NzbDrone.Core.Books.Author>,
public class AuthorModule : ReadarrRestModuleWithSignalR<AuthorResource, NzbDrone.Core.Books.Author>,
IHandle<BookImportedEvent>,
IHandle<BookEditedEvent>,
IHandle<BookFileDeletedEvent>,
@ -34,24 +34,24 @@ namespace Readarr.Api.V1.Artist
private readonly IAuthorService _authorService;
private readonly IBookService _bookService;
private readonly IAddAuthorService _addAuthorService;
private readonly IAuthorStatisticsService _artistStatisticsService;
private readonly IAuthorStatisticsService _authorStatisticsService;
private readonly IMapCoversToLocal _coverMapper;
private readonly IManageCommandQueue _commandQueueManager;
private readonly IRootFolderService _rootFolderService;
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
public AuthorModule(IBroadcastSignalRMessage signalRBroadcaster,
IAuthorService authorService,
IBookService bookService,
IAddAuthorService addAuthorService,
IAuthorStatisticsService artistStatisticsService,
IAuthorStatisticsService authorStatisticsService,
IMapCoversToLocal coverMapper,
IManageCommandQueue commandQueueManager,
IRootFolderService rootFolderService,
RootFolderValidator rootFolderValidator,
MappedNetworkDriveValidator mappedNetworkDriveValidator,
AuthorPathValidator artistPathValidator,
ArtistExistsValidator artistExistsValidator,
AuthorAncestorValidator artistAncestorValidator,
AuthorPathValidator authorPathValidator,
AuthorExistsValidator authorExistsValidator,
AuthorAncestorValidator authorAncestorValidator,
SystemFolderValidator systemFolderValidator,
QualityProfileExistsValidator qualityProfileExistsValidator,
MetadataProfileExistsValidator metadataProfileExistsValidator)
@ -60,17 +60,17 @@ namespace Readarr.Api.V1.Artist
_authorService = authorService;
_bookService = bookService;
_addAuthorService = addAuthorService;
_artistStatisticsService = artistStatisticsService;
_authorStatisticsService = authorStatisticsService;
_coverMapper = coverMapper;
_commandQueueManager = commandQueueManager;
_rootFolderService = rootFolderService;
GetResourceAll = AllArtists;
GetResourceById = GetArtist;
CreateResource = AddArtist;
UpdateResource = UpdateArtist;
DeleteResource = DeleteArtist;
GetResourceAll = AllAuthors;
GetResourceById = GetAuthor;
CreateResource = AddAuthor;
UpdateResource = UpdateAuthor;
DeleteResource = DeleteAuthor;
Http.Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.QualityProfileId));
Http.Validation.RuleBuilderExtensions.ValidId(SharedValidator.RuleFor(s => s.MetadataProfileId));
@ -80,8 +80,8 @@ namespace Readarr.Api.V1.Artist
.IsValidPath()
.SetValidator(rootFolderValidator)
.SetValidator(mappedNetworkDriveValidator)
.SetValidator(artistPathValidator)
.SetValidator(artistAncestorValidator)
.SetValidator(authorPathValidator)
.SetValidator(authorAncestorValidator)
.SetValidator(systemFolderValidator)
.When(s => !s.Path.IsNullOrWhiteSpace());
@ -90,26 +90,26 @@ namespace Readarr.Api.V1.Artist
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.ArtistName).NotEmpty();
PostValidator.RuleFor(s => s.ForeignAuthorId).NotEmpty().SetValidator(artistExistsValidator);
PostValidator.RuleFor(s => s.AuthorName).NotEmpty();
PostValidator.RuleFor(s => s.ForeignAuthorId).NotEmpty().SetValidator(authorExistsValidator);
PutValidator.RuleFor(s => s.Path).IsValidPath();
}
private ArtistResource GetArtist(int id)
private AuthorResource GetAuthor(int id)
{
var artist = _authorService.GetAuthor(id);
return GetArtistResource(artist);
var author = _authorService.GetAuthor(id);
return GetArtistResource(author);
}
private ArtistResource GetArtistResource(NzbDrone.Core.Books.Author artist)
private AuthorResource GetArtistResource(NzbDrone.Core.Books.Author author)
{
if (artist == null)
if (author == null)
{
return null;
}
var resource = artist.ToResource();
var resource = author.ToResource();
MapCoversToLocal(resource);
FetchAndLinkArtistStatistics(resource);
LinkNextPreviousAlbums(resource);
@ -120,53 +120,53 @@ namespace Readarr.Api.V1.Artist
return resource;
}
private List<ArtistResource> AllArtists()
private List<AuthorResource> AllAuthors()
{
var artistStats = _artistStatisticsService.AuthorStatistics();
var artistsResources = _authorService.GetAllAuthors().ToResource();
var authorStats = _authorStatisticsService.AuthorStatistics();
var authorResources = _authorService.GetAllAuthors().ToResource();
MapCoversToLocal(artistsResources.ToArray());
LinkNextPreviousAlbums(artistsResources.ToArray());
LinkArtistStatistics(artistsResources, artistStats);
MapCoversToLocal(authorResources.ToArray());
LinkNextPreviousAlbums(authorResources.ToArray());
LinkArtistStatistics(authorResources, authorStats);
//PopulateAlternateTitles(seriesResources);
return artistsResources;
return authorResources;
}
private int AddArtist(ArtistResource artistResource)
private int AddAuthor(AuthorResource authorResource)
{
var artist = _addAuthorService.AddAuthor(artistResource.ToModel());
var author = _addAuthorService.AddAuthor(authorResource.ToModel());
return artist.Id;
return author.Id;
}
private void UpdateArtist(ArtistResource artistResource)
private void UpdateAuthor(AuthorResource authorResource)
{
var moveFiles = Request.GetBooleanQueryParameter("moveFiles");
var artist = _authorService.GetAuthor(artistResource.Id);
var author = _authorService.GetAuthor(authorResource.Id);
if (moveFiles)
{
var sourcePath = artist.Path;
var destinationPath = artistResource.Path;
var sourcePath = author.Path;
var destinationPath = authorResource.Path;
_commandQueueManager.Push(new MoveAuthorCommand
{
AuthorId = artist.Id,
AuthorId = author.Id,
SourcePath = sourcePath,
DestinationPath = destinationPath,
Trigger = CommandTrigger.Manual
});
}
var model = artistResource.ToModel(artist);
var model = authorResource.ToModel(author);
_authorService.UpdateAuthor(model);
BroadcastResourceChange(ModelAction.Updated, artistResource);
BroadcastResourceChange(ModelAction.Updated, authorResource);
}
private void DeleteArtist(int id)
private void DeleteAuthor(int id)
{
var deleteFiles = Request.GetBooleanQueryParameter("deleteFiles");
var addImportListExclusion = Request.GetBooleanQueryParameter("addImportListExclusion");
@ -174,48 +174,48 @@ namespace Readarr.Api.V1.Artist
_authorService.DeleteAuthor(id, deleteFiles, addImportListExclusion);
}
private void MapCoversToLocal(params ArtistResource[] artists)
private void MapCoversToLocal(params AuthorResource[] authors)
{
foreach (var artistResource in artists)
foreach (var authorResource in authors)
{
_coverMapper.ConvertToLocalUrls(artistResource.Id, MediaCoverEntity.Author, artistResource.Images);
_coverMapper.ConvertToLocalUrls(authorResource.Id, MediaCoverEntity.Author, authorResource.Images);
}
}
private void LinkNextPreviousAlbums(params ArtistResource[] artists)
private void LinkNextPreviousAlbums(params AuthorResource[] authors)
{
var nextAlbums = _bookService.GetNextBooksByAuthorMetadataId(artists.Select(x => x.ArtistMetadataId));
var lastAlbums = _bookService.GetLastBooksByAuthorMetadataId(artists.Select(x => x.ArtistMetadataId));
var nextBooks = _bookService.GetNextBooksByAuthorMetadataId(authors.Select(x => x.AuthorMetadataId));
var lastBooks = _bookService.GetLastBooksByAuthorMetadataId(authors.Select(x => x.AuthorMetadataId));
foreach (var artistResource in artists)
foreach (var authorResource in authors)
{
artistResource.NextAlbum = nextAlbums.FirstOrDefault(x => x.AuthorMetadataId == artistResource.ArtistMetadataId);
artistResource.LastAlbum = lastAlbums.FirstOrDefault(x => x.AuthorMetadataId == artistResource.ArtistMetadataId);
authorResource.NextBook = nextBooks.FirstOrDefault(x => x.AuthorMetadataId == authorResource.AuthorMetadataId);
authorResource.LastBook = lastBooks.FirstOrDefault(x => x.AuthorMetadataId == authorResource.AuthorMetadataId);
}
}
private void FetchAndLinkArtistStatistics(ArtistResource resource)
private void FetchAndLinkArtistStatistics(AuthorResource resource)
{
LinkArtistStatistics(resource, _artistStatisticsService.AuthorStatistics(resource.Id));
LinkArtistStatistics(resource, _authorStatisticsService.AuthorStatistics(resource.Id));
}
private void LinkArtistStatistics(List<ArtistResource> resources, List<AuthorStatistics> artistStatistics)
private void LinkArtistStatistics(List<AuthorResource> resources, List<AuthorStatistics> authorStatistics)
{
foreach (var artist in resources)
foreach (var author in resources)
{
var stats = artistStatistics.SingleOrDefault(ss => ss.AuthorId == artist.Id);
var stats = authorStatistics.SingleOrDefault(ss => ss.AuthorId == author.Id);
if (stats == null)
{
continue;
}
LinkArtistStatistics(artist, stats);
LinkArtistStatistics(author, stats);
}
}
private void LinkArtistStatistics(ArtistResource resource, AuthorStatistics artistStatistics)
private void LinkArtistStatistics(AuthorResource resource, AuthorStatistics authorStatistics)
{
resource.Statistics = artistStatistics.ToResource();
resource.Statistics = authorStatistics.ToResource();
}
//private void PopulateAlternateTitles(List<ArtistResource> resources)
@ -234,7 +234,7 @@ namespace Readarr.Api.V1.Artist
// resource.AlternateTitles = mappings.Select(v => new AlternateTitleResource { Title = v.Title, SeasonNumber = v.SeasonNumber, SceneSeasonNumber = v.SceneSeasonNumber }).ToList();
//}
private void LinkRootFolderPath(ArtistResource resource)
private void LinkRootFolderPath(AuthorResource resource)
{
resource.RootFolderPath = _rootFolderService.GetBestRootFolderPath(resource.Path);
}
@ -246,7 +246,7 @@ namespace Readarr.Api.V1.Artist
public void Handle(BookEditedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Album.Author.Value));
BroadcastResourceChange(ModelAction.Updated, GetArtistResource(message.Book.Author.Value));
}
public void Handle(BookFileDeletedEvent message)

@ -7,30 +7,30 @@ using NzbDrone.Core.Books;
using NzbDrone.Core.MediaCover;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Author
{
public class ArtistResource : RestResource
public class AuthorResource : RestResource
{
//Todo: Sorters should be done completely on the client
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
//Todo: We should get the entire Profile instead of ID and Name separately
[JsonIgnore]
public int ArtistMetadataId { get; set; }
public int AuthorMetadataId { get; set; }
public AuthorStatusType Status { get; set; }
public bool Ended => Status == AuthorStatusType.Ended;
public string ArtistName { get; set; }
public string AuthorName { get; set; }
public string ForeignAuthorId { get; set; }
public int GoodreadsId { get; set; }
public string TitleSlug { get; set; }
public string Overview { get; set; }
public string ArtistType { get; set; }
public string AuthorType { get; set; }
public string Disambiguation { get; set; }
public List<Links> Links { get; set; }
public Book NextAlbum { get; set; }
public Book LastAlbum { get; set; }
public Book NextBook { get; set; }
public Book LastBook { get; set; }
public List<MediaCover> Images { get; set; }
@ -42,7 +42,6 @@ namespace Readarr.Api.V1.Artist
public int MetadataProfileId { get; set; }
//Editing Only
public bool AlbumFolder { get; set; }
public bool Monitored { get; set; }
public string RootFolderPath { get; set; }
@ -54,31 +53,31 @@ namespace Readarr.Api.V1.Artist
public AddAuthorOptions AddOptions { get; set; }
public Ratings Ratings { get; set; }
public ArtistStatisticsResource Statistics { get; set; }
public AuthorStatisticsResource Statistics { get; set; }
}
public static class ArtistResourceMapper
public static class AuthorResourceMapper
{
public static ArtistResource ToResource(this NzbDrone.Core.Books.Author model)
public static AuthorResource ToResource(this NzbDrone.Core.Books.Author model)
{
if (model == null)
{
return null;
}
return new ArtistResource
return new AuthorResource
{
Id = model.Id,
ArtistMetadataId = model.AuthorMetadataId,
AuthorMetadataId = model.AuthorMetadataId,
ArtistName = model.Name,
AuthorName = model.Name,
//AlternateTitles
SortName = model.SortName,
Status = model.Metadata.Value.Status,
Overview = model.Metadata.Value.Overview,
ArtistType = model.Metadata.Value.Type,
AuthorType = model.Metadata.Value.Type,
Disambiguation = model.Metadata.Value.Disambiguation,
Images = model.Metadata.Value.Images.JsonClone(),
@ -103,11 +102,11 @@ namespace Readarr.Api.V1.Artist
AddOptions = model.AddOptions,
Ratings = model.Metadata.Value.Ratings,
Statistics = new ArtistStatisticsResource()
Statistics = new AuthorStatisticsResource()
};
}
public static NzbDrone.Core.Books.Author ToModel(this ArtistResource resource)
public static NzbDrone.Core.Books.Author ToModel(this AuthorResource resource)
{
if (resource == null)
{
@ -123,14 +122,14 @@ namespace Readarr.Api.V1.Artist
ForeignAuthorId = resource.ForeignAuthorId,
GoodreadsId = resource.GoodreadsId,
TitleSlug = resource.TitleSlug,
Name = resource.ArtistName,
Name = resource.AuthorName,
Status = resource.Status,
Overview = resource.Overview,
Links = resource.Links,
Images = resource.Images,
Genres = resource.Genres,
Ratings = resource.Ratings,
Type = resource.ArtistType
Type = resource.AuthorType
},
//AlternateTitles
@ -150,21 +149,21 @@ namespace Readarr.Api.V1.Artist
};
}
public static NzbDrone.Core.Books.Author ToModel(this ArtistResource resource, NzbDrone.Core.Books.Author artist)
public static NzbDrone.Core.Books.Author ToModel(this AuthorResource resource, NzbDrone.Core.Books.Author author)
{
var updatedArtist = resource.ToModel();
var updatedAuthor = resource.ToModel();
artist.ApplyChanges(updatedArtist);
author.ApplyChanges(updatedAuthor);
return artist;
return author;
}
public static List<ArtistResource> ToResource(this IEnumerable<NzbDrone.Core.Books.Author> artist)
public static List<AuthorResource> ToResource(this IEnumerable<NzbDrone.Core.Books.Author> author)
{
return artist.Select(ToResource).ToList();
return author.Select(ToResource).ToList();
}
public static List<NzbDrone.Core.Books.Author> ToModel(this IEnumerable<ArtistResource> resources)
public static List<NzbDrone.Core.Books.Author> ToModel(this IEnumerable<AuthorResource> resources)
{
return resources.Select(ToModel).ToList();
}

@ -0,0 +1,43 @@
using NzbDrone.Core.AuthorStats;
namespace Readarr.Api.V1.Author
{
public class AuthorStatisticsResource
{
public int BookCount { get; set; }
public int BookFileCount { get; set; }
public int TotalBookCount { get; set; }
public long SizeOnDisk { get; set; }
public decimal PercentOfBooks
{
get
{
if (BookCount == 0)
{
return 0;
}
return BookFileCount / (decimal)BookCount * 100;
}
}
}
public static class AuthorStatisticsResourceMapper
{
public static AuthorStatisticsResource ToResource(this AuthorStatistics model)
{
if (model == null)
{
return null;
}
return new AuthorStatisticsResource
{
BookCount = model.BookCount,
BookFileCount = model.BookFileCount,
SizeOnDisk = model.SizeOnDisk
};
}
}
}

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Qualities;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Blacklist
@ -18,7 +18,7 @@ namespace Readarr.Api.V1.Blacklist
public string Indexer { get; set; }
public string Message { get; set; }
public ArtistResource Artist { get; set; }
public AuthorResource Author { get; set; }
}
public static class BlacklistResourceMapper
@ -43,7 +43,7 @@ namespace Readarr.Api.V1.Blacklist
Indexer = model.Indexer,
Message = model.Message,
Artist = model.Author.ToResource()
Author = model.Author.ToResource()
};
}
}

@ -1,11 +1,11 @@
using System.Collections.Generic;
using NzbDrone.Core.Qualities;
namespace Readarr.Api.V1.TrackFiles
namespace Readarr.Api.V1.BookFiles
{
public class TrackFileListResource
public class BookFileListResource
{
public List<int> TrackFileIds { get; set; }
public List<int> BookFileIds { get; set; }
public QualityModel Quality { get; set; }
}
}

@ -14,9 +14,9 @@ using Readarr.Http;
using Readarr.Http.Extensions;
using HttpStatusCode = System.Net.HttpStatusCode;
namespace Readarr.Api.V1.TrackFiles
namespace Readarr.Api.V1.BookFiles
{
public class TrackFileModule : ReadarrRestModuleWithSignalR<TrackFileResource, BookFile>,
public class BookFileModule : ReadarrRestModuleWithSignalR<BookFileResource, BookFile>,
IHandle<BookFileAddedEvent>,
IHandle<BookFileDeletedEvent>
{
@ -27,7 +27,7 @@ namespace Readarr.Api.V1.TrackFiles
private readonly IBookService _bookService;
private readonly IUpgradableSpecification _upgradableSpecification;
public TrackFileModule(IBroadcastSignalRMessage signalRBroadcaster,
public BookFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService,
IDeleteMediaFiles mediaFileDeletionService,
IAudioTagService audioTagService,
@ -43,44 +43,44 @@ namespace Readarr.Api.V1.TrackFiles
_bookService = bookService;
_upgradableSpecification = upgradableSpecification;
GetResourceById = GetTrackFile;
GetResourceAll = GetTrackFiles;
GetResourceById = GetBookFile;
GetResourceAll = GetBookFiles;
UpdateResource = SetQuality;
DeleteResource = DeleteTrackFile;
DeleteResource = DeleteBookFile;
Put("/editor", trackFiles => SetQuality());
Delete("/bulk", trackFiles => DeleteTrackFiles());
Delete("/bulk", trackFiles => DeleteBookFiles());
}
private TrackFileResource MapToResource(BookFile trackFile)
private BookFileResource MapToResource(BookFile bookFile)
{
if (trackFile.BookId > 0 && trackFile.Author != null && trackFile.Author.Value != null)
if (bookFile.BookId > 0 && bookFile.Author != null && bookFile.Author.Value != null)
{
return trackFile.ToResource(trackFile.Author.Value, _upgradableSpecification);
return bookFile.ToResource(bookFile.Author.Value, _upgradableSpecification);
}
else
{
return trackFile.ToResource();
return bookFile.ToResource();
}
}
private TrackFileResource GetTrackFile(int id)
private BookFileResource GetBookFile(int id)
{
var resource = MapToResource(_mediaFileService.Get(id));
resource.AudioTags = _audioTagService.ReadTags(resource.Path);
return resource;
}
private List<TrackFileResource> GetTrackFiles()
private List<BookFileResource> GetBookFiles()
{
var authorIdQuery = Request.Query.AuthorId;
var trackFileIdsQuery = Request.Query.TrackFileIds;
var bookFileIdsQuery = Request.Query.TrackFileIds;
var bookIdQuery = Request.Query.BookId;
var unmappedQuery = Request.Query.Unmapped;
if (!authorIdQuery.HasValue && !trackFileIdsQuery.HasValue && !bookIdQuery.HasValue && !unmappedQuery.HasValue)
if (!authorIdQuery.HasValue && !bookFileIdsQuery.HasValue && !bookIdQuery.HasValue && !unmappedQuery.HasValue)
{
throw new Readarr.Http.REST.BadRequestException("authorId, bookId, trackFileIds or unmapped must be provided");
throw new Readarr.Http.REST.BadRequestException("authorId, bookId, bookFileIds or unmapped must be provided");
}
if (unmappedQuery.HasValue && Convert.ToBoolean(unmappedQuery.Value))
@ -92,9 +92,9 @@ namespace Readarr.Api.V1.TrackFiles
if (authorIdQuery.HasValue && !bookIdQuery.HasValue)
{
int authorId = Convert.ToInt32(authorIdQuery.Value);
var artist = _authorService.GetAuthor(authorId);
var author = _authorService.GetAuthor(authorId);
return _mediaFileService.GetFilesByAuthor(authorId).ConvertAll(f => f.ToResource(artist, _upgradableSpecification));
return _mediaFileService.GetFilesByAuthor(authorId).ConvertAll(f => f.ToResource(author, _upgradableSpecification));
}
if (bookIdQuery.HasValue)
@ -105,84 +105,84 @@ namespace Readarr.Api.V1.TrackFiles
.Select(e => Convert.ToInt32(e))
.ToList();
var result = new List<TrackFileResource>();
var result = new List<BookFileResource>();
foreach (var bookId in bookIds)
{
var album = _bookService.GetBook(bookId);
var albumArtist = _authorService.GetAuthor(album.AuthorId);
result.AddRange(_mediaFileService.GetFilesByBook(album.Id).ConvertAll(f => f.ToResource(albumArtist, _upgradableSpecification)));
var book = _bookService.GetBook(bookId);
var bookAuthor = _authorService.GetAuthor(book.AuthorId);
result.AddRange(_mediaFileService.GetFilesByBook(book.Id).ConvertAll(f => f.ToResource(bookAuthor, _upgradableSpecification)));
}
return result;
}
else
{
string trackFileIdsValue = trackFileIdsQuery.Value.ToString();
string bookFileIdsValue = bookFileIdsQuery.Value.ToString();
var trackFileIds = trackFileIdsValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
var bookFileIds = bookFileIdsValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(e => Convert.ToInt32(e))
.ToList();
// trackfiles will come back with the artist already populated
var trackFiles = _mediaFileService.Get(trackFileIds);
return trackFiles.ConvertAll(e => MapToResource(e));
var bookFiles = _mediaFileService.Get(bookFileIds);
return bookFiles.ConvertAll(e => MapToResource(e));
}
}
private void SetQuality(TrackFileResource trackFileResource)
private void SetQuality(BookFileResource bookFileResource)
{
var trackFile = _mediaFileService.Get(trackFileResource.Id);
trackFile.Quality = trackFileResource.Quality;
_mediaFileService.Update(trackFile);
var bookFile = _mediaFileService.Get(bookFileResource.Id);
bookFile.Quality = bookFileResource.Quality;
_mediaFileService.Update(bookFile);
}
private object SetQuality()
{
var resource = Request.Body.FromJson<TrackFileListResource>();
var trackFiles = _mediaFileService.Get(resource.TrackFileIds);
var resource = Request.Body.FromJson<BookFileListResource>();
var bookFiles = _mediaFileService.Get(resource.BookFileIds);
foreach (var trackFile in trackFiles)
foreach (var bookFile in bookFiles)
{
if (resource.Quality != null)
{
trackFile.Quality = resource.Quality;
bookFile.Quality = resource.Quality;
}
}
_mediaFileService.Update(trackFiles);
_mediaFileService.Update(bookFiles);
return ResponseWithCode(trackFiles.ConvertAll(f => f.ToResource(trackFiles.First().Author.Value, _upgradableSpecification)),
return ResponseWithCode(bookFiles.ConvertAll(f => f.ToResource(bookFiles.First().Author.Value, _upgradableSpecification)),
Nancy.HttpStatusCode.Accepted);
}
private void DeleteTrackFile(int id)
private void DeleteBookFile(int id)
{
var trackFile = _mediaFileService.Get(id);
var bookFile = _mediaFileService.Get(id);
if (trackFile == null)
if (bookFile == null)
{
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Track file not found");
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Book file not found");
}
if (trackFile.BookId > 0 && trackFile.Author != null && trackFile.Author.Value != null)
if (bookFile.BookId > 0 && bookFile.Author != null && bookFile.Author.Value != null)
{
_mediaFileDeletionService.DeleteTrackFile(trackFile.Author.Value, trackFile);
_mediaFileDeletionService.DeleteTrackFile(bookFile.Author.Value, bookFile);
}
else
{
_mediaFileDeletionService.DeleteTrackFile(trackFile, "Unmapped_Files");
_mediaFileDeletionService.DeleteTrackFile(bookFile, "Unmapped_Files");
}
}
private object DeleteTrackFiles()
private object DeleteBookFiles()
{
var resource = Request.Body.FromJson<TrackFileListResource>();
var trackFiles = _mediaFileService.Get(resource.TrackFileIds);
var artist = trackFiles.First().Author.Value;
var resource = Request.Body.FromJson<BookFileListResource>();
var bookFiles = _mediaFileService.Get(resource.BookFileIds);
var author = bookFiles.First().Author.Value;
foreach (var trackFile in trackFiles)
foreach (var bookFile in bookFiles)
{
_mediaFileDeletionService.DeleteTrackFile(artist, trackFile);
_mediaFileDeletionService.DeleteTrackFile(author, bookFile);
}
return new object();

@ -7,9 +7,9 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using Readarr.Http.REST;
namespace Readarr.Api.V1.TrackFiles
namespace Readarr.Api.V1.BookFiles
{
public class TrackFileResource : RestResource
public class BookFileResource : RestResource
{
public int AuthorId { get; set; }
public int BookId { get; set; }
@ -24,7 +24,7 @@ namespace Readarr.Api.V1.TrackFiles
public ParsedTrackInfo AudioTags { get; set; }
}
public static class TrackFileResourceMapper
public static class BookFileResourceMapper
{
private static int QualityWeight(QualityModel quality)
{
@ -39,14 +39,14 @@ namespace Readarr.Api.V1.TrackFiles
return qualityWeight;
}
public static TrackFileResource ToResource(this BookFile model)
public static BookFileResource ToResource(this BookFile model)
{
if (model == null)
{
return null;
}
return new TrackFileResource
return new BookFileResource
{
Id = model.Id,
BookId = model.BookId,
@ -59,18 +59,18 @@ namespace Readarr.Api.V1.TrackFiles
};
}
public static TrackFileResource ToResource(this BookFile model, NzbDrone.Core.Books.Author artist, IUpgradableSpecification upgradableSpecification)
public static BookFileResource ToResource(this BookFile model, NzbDrone.Core.Books.Author author, IUpgradableSpecification upgradableSpecification)
{
if (model == null)
{
return null;
}
return new TrackFileResource
return new BookFileResource
{
Id = model.Id,
AuthorId = artist.Id,
AuthorId = author.Id,
BookId = model.BookId,
Path = model.Path,
Size = model.Size,
@ -78,7 +78,7 @@ namespace Readarr.Api.V1.TrackFiles
Quality = model.Quality,
QualityWeight = QualityWeight(model.Quality),
MediaInfo = model.MediaInfo.ToResource(),
QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(artist.QualityProfile.Value, model.Quality)
QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(author.QualityProfile.Value, model.Quality)
};
}
}

@ -2,7 +2,7 @@ using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
using Readarr.Http.REST;
namespace Readarr.Api.V1.TrackFiles
namespace Readarr.Api.V1.BookFiles
{
public class MediaInfoResource : RestResource
{

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Readarr.Api.V1.Books;
namespace Readarr.Api.V1.Bookshelf
{
public class BookshelfAuthorResource
{
public int Id { get; set; }
public bool? Monitored { get; set; }
public List<BookResource> Books { get; set; }
}
}

@ -0,0 +1,47 @@
using System.Linq;
using Nancy;
using NzbDrone.Core.Books;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Bookshelf
{
public class BookshelfModule : ReadarrV1Module
{
private readonly IAuthorService _authorService;
private readonly IBookMonitoredService _bookMonitoredService;
public BookshelfModule(IAuthorService authorService, IBookMonitoredService bookMonitoredService)
: base("/bookshelf")
{
_authorService = authorService;
_bookMonitoredService = bookMonitoredService;
Post("/", artist => UpdateAll());
}
private object UpdateAll()
{
//Read from request
var request = Request.Body.FromJson<BookshelfResource>();
var authorToUpdate = _authorService.GetAuthors(request.Authors.Select(s => s.Id));
foreach (var s in request.Authors)
{
var author = authorToUpdate.Single(c => c.Id == s.Id);
if (s.Monitored.HasValue)
{
author.Monitored = s.Monitored.Value;
}
if (request.MonitoringOptions != null && request.MonitoringOptions.Monitor == MonitorTypes.None)
{
author.Monitored = false;
}
_bookMonitoredService.SetBookMonitoredStatus(author, request.MonitoringOptions);
}
return ResponseWithCode("ok", HttpStatusCode.Accepted);
}
}
}

@ -1,11 +1,11 @@
using System.Collections.Generic;
using NzbDrone.Core.Books;
namespace Readarr.Api.V1.AlbumStudio
namespace Readarr.Api.V1.Bookshelf
{
public class AlbumStudioResource
public class BookshelfResource
{
public List<AlbumStudioArtistResource> Artist { get; set; }
public List<BookshelfAuthorResource> Authors { get; set; }
public MonitoringOptions MonitoringOptions { get; set; }
}
}

@ -5,14 +5,14 @@ using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using Readarr.Http;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class AlbumLookupModule : ReadarrRestModule<AlbumResource>
public class BookLookupModule : ReadarrRestModule<BookResource>
{
private readonly ISearchForNewBook _searchProxy;
public AlbumLookupModule(ISearchForNewBook searchProxy)
: base("/album/lookup")
public BookLookupModule(ISearchForNewBook searchProxy)
: base("/book/lookup")
{
_searchProxy = searchProxy;
Get("/", x => Search());
@ -24,12 +24,12 @@ namespace Readarr.Api.V1.Albums
return MapToResource(searchResults).ToList();
}
private static IEnumerable<AlbumResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Book> albums)
private static IEnumerable<BookResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Book> books)
{
foreach (var currentAlbum in albums)
foreach (var currentBook in books)
{
var resource = currentAlbum.ToResource();
var cover = currentAlbum.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
var resource = currentBook.ToResource();
var cover = currentBook.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
if (cover != null)
{
resource.RemoteCover = cover.Url;

@ -19,9 +19,9 @@ using NzbDrone.Core.Validation.Paths;
using NzbDrone.SignalR;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class AlbumModule : AlbumModuleWithSignalR,
public class BookModule : BookModuleWithSignalR,
IHandle<BookGrabbedEvent>,
IHandle<BookEditedEvent>,
IHandle<BookUpdatedEvent>,
@ -32,53 +32,53 @@ namespace Readarr.Api.V1.Albums
protected readonly IAuthorService _authorService;
protected readonly IAddBookService _addBookService;
public AlbumModule(IAuthorService authorService,
public BookModule(IAuthorService authorService,
IBookService bookService,
IAddBookService addBookService,
IAuthorStatisticsService artistStatisticsService,
IAuthorStatisticsService authorStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster,
QualityProfileExistsValidator qualityProfileExistsValidator,
MetadataProfileExistsValidator metadataProfileExistsValidator)
: base(bookService, artistStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster)
: base(bookService, authorStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster)
{
_authorService = authorService;
_addBookService = addBookService;
GetResourceAll = GetAlbums;
CreateResource = AddAlbum;
UpdateResource = UpdateAlbum;
DeleteResource = DeleteAlbum;
Put("/monitor", x => SetAlbumsMonitored());
GetResourceAll = GetBooks;
CreateResource = AddBook;
UpdateResource = UpdateBook;
DeleteResource = DeleteBook;
Put("/monitor", x => SetBooksMonitored());
PostValidator.RuleFor(s => s.ForeignBookId).NotEmpty();
PostValidator.RuleFor(s => s.Artist.QualityProfileId).SetValidator(qualityProfileExistsValidator);
PostValidator.RuleFor(s => s.Artist.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
PostValidator.RuleFor(s => s.Artist.RootFolderPath).IsValidPath().When(s => s.Artist.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.Artist.ForeignAuthorId).NotEmpty();
PostValidator.RuleFor(s => s.Author.QualityProfileId).SetValidator(qualityProfileExistsValidator);
PostValidator.RuleFor(s => s.Author.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
PostValidator.RuleFor(s => s.Author.RootFolderPath).IsValidPath().When(s => s.Author.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.Author.ForeignAuthorId).NotEmpty();
}
private List<AlbumResource> GetAlbums()
private List<BookResource> GetBooks()
{
var authorIdQuery = Request.Query.AuthorId;
var bookIdsQuery = Request.Query.BookIds;
var slugQuery = Request.Query.TitleSlug;
var includeAllArtistAlbumsQuery = Request.Query.IncludeAllArtistAlbums;
var includeAllAuthorBooksQuery = Request.Query.IncludeAllAuthorBooks;
if (!Request.Query.AuthorId.HasValue && !bookIdsQuery.HasValue && !slugQuery.HasValue)
{
var albums = _bookService.GetAllBooks();
var books = _bookService.GetAllBooks();
var artists = _authorService.GetAllAuthors().ToDictionary(x => x.AuthorMetadataId);
var authors = _authorService.GetAllAuthors().ToDictionary(x => x.AuthorMetadataId);
foreach (var album in albums)
foreach (var book in books)
{
album.Author = artists[album.AuthorMetadataId];
book.Author = authors[book.AuthorMetadataId];
}
return MapToResource(albums, false);
return MapToResource(books, false);
}
if (authorIdQuery.HasValue)
@ -92,20 +92,20 @@ namespace Readarr.Api.V1.Albums
{
string titleSlug = slugQuery.Value.ToString();
var album = _bookService.FindBySlug(titleSlug);
var book = _bookService.FindBySlug(titleSlug);
if (album == null)
if (book == null)
{
return MapToResource(new List<Book>(), false);
}
if (includeAllArtistAlbumsQuery.HasValue && Convert.ToBoolean(includeAllArtistAlbumsQuery.Value))
if (includeAllAuthorBooksQuery.HasValue && Convert.ToBoolean(includeAllAuthorBooksQuery.Value))
{
return MapToResource(_bookService.GetBooksByAuthor(album.AuthorId), false);
return MapToResource(_bookService.GetBooksByAuthor(book.AuthorId), false);
}
else
{
return MapToResource(new List<Book> { album }, false);
return MapToResource(new List<Book> { book }, false);
}
}
@ -118,25 +118,25 @@ namespace Readarr.Api.V1.Albums
return MapToResource(_bookService.GetBooks(bookIds), false);
}
private int AddAlbum(AlbumResource albumResource)
private int AddBook(BookResource bookResource)
{
var album = _addBookService.AddBook(albumResource.ToModel());
var book = _addBookService.AddBook(bookResource.ToModel());
return album.Id;
return book.Id;
}
private void UpdateAlbum(AlbumResource albumResource)
private void UpdateBook(BookResource bookResource)
{
var album = _bookService.GetBook(albumResource.Id);
var book = _bookService.GetBook(bookResource.Id);
var model = albumResource.ToModel(album);
var model = bookResource.ToModel(book);
_bookService.UpdateBook(model);
BroadcastResourceChange(ModelAction.Updated, model.Id);
}
private void DeleteAlbum(int id)
private void DeleteBook(int id)
{
var deleteFiles = Request.GetBooleanQueryParameter("deleteFiles");
var addImportListExclusion = Request.GetBooleanQueryParameter("addImportListExclusion");
@ -144,9 +144,9 @@ namespace Readarr.Api.V1.Albums
_bookService.DeleteBook(id, deleteFiles, addImportListExclusion);
}
private object SetAlbumsMonitored()
private object SetBooksMonitored()
{
var resource = Request.Body.FromJson<AlbumsMonitoredResource>();
var resource = Request.Body.FromJson<BooksMonitoredResource>();
_bookService.SetMonitored(resource.BookIds, resource.Monitored);
@ -155,9 +155,9 @@ namespace Readarr.Api.V1.Albums
public void Handle(BookGrabbedEvent message)
{
foreach (var album in message.Book.Books)
foreach (var book in message.Book.Books)
{
var resource = album.ToResource();
var resource = book.ToResource();
resource.Grabbed = true;
BroadcastResourceChange(ModelAction.Updated, resource);
@ -166,7 +166,7 @@ namespace Readarr.Api.V1.Albums
public void Handle(BookEditedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, MapToResource(message.Album, true));
BroadcastResourceChange(ModelAction.Updated, MapToResource(message.Book, true));
}
public void Handle(BookUpdatedEvent message)

@ -0,0 +1,133 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.AuthorStats;
using NzbDrone.Core.Books;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaCover;
using NzbDrone.SignalR;
using Readarr.Api.V1.Author;
using Readarr.Http;
namespace Readarr.Api.V1.Books
{
public abstract class BookModuleWithSignalR : ReadarrRestModuleWithSignalR<BookResource, Book>
{
protected readonly IBookService _bookService;
protected readonly IAuthorStatisticsService _authorStatisticsService;
protected readonly IUpgradableSpecification _qualityUpgradableSpecification;
protected readonly IMapCoversToLocal _coverMapper;
protected BookModuleWithSignalR(IBookService bookService,
IAuthorStatisticsService authorStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(signalRBroadcaster)
{
_bookService = bookService;
_authorStatisticsService = authorStatisticsService;
_coverMapper = coverMapper;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetBook;
}
protected BookModuleWithSignalR(IBookService bookService,
IAuthorStatisticsService authorStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster,
string resource)
: base(signalRBroadcaster, resource)
{
_bookService = bookService;
_authorStatisticsService = authorStatisticsService;
_coverMapper = coverMapper;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetBook;
}
protected BookResource GetBook(int id)
{
var book = _bookService.GetBook(id);
var resource = MapToResource(book, true);
return resource;
}
protected BookResource MapToResource(Book book, bool includeAuthor)
{
var resource = book.ToResource();
if (includeAuthor)
{
var artist = book.Author.Value;
resource.Author = artist.ToResource();
}
FetchAndLinkAlbumStatistics(resource);
MapCoversToLocal(resource);
return resource;
}
protected List<BookResource> MapToResource(List<Book> books, bool includeAuthor)
{
var result = books.ToResource();
if (includeAuthor)
{
var authorDict = new Dictionary<int, NzbDrone.Core.Books.Author>();
for (var i = 0; i < books.Count; i++)
{
var book = books[i];
var resource = result[i];
var author = authorDict.GetValueOrDefault(books[i].AuthorMetadataId) ?? book.Author?.Value;
authorDict[author.AuthorMetadataId] = author;
resource.Author = author.ToResource();
}
}
var authorStats = _authorStatisticsService.AuthorStatistics();
LinkAuthorStatistics(result, authorStats);
MapCoversToLocal(result.ToArray());
return result;
}
private void FetchAndLinkAlbumStatistics(BookResource resource)
{
LinkAuthorStatistics(resource, _authorStatisticsService.AuthorStatistics(resource.AuthorId));
}
private void LinkAuthorStatistics(List<BookResource> resources, List<AuthorStatistics> authorStatistics)
{
foreach (var book in resources)
{
var stats = authorStatistics.SingleOrDefault(ss => ss.AuthorId == book.AuthorId);
LinkAuthorStatistics(book, stats);
}
}
private void LinkAuthorStatistics(BookResource resource, AuthorStatistics authorStatistics)
{
if (authorStatistics?.BookStatistics != null)
{
var dictBookStats = authorStatistics.BookStatistics.ToDictionary(v => v.BookId);
resource.Statistics = dictBookStats.GetValueOrDefault(resource.Id).ToResource();
}
}
private void MapCoversToLocal(params BookResource[] books)
{
foreach (var bookResource in books)
{
_coverMapper.ConvertToLocalUrls(bookResource.Id, MediaCoverEntity.Book, bookResource.Images);
}
}
}
}

@ -4,13 +4,13 @@ using System.Linq;
using Newtonsoft.Json;
using NzbDrone.Core.Books;
using NzbDrone.Core.MediaCover;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.TrackFiles;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.BookFiles;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class AlbumResource : RestResource
public class BookResource : RestResource
{
public string Title { get; set; }
public string Disambiguation { get; set; }
@ -27,10 +27,10 @@ namespace Readarr.Api.V1.Albums
public Ratings Ratings { get; set; }
public DateTime? ReleaseDate { get; set; }
public List<string> Genres { get; set; }
public ArtistResource Artist { get; set; }
public AuthorResource Author { get; set; }
public List<MediaCover> Images { get; set; }
public List<Links> Links { get; set; }
public AlbumStatisticsResource Statistics { get; set; }
public BookStatisticsResource Statistics { get; set; }
public AddBookOptions AddOptions { get; set; }
public string RemoteCover { get; set; }
@ -39,16 +39,16 @@ namespace Readarr.Api.V1.Albums
public bool Grabbed { get; set; }
}
public static class AlbumResourceMapper
public static class BookResourceMapper
{
public static AlbumResource ToResource(this Book model)
public static BookResource ToResource(this Book model)
{
if (model == null)
{
return null;
}
return new AlbumResource
return new BookResource
{
Id = model.Id,
AuthorId = model.AuthorId,
@ -68,18 +68,18 @@ namespace Readarr.Api.V1.Albums
Images = model.Images,
Links = model.Links,
Ratings = model.Ratings,
Artist = model.Author?.Value.ToResource()
Author = model.Author?.Value.ToResource()
};
}
public static Book ToModel(this AlbumResource resource)
public static Book ToModel(this BookResource resource)
{
if (resource == null)
{
return null;
}
var artist = resource.Artist?.ToModel() ?? new NzbDrone.Core.Books.Author();
var author = resource.Author?.ToModel() ?? new NzbDrone.Core.Books.Author();
return new Book
{
@ -97,26 +97,26 @@ namespace Readarr.Api.V1.Albums
Images = resource.Images,
Monitored = resource.Monitored,
AddOptions = resource.AddOptions,
Author = artist,
AuthorMetadata = artist.Metadata.Value
Author = author,
AuthorMetadata = author.Metadata.Value
};
}
public static Book ToModel(this AlbumResource resource, Book album)
public static Book ToModel(this BookResource resource, Book book)
{
var updatedAlbum = resource.ToModel();
var updatedBook = resource.ToModel();
album.ApplyChanges(updatedAlbum);
book.ApplyChanges(updatedBook);
return album;
return book;
}
public static List<AlbumResource> ToResource(this IEnumerable<Book> models)
public static List<BookResource> ToResource(this IEnumerable<Book> models)
{
return models?.Select(ToResource).ToList();
}
public static List<Book> ToModel(this IEnumerable<AlbumResource> resources)
public static List<Book> ToModel(this IEnumerable<BookResource> resources)
{
return resources.Select(ToModel).ToList();
}

@ -1,12 +1,11 @@
using NzbDrone.Core.AuthorStats;
namespace Readarr.Api.V1.Artist
namespace Readarr.Api.V1.Books
{
public class ArtistStatisticsResource
public class BookStatisticsResource
{
public int BookCount { get; set; }
public int BookFileCount { get; set; }
public int TrackCount { get; set; }
public int BookCount { get; set; }
public int TotalTrackCount { get; set; }
public long SizeOnDisk { get; set; }
@ -14,29 +13,29 @@ namespace Readarr.Api.V1.Artist
{
get
{
if (TrackCount == 0)
if (BookCount == 0)
{
return 0;
}
return BookFileCount / (decimal)TrackCount * 100;
return BookFileCount / (decimal)BookCount * 100;
}
}
}
public static class ArtistStatisticsResourceMapper
public static class BookStatisticsResourceMapper
{
public static ArtistStatisticsResource ToResource(this AuthorStatistics model)
public static BookStatisticsResource ToResource(this BookStatistics model)
{
if (model == null)
{
return null;
}
return new ArtistStatisticsResource
return new BookStatisticsResource
{
BookCount = model.BookCount,
BookFileCount = model.BookFileCount,
BookCount = model.BookCount,
SizeOnDisk = model.SizeOnDisk
};
}

@ -1,8 +1,8 @@
using System.Collections.Generic;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class AlbumsMonitoredResource
public class BooksMonitoredResource
{
public List<int> BookIds { get; set; }
public bool Monitored { get; set; }

@ -3,21 +3,21 @@ using NzbDrone.Core.MediaFiles;
using Readarr.Http;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class RenameTrackModule : ReadarrRestModule<RenameTrackResource>
public class RenameBookModule : ReadarrRestModule<RenameBookResource>
{
private readonly IRenameTrackFileService _renameTrackFileService;
private readonly IRenameBookFileService _renameBookFileService;
public RenameTrackModule(IRenameTrackFileService renameTrackFileService)
public RenameBookModule(IRenameBookFileService renameBookFileService)
: base("rename")
{
_renameTrackFileService = renameTrackFileService;
_renameBookFileService = renameBookFileService;
GetResourceAll = GetTracks;
GetResourceAll = GetBookFiles;
}
private List<RenameTrackResource> GetTracks()
private List<RenameBookResource> GetBookFiles()
{
int authorId;
@ -33,10 +33,10 @@ namespace Readarr.Api.V1.Albums
if (Request.Query.bookId.HasValue)
{
var bookId = (int)Request.Query.bookId;
return _renameTrackFileService.GetRenamePreviews(authorId, bookId).ToResource();
return _renameBookFileService.GetRenamePreviews(authorId, bookId).ToResource();
}
return _renameTrackFileService.GetRenamePreviews(authorId).ToResource();
return _renameBookFileService.GetRenamePreviews(authorId).ToResource();
}
}
}

@ -2,37 +2,37 @@ using System.Collections.Generic;
using System.Linq;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class RenameTrackResource : RestResource
public class RenameBookResource : RestResource
{
public int AuthorId { get; set; }
public int BookId { get; set; }
public int TrackFileId { get; set; }
public int BookFileId { get; set; }
public string ExistingPath { get; set; }
public string NewPath { get; set; }
}
public static class RenameTrackResourceMapper
public static class RenameBookResourceMapper
{
public static RenameTrackResource ToResource(this NzbDrone.Core.MediaFiles.RenameBookFilePreview model)
public static RenameBookResource ToResource(this NzbDrone.Core.MediaFiles.RenameBookFilePreview model)
{
if (model == null)
{
return null;
}
return new RenameTrackResource
return new RenameBookResource
{
AuthorId = model.AuthorId,
BookId = model.BookId,
TrackFileId = model.BookFileId,
BookFileId = model.BookFileId,
ExistingPath = model.ExistingPath,
NewPath = model.NewPath
};
}
public static List<RenameTrackResource> ToResource(this IEnumerable<NzbDrone.Core.MediaFiles.RenameBookFilePreview> models)
public static List<RenameBookResource> ToResource(this IEnumerable<NzbDrone.Core.MediaFiles.RenameBookFilePreview> models)
{
return models.Select(ToResource).ToList();
}

@ -4,21 +4,21 @@ using NzbDrone.Core.MediaFiles;
using Readarr.Http;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class RetagTrackModule : ReadarrRestModule<RetagTrackResource>
public class RetagBookModule : ReadarrRestModule<RetagBookResource>
{
private readonly IAudioTagService _audioTagService;
public RetagTrackModule(IAudioTagService audioTagService)
public RetagBookModule(IAudioTagService audioTagService)
: base("retag")
{
_audioTagService = audioTagService;
GetResourceAll = GetTracks;
GetResourceAll = GetBooks;
}
private List<RetagTrackResource> GetTracks()
private List<RetagBookResource> GetBooks()
{
if (Request.Query.bookId.HasValue)
{

@ -2,7 +2,7 @@ using System.Collections.Generic;
using System.Linq;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class TagDifference
{
@ -11,31 +11,31 @@ namespace Readarr.Api.V1.Albums
public string NewValue { get; set; }
}
public class RetagTrackResource : RestResource
public class RetagBookResource : RestResource
{
public int AuthorId { get; set; }
public int BookId { get; set; }
public List<int> TrackNumbers { get; set; }
public int TrackFileId { get; set; }
public int BookFileId { get; set; }
public string Path { get; set; }
public List<TagDifference> Changes { get; set; }
}
public static class RetagTrackResourceMapper
{
public static RetagTrackResource ToResource(this NzbDrone.Core.MediaFiles.RetagBookFilePreview model)
public static RetagBookResource ToResource(this NzbDrone.Core.MediaFiles.RetagBookFilePreview model)
{
if (model == null)
{
return null;
}
return new RetagTrackResource
return new RetagBookResource
{
AuthorId = model.AuthorId,
BookId = model.BookId,
TrackNumbers = model.TrackNumbers.ToList(),
TrackFileId = model.BookFileId,
BookFileId = model.BookFileId,
Path = model.Path,
Changes = model.Changes.Select(x => new TagDifference
{
@ -46,7 +46,7 @@ namespace Readarr.Api.V1.Albums
};
}
public static List<RetagTrackResource> ToResource(this IEnumerable<NzbDrone.Core.MediaFiles.RetagBookFilePreview> models)
public static List<RetagBookResource> ToResource(this IEnumerable<NzbDrone.Core.MediaFiles.RetagBookFilePreview> models)
{
return models.Select(ToResource).ToList();
}

@ -61,35 +61,35 @@ namespace Readarr.Api.V1.Calendar
tags.AddRange(tagInput.Split(',').Select(_tagService.GetTag).Select(t => t.Id));
}
var albums = _bookService.BooksBetweenDates(start, end, unmonitored);
var books = _bookService.BooksBetweenDates(start, end, unmonitored);
var calendar = new Ical.Net.Calendar
{
ProductId = "-//readarr.com//Readarr//EN"
};
var calendarName = "Readarr Music Schedule";
var calendarName = "Readarr Book Schedule";
calendar.AddProperty(new CalendarProperty("NAME", calendarName));
calendar.AddProperty(new CalendarProperty("X-WR-CALNAME", calendarName));
foreach (var album in albums.OrderBy(v => v.ReleaseDate.Value))
foreach (var book in books.OrderBy(v => v.ReleaseDate.Value))
{
var artist = _authorService.GetAuthor(album.AuthorId); // Temp fix TODO: Figure out why Album.Artist is not populated during AlbumsBetweenDates Query
var author = _authorService.GetAuthor(book.AuthorId); // Temp fix TODO: Figure out why Album.Artist is not populated during AlbumsBetweenDates Query
if (tags.Any() && tags.None(artist.Tags.Contains))
if (tags.Any() && tags.None(author.Tags.Contains))
{
continue;
}
var occurrence = calendar.Create<CalendarEvent>();
occurrence.Uid = "Readarr_album_" + album.Id;
occurrence.Uid = "Readarr_book_" + book.Id;
//occurrence.Status = album.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
occurrence.Description = album.Overview;
occurrence.Categories = album.Genres;
occurrence.Description = book.Overview;
occurrence.Categories = book.Genres;
occurrence.Start = new CalDateTime(album.ReleaseDate.Value.ToLocalTime()) { HasTime = false };
occurrence.Start = new CalDateTime(book.ReleaseDate.Value.ToLocalTime()) { HasTime = false };
occurrence.Summary = $"{artist.Name} - {album.Title}";
occurrence.Summary = $"{author.Name} - {book.Title}";
}
var serializer = (IStringSerializer)new SerializerFactory().Build(calendar.GetType(), new SerializationContext());

@ -6,32 +6,32 @@ using NzbDrone.Core.Books;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaCover;
using NzbDrone.SignalR;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Books;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Calendar
{
public class CalendarModule : AlbumModuleWithSignalR
public class CalendarModule : BookModuleWithSignalR
{
public CalendarModule(IBookService bookService,
IAuthorStatisticsService artistStatisticsService,
IAuthorStatisticsService authorStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(bookService, artistStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster, "calendar")
: base(bookService, authorStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster, "calendar")
{
GetResourceAll = GetCalendar;
}
private List<AlbumResource> GetCalendar()
private List<BookResource> GetCalendar()
{
var start = DateTime.Today;
var end = DateTime.Today.AddDays(2);
var includeUnmonitored = Request.GetBooleanQueryParameter("unmonitored");
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
//TODO: Add Album Image support to AlbumModuleWithSignalR
var includeAlbumImages = Request.GetBooleanQueryParameter("includeAlbumImages");
var includeBookImages = Request.GetBooleanQueryParameter("includeBookImages");
var queryStart = Request.Query.Start;
var queryEnd = Request.Query.End;
@ -46,7 +46,7 @@ namespace Readarr.Api.V1.Calendar
end = DateTime.Parse(queryEnd.Value);
}
var resources = MapToResource(_bookService.BooksBetweenDates(start, end, includeUnmonitored), includeArtist);
var resources = MapToResource(_bookService.BooksBetweenDates(start, end, includeUnmonitored), includeAuthor);
return resources.OrderBy(e => e.ReleaseDate).ToList();
}

@ -7,11 +7,11 @@ namespace Readarr.Api.V1.Config
{
public class MediaManagementConfigResource : RestResource
{
public bool AutoUnmonitorPreviouslyDownloadedTracks { get; set; }
public bool AutoUnmonitorPreviouslyDownloadedBooks { get; set; }
public string RecycleBin { get; set; }
public int RecycleBinCleanupDays { get; set; }
public ProperDownloadTypes DownloadPropersAndRepacks { get; set; }
public bool CreateEmptyArtistFolders { get; set; }
public bool CreateEmptyAuthorFolders { get; set; }
public bool DeleteEmptyFolders { get; set; }
public FileDateType FileDate { get; set; }
public bool WatchLibraryForChanges { get; set; }
@ -37,11 +37,11 @@ namespace Readarr.Api.V1.Config
{
return new MediaManagementConfigResource
{
AutoUnmonitorPreviouslyDownloadedTracks = model.AutoUnmonitorPreviouslyDownloadedTracks,
AutoUnmonitorPreviouslyDownloadedBooks = model.AutoUnmonitorPreviouslyDownloadedBooks,
RecycleBin = model.RecycleBin,
RecycleBinCleanupDays = model.RecycleBinCleanupDays,
DownloadPropersAndRepacks = model.DownloadPropersAndRepacks,
CreateEmptyArtistFolders = model.CreateEmptyArtistFolders,
CreateEmptyAuthorFolders = model.CreateEmptyAuthorFolders,
DeleteEmptyFolders = model.DeleteEmptyFolders,
FileDate = model.FileDate,
WatchLibraryForChanges = model.WatchLibraryForChanges,

@ -75,11 +75,11 @@ namespace Readarr.Api.V1.Config
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
sampleResource.SingleBookExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
? null
: singleTrackSampleResult.FileName;
sampleResource.ArtistFolderExample = nameSpec.AuthorFolderFormat.IsNullOrWhiteSpace()
sampleResource.AuthorFolderExample = nameSpec.AuthorFolderFormat.IsNullOrWhiteSpace()
? null
: _filenameSampleService.GetAuthorFolderSample(nameSpec);

@ -4,8 +4,8 @@ namespace Readarr.Api.V1.Config
{
public class NamingExampleResource
{
public string SingleTrackExample { get; set; }
public string ArtistFolderExample { get; set; }
public string SingleBookExample { get; set; }
public string AuthorFolderExample { get; set; }
}
public static class NamingConfigResourceMapper

@ -6,8 +6,8 @@ using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Download;
using NzbDrone.Core.History;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http;
using Readarr.Http.Extensions;
using Readarr.Http.REST;
@ -30,7 +30,7 @@ namespace Readarr.Api.V1.History
GetResourcePaged = GetHistory;
Get("/since", x => GetHistorySince());
Get("/artist", x => GetArtistHistory());
Get("/author", x => GetArtistHistory());
Post("/failed", x => MarkAsFailed());
}
@ -40,12 +40,12 @@ namespace Readarr.Api.V1.History
if (includeArtist)
{
resource.Artist = model.Author.ToResource();
resource.Author = model.Author.ToResource();
}
if (includeAlbum)
{
resource.Album = model.Book.ToResource();
resource.Book = model.Book.ToResource();
}
if (model.Author != null)
@ -59,8 +59,8 @@ namespace Readarr.Api.V1.History
private PagingResource<HistoryResource> GetHistory(PagingResource<HistoryResource> pagingResource)
{
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, NzbDrone.Core.History.History>("date", SortDirection.Descending);
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAlbum = Request.GetBooleanQueryParameter("includeAlbum");
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
var includeBook = Request.GetBooleanQueryParameter("includeBook");
var eventTypeFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "eventType");
var bookIdFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "bookId");
@ -84,7 +84,7 @@ namespace Readarr.Api.V1.History
pagingSpec.FilterExpressions.Add(h => h.DownloadId == downloadId);
}
return ApplyToPage(_historyService.Paged, pagingSpec, h => MapToResource(h, includeArtist, includeAlbum));
return ApplyToPage(_historyService.Paged, pagingSpec, h => MapToResource(h, includeAuthor, includeBook));
}
private List<HistoryResource> GetHistorySince()
@ -99,15 +99,15 @@ namespace Readarr.Api.V1.History
DateTime date = DateTime.Parse(queryDate.Value);
HistoryEventType? eventType = null;
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAlbum = Request.GetBooleanQueryParameter("includeAlbum");
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
var includeBook = Request.GetBooleanQueryParameter("includeBook");
if (queryEventType.HasValue)
{
eventType = (HistoryEventType)Convert.ToInt32(queryEventType.Value);
}
return _historyService.Since(date, eventType).Select(h => MapToResource(h, includeArtist, includeAlbum)).ToList();
return _historyService.Since(date, eventType).Select(h => MapToResource(h, includeAuthor, includeBook)).ToList();
}
private List<HistoryResource> GetArtistHistory()
@ -123,8 +123,8 @@ namespace Readarr.Api.V1.History
int authorId = Convert.ToInt32(queryAuthorId.Value);
HistoryEventType? eventType = null;
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAlbum = Request.GetBooleanQueryParameter("includeAlbum");
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
var includeBook = Request.GetBooleanQueryParameter("includeBook");
if (queryEventType.HasValue)
{
@ -135,10 +135,10 @@ namespace Readarr.Api.V1.History
{
int bookId = Convert.ToInt32(queryBookId.Value);
return _historyService.GetByBook(bookId, eventType).Select(h => MapToResource(h, includeArtist, includeAlbum)).ToList();
return _historyService.GetByBook(bookId, eventType).Select(h => MapToResource(h, includeAuthor, includeBook)).ToList();
}
return _historyService.GetByAuthor(authorId, eventType).Select(h => MapToResource(h, includeArtist, includeAlbum)).ToList();
return _historyService.GetByAuthor(authorId, eventType).Select(h => MapToResource(h, includeAuthor, includeBook)).ToList();
}
private object MarkAsFailed()

@ -2,8 +2,8 @@ using System;
using System.Collections.Generic;
using NzbDrone.Core.History;
using NzbDrone.Core.Qualities;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http.REST;
namespace Readarr.Api.V1.History
@ -22,8 +22,8 @@ namespace Readarr.Api.V1.History
public Dictionary<string, string> Data { get; set; }
public AlbumResource Album { get; set; }
public ArtistResource Artist { get; set; }
public BookResource Book { get; set; }
public AuthorResource Author { get; set; }
}
public static class HistoryResourceMapper

@ -23,7 +23,7 @@ namespace Readarr.Api.V1.ImportLists
DeleteResource = DeleteImportListExclusionResource;
SharedValidator.RuleFor(c => c.ForeignId).NotEmpty().SetValidator(guidValidator).SetValidator(importListExclusionExistsValidator);
SharedValidator.RuleFor(c => c.ArtistName).NotEmpty();
SharedValidator.RuleFor(c => c.AuthorName).NotEmpty();
}
private ImportListExclusionResource GetImportListExclusion(int id)

@ -8,7 +8,7 @@ namespace Readarr.Api.V1.ImportLists
public class ImportListExclusionResource : RestResource
{
public string ForeignId { get; set; }
public string ArtistName { get; set; }
public string AuthorName { get; set; }
}
public static class ImportListExclusionResourceMapper
@ -24,7 +24,7 @@ namespace Readarr.Api.V1.ImportLists
{
Id = model.Id,
ForeignId = model.ForeignId,
ArtistName = model.Name,
AuthorName = model.Name,
};
}
@ -39,7 +39,7 @@ namespace Readarr.Api.V1.ImportLists
{
Id = resource.Id,
ForeignId = resource.ForeignId,
Name = resource.ArtistName
Name = resource.AuthorName
};
}

@ -24,7 +24,7 @@ namespace Readarr.Api.V1.Indexers
private readonly IDownloadService _downloadService;
private readonly Logger _logger;
private readonly ICached<RemoteBook> _remoteAlbumCache;
private readonly ICached<RemoteBook> _remoteBookCache;
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
ISearchForNzb nzbSearchService,
@ -47,12 +47,12 @@ namespace Readarr.Api.V1.Indexers
PostValidator.RuleFor(s => s.IndexerId).ValidId();
PostValidator.RuleFor(s => s.Guid).NotEmpty();
_remoteAlbumCache = cacheManager.GetCache<RemoteBook>(GetType(), "remoteAlbums");
_remoteBookCache = cacheManager.GetCache<RemoteBook>(GetType(), "remoteBooks");
}
private object DownloadRelease(ReleaseResource release)
{
var remoteAlbum = _remoteAlbumCache.Find(GetCacheKey(release));
var remoteAlbum = _remoteBookCache.Find(GetCacheKey(release));
if (remoteAlbum == null)
{
@ -78,46 +78,46 @@ namespace Readarr.Api.V1.Indexers
{
if (Request.Query.bookId.HasValue)
{
return GetAlbumReleases(Request.Query.bookId);
return GetBookReleases(Request.Query.bookId);
}
if (Request.Query.authorId.HasValue)
{
return GetArtistReleases(Request.Query.authorId);
return GetAuthorReleases(Request.Query.authorId);
}
return GetRss();
}
private List<ReleaseResource> GetAlbumReleases(int bookId)
private List<ReleaseResource> GetBookReleases(int bookId)
{
try
{
var decisions = _nzbSearchService.AlbumSearch(bookId, true, true, true);
var decisions = _nzbSearchService.BookSearch(bookId, true, true, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions);
}
catch (Exception ex)
{
_logger.Error(ex, "Album search failed");
_logger.Error(ex, "Book search failed");
}
return new List<ReleaseResource>();
}
private List<ReleaseResource> GetArtistReleases(int authorId)
private List<ReleaseResource> GetAuthorReleases(int authorId)
{
try
{
var decisions = _nzbSearchService.ArtistSearch(authorId, false, true, true);
var decisions = _nzbSearchService.AuthorSearch(authorId, false, true, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions);
}
catch (Exception ex)
{
_logger.Error(ex, "Artist search failed");
_logger.Error(ex, "Author search failed");
}
return new List<ReleaseResource>();
@ -135,7 +135,7 @@ namespace Readarr.Api.V1.Indexers
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
{
var resource = base.MapDecision(decision, initialWeight);
_remoteAlbumCache.Set(GetCacheKey(resource), decision.RemoteBook, TimeSpan.FromMinutes(30));
_remoteBookCache.Set(GetCacheKey(resource), decision.RemoteBook, TimeSpan.FromMinutes(30));
return resource;
}

@ -28,8 +28,8 @@ namespace Readarr.Api.V1.Indexers
public bool Discography { get; set; }
public bool SceneSource { get; set; }
public string AirDate { get; set; }
public string ArtistName { get; set; }
public string AlbumTitle { get; set; }
public string AuthorName { get; set; }
public string BookTitle { get; set; }
public bool Approved { get; set; }
public bool TemporarilyRejected { get; set; }
public bool Rejected { get; set; }
@ -65,15 +65,15 @@ namespace Readarr.Api.V1.Indexers
public static ReleaseResource ToResource(this DownloadDecision model)
{
var releaseInfo = model.RemoteBook.Release;
var parsedAlbumInfo = model.RemoteBook.ParsedBookInfo;
var remoteAlbum = model.RemoteBook;
var parsedBookInfo = model.RemoteBook.ParsedBookInfo;
var remoteBook = model.RemoteBook;
var torrentInfo = (model.RemoteBook.Release as TorrentInfo) ?? new TorrentInfo();
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?)
return new ReleaseResource
{
Guid = releaseInfo.Guid,
Quality = parsedAlbumInfo.Quality,
Quality = parsedBookInfo.Quality,
//QualityWeight
Age = releaseInfo.Age,
@ -82,12 +82,12 @@ namespace Readarr.Api.V1.Indexers
Size = releaseInfo.Size,
IndexerId = releaseInfo.IndexerId,
Indexer = releaseInfo.Indexer,
ReleaseGroup = parsedAlbumInfo.ReleaseGroup,
ReleaseHash = parsedAlbumInfo.ReleaseHash,
ReleaseGroup = parsedBookInfo.ReleaseGroup,
ReleaseHash = parsedBookInfo.ReleaseHash,
Title = releaseInfo.Title,
ArtistName = parsedAlbumInfo.AuthorName,
AlbumTitle = parsedAlbumInfo.BookTitle,
Discography = parsedAlbumInfo.Discography,
AuthorName = parsedBookInfo.AuthorName,
BookTitle = parsedBookInfo.BookTitle,
Discography = parsedBookInfo.Discography,
Approved = model.Approved,
TemporarilyRejected = model.TemporarilyRejected,
Rejected = model.Rejected,
@ -96,10 +96,10 @@ namespace Readarr.Api.V1.Indexers
CommentUrl = releaseInfo.CommentUrl,
DownloadUrl = releaseInfo.DownloadUrl,
InfoUrl = releaseInfo.InfoUrl,
DownloadAllowed = remoteAlbum.DownloadAllowed,
DownloadAllowed = remoteBook.DownloadAllowed,
//ReleaseWeight
PreferredWordScore = remoteAlbum.PreferredWordScore,
PreferredWordScore = remoteBook.PreferredWordScore,
MagnetUrl = torrentInfo.MagnetUrl,
InfoHash = torrentInfo.InfoHash,

@ -70,8 +70,8 @@ namespace Readarr.Api.V1.ManualImport
Path = resource.Path,
Name = resource.Name,
Size = resource.Size,
Author = resource.Artist == null ? null : _authorService.GetAuthor(resource.Artist.Id),
Book = resource.Album == null ? null : _bookService.GetBook(resource.Album.Id),
Author = resource.Author == null ? null : _authorService.GetAuthor(resource.Author.Id),
Book = resource.Book == null ? null : _bookService.GetBook(resource.Book.Id),
Quality = resource.Quality,
DownloadId = resource.DownloadId,
AdditionalFile = resource.AdditionalFile,

@ -4,8 +4,8 @@ using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles.BookImport.Manual;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http.REST;
namespace Readarr.Api.V1.ManualImport
@ -15,8 +15,8 @@ namespace Readarr.Api.V1.ManualImport
public string Path { get; set; }
public string Name { get; set; }
public long Size { get; set; }
public ArtistResource Artist { get; set; }
public AlbumResource Album { get; set; }
public AuthorResource Author { get; set; }
public BookResource Book { get; set; }
public QualityModel Quality { get; set; }
public int QualityWeight { get; set; }
public string DownloadId { get; set; }
@ -41,8 +41,8 @@ namespace Readarr.Api.V1.ManualImport
Path = model.Path,
Name = model.Name,
Size = model.Size,
Artist = model.Author.ToResource(),
Album = model.Book.ToResource(),
Author = model.Author.ToResource(),
Book = model.Book.ToResource(),
Quality = model.Quality,
//QualityWeight

@ -10,8 +10,8 @@ namespace Readarr.Api.V1.MediaCovers
{
public class MediaCoverModule : ReadarrV1Module
{
private const string MEDIA_COVER_ARTIST_ROUTE = @"/Artist/(?<authorId>\d+)/(?<filename>(.+)\.(jpg|png|gif))";
private const string MEDIA_COVER_ALBUM_ROUTE = @"/Album/(?<authorId>\d+)/(?<filename>(.+)\.(jpg|png|gif))";
private const string MEDIA_COVER_AUTHOR_ROUTE = @"/Author/(?<authorId>\d+)/(?<filename>(.+)\.(jpg|png|gif))";
private const string MEDIA_COVER_BOOK_ROUTE = @"/Book/(?<authorId>\d+)/(?<filename>(.+)\.(jpg|png|gif))";
private static readonly Regex RegexResizedImage = new Regex(@"-\d+(?=\.(jpg|png|gif)$)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
@ -24,8 +24,8 @@ namespace Readarr.Api.V1.MediaCovers
_appFolderInfo = appFolderInfo;
_diskProvider = diskProvider;
Get(MEDIA_COVER_ARTIST_ROUTE, options => GetArtistMediaCover(options.authorId, options.filename));
Get(MEDIA_COVER_ALBUM_ROUTE, options => GetAlbumMediaCover(options.authorId, options.filename));
Get(MEDIA_COVER_AUTHOR_ROUTE, options => GetArtistMediaCover(options.authorId, options.filename));
Get(MEDIA_COVER_BOOK_ROUTE, options => GetAlbumMediaCover(options.authorId, options.filename));
}
private object GetArtistMediaCover(int authorId, string filename)
@ -50,7 +50,7 @@ namespace Readarr.Api.V1.MediaCovers
private object GetAlbumMediaCover(int bookId, string filename)
{
var filePath = Path.Combine(_appFolderInfo.GetAppDataPath(), "MediaCover", "Albums", bookId.ToString(), filename);
var filePath = Path.Combine(_appFolderInfo.GetAppDataPath(), "MediaCover", "Books", bookId.ToString(), filename);
if (!_diskProvider.FileExists(filePath) || _diskProvider.GetFileSize(filePath) == 0)
{

@ -1,6 +1,6 @@
using NzbDrone.Core.Parser;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http;
namespace Readarr.Api.V1.Parse
@ -33,9 +33,9 @@ namespace Readarr.Api.V1.Parse
return new ParseResource
{
Title = title,
ParsedAlbumInfo = remoteAlbum.ParsedBookInfo,
Artist = remoteAlbum.Author.ToResource(),
Albums = remoteAlbum.Books.ToResource()
ParsedBookInfo = remoteAlbum.ParsedBookInfo,
Author = remoteAlbum.Author.ToResource(),
Books = remoteAlbum.Books.ToResource()
};
}
else
@ -43,7 +43,7 @@ namespace Readarr.Api.V1.Parse
return new ParseResource
{
Title = title,
ParsedAlbumInfo = parsedAlbumInfo
ParsedBookInfo = parsedAlbumInfo
};
}
}

@ -1,7 +1,7 @@
using System.Collections.Generic;
using NzbDrone.Core.Parser.Model;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Parse
@ -9,8 +9,8 @@ namespace Readarr.Api.V1.Parse
public class ParseResource : RestResource
{
public string Title { get; set; }
public ParsedBookInfo ParsedAlbumInfo { get; set; }
public ArtistResource Artist { get; set; }
public List<AlbumResource> Albums { get; set; }
public ParsedBookInfo ParsedBookInfo { get; set; }
public AuthorResource Author { get; set; }
public List<BookResource> Books { get; set; }
}
}

@ -27,8 +27,8 @@ namespace Readarr.Api.V1.Queue
private List<QueueResource> GetQueue()
{
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAlbum = Request.GetBooleanQueryParameter("includeAlbum", true);
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
var includeBook = Request.GetBooleanQueryParameter("includeBook", true);
var queue = _queueService.GetQueue();
var pending = _pendingReleaseService.GetPendingQueue();
var fullQueue = queue.Concat(pending);
@ -38,7 +38,7 @@ namespace Readarr.Api.V1.Queue
if (authorIdQuery.HasValue)
{
return fullQueue.Where(q => q.Author?.Id == (int)authorIdQuery).ToResource(includeArtist, includeAlbum);
return fullQueue.Where(q => q.Author?.Id == (int)authorIdQuery).ToResource(includeAuthor, includeBook);
}
if (bookIdsQuery.HasValue)
@ -49,10 +49,10 @@ namespace Readarr.Api.V1.Queue
.Select(e => Convert.ToInt32(e))
.ToList();
return fullQueue.Where(q => q.Book != null && bookIds.Contains(q.Book.Id)).ToResource(includeArtist, includeAlbum);
return fullQueue.Where(q => q.Book != null && bookIds.Contains(q.Book.Id)).ToResource(includeAuthor, includeBook);
}
return fullQueue.ToResource(includeArtist, includeAlbum);
return fullQueue.ToResource(includeAuthor, includeBook);
}
public void Handle(QueueUpdatedEvent message)

@ -38,20 +38,20 @@ namespace Readarr.Api.V1.Queue
private PagingResource<QueueResource> GetQueue(PagingResource<QueueResource> pagingResource)
{
var pagingSpec = pagingResource.MapToPagingSpec<QueueResource, NzbDrone.Core.Queue.Queue>("timeleft", SortDirection.Ascending);
var includeUnknownArtistItems = Request.GetBooleanQueryParameter("includeUnknownArtistItems");
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAlbum = Request.GetBooleanQueryParameter("includeAlbum");
var includeUnknownAuthorItems = Request.GetBooleanQueryParameter("includeUnknownAuthorItems");
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
var includeBook = Request.GetBooleanQueryParameter("includeBook");
return ApplyToPage((spec) => GetQueue(spec, includeUnknownArtistItems), pagingSpec, (q) => MapToResource(q, includeArtist, includeAlbum));
return ApplyToPage((spec) => GetQueue(spec, includeUnknownAuthorItems), pagingSpec, (q) => MapToResource(q, includeAuthor, includeBook));
}
private PagingSpec<NzbDrone.Core.Queue.Queue> GetQueue(PagingSpec<NzbDrone.Core.Queue.Queue> pagingSpec, bool includeUnknownArtistItems)
private PagingSpec<NzbDrone.Core.Queue.Queue> GetQueue(PagingSpec<NzbDrone.Core.Queue.Queue> pagingSpec, bool includeUnknownAuthorItems)
{
var ascending = pagingSpec.SortDirection == SortDirection.Ascending;
var orderByFunc = GetOrderByFunc(pagingSpec);
var queue = _queueService.GetQueue();
var filteredQueue = includeUnknownArtistItems ? queue : queue.Where(q => q.Author != null);
var filteredQueue = includeUnknownAuthorItems ? queue : queue.Where(q => q.Author != null);
var pending = _pendingReleaseService.GetPendingQueue();
var fullQueue = filteredQueue.Concat(pending).ToList();
IOrderedEnumerable<NzbDrone.Core.Queue.Queue> ordered;
@ -122,11 +122,11 @@ namespace Readarr.Api.V1.Queue
return q => q.Author?.SortName;
case "title":
return q => q.Title;
case "album":
case "book":
return q => q.Book;
case "books.title":
case "book.title":
return q => q.Book?.Title;
case "album.releaseDate":
case "book.releaseDate":
return q => q.Book?.ReleaseDate;
case "quality":
return q => q.Quality;
@ -138,9 +138,9 @@ namespace Readarr.Api.V1.Queue
}
}
private QueueResource MapToResource(NzbDrone.Core.Queue.Queue queueItem, bool includeArtist, bool includeAlbum)
private QueueResource MapToResource(NzbDrone.Core.Queue.Queue queueItem, bool includeAuthor, bool includeBook)
{
return queueItem.ToResource(includeArtist, includeAlbum);
return queueItem.ToResource(includeAuthor, includeBook);
}
public void Handle(QueueUpdatedEvent message)

@ -5,8 +5,8 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Qualities;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Queue
@ -15,8 +15,8 @@ namespace Readarr.Api.V1.Queue
{
public int? AuthorId { get; set; }
public int? BookId { get; set; }
public ArtistResource Artist { get; set; }
public AlbumResource Album { get; set; }
public AuthorResource Author { get; set; }
public BookResource Book { get; set; }
public QualityModel Quality { get; set; }
public decimal Size { get; set; }
public string Title { get; set; }
@ -50,8 +50,8 @@ namespace Readarr.Api.V1.Queue
Id = model.Id,
AuthorId = model.Author?.Id,
BookId = model.Book?.Id,
Artist = includeArtist && model.Author != null ? model.Author.ToResource() : null,
Album = includeAlbum && model.Book != null ? model.Book.ToResource() : null,
Author = includeArtist && model.Author != null ? model.Author.ToResource() : null,
Book = includeAlbum && model.Book != null ? model.Book.ToResource() : null,
Quality = model.Quality,
Size = model.Size,
Title = model.Title,
@ -72,9 +72,9 @@ namespace Readarr.Api.V1.Queue
};
}
public static List<QueueResource> ToResource(this IEnumerable<NzbDrone.Core.Queue.Queue> models, bool includeArtist, bool includeAlbum)
public static List<QueueResource> ToResource(this IEnumerable<NzbDrone.Core.Queue.Queue> models, bool includeAuthor, bool includeBook)
{
return models.Select((m) => ToResource(m, includeArtist, includeAlbum)).ToList();
return models.Select((m) => ToResource(m, includeAuthor, includeBook)).ToList();
}
}
}

@ -4,8 +4,8 @@ using System.Linq;
using Nancy;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http;
namespace Readarr.Api.V1.Search
@ -37,26 +37,26 @@ namespace Readarr.Api.V1.Search
if (result is NzbDrone.Core.Books.Author)
{
var artist = (NzbDrone.Core.Books.Author)result;
resource.Artist = artist.ToResource();
resource.ForeignId = artist.ForeignAuthorId;
var author = (NzbDrone.Core.Books.Author)result;
resource.Author = author.ToResource();
resource.ForeignId = author.ForeignAuthorId;
var poster = artist.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
var poster = author.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.Artist.RemotePoster = poster.Url;
resource.Author.RemotePoster = poster.Url;
}
}
else if (result is NzbDrone.Core.Books.Book)
{
var album = (NzbDrone.Core.Books.Book)result;
resource.Album = album.ToResource();
resource.ForeignId = album.ForeignBookId;
var book = (NzbDrone.Core.Books.Book)result;
resource.Book = book.ToResource();
resource.ForeignId = book.ForeignBookId;
var cover = album.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
var cover = book.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
if (cover != null)
{
resource.Album.RemoteCover = cover.Url;
resource.Book.RemoteCover = cover.Url;
}
}
else

@ -1,5 +1,5 @@
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Artist;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Search
@ -8,7 +8,7 @@ namespace Readarr.Api.V1.Search
SearchResource : RestResource
{
public string ForeignId { get; set; }
public ArtistResource Artist { get; set; }
public AlbumResource Album { get; set; }
public AuthorResource Author { get; set; }
public BookResource Book { get; set; }
}
}

@ -5,29 +5,29 @@ using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaCover;
using NzbDrone.SignalR;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Books;
using Readarr.Http;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Wanted
{
public class CutoffModule : AlbumModuleWithSignalR
public class CutoffModule : BookModuleWithSignalR
{
private readonly IBookCutoffService _albumCutoffService;
private readonly IBookCutoffService _bookCutoffService;
public CutoffModule(IBookCutoffService albumCutoffService,
public CutoffModule(IBookCutoffService bookCutoffService,
IBookService bookService,
IAuthorStatisticsService artistStatisticsService,
IAuthorStatisticsService authorStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(bookService, artistStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster, "wanted/cutoff")
: base(bookService, authorStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster, "wanted/cutoff")
{
_albumCutoffService = albumCutoffService;
GetResourcePaged = GetCutoffUnmetAlbums;
_bookCutoffService = bookCutoffService;
GetResourcePaged = GetCutoffUnmetBooks;
}
private PagingResource<AlbumResource> GetCutoffUnmetAlbums(PagingResource<AlbumResource> pagingResource)
private PagingResource<BookResource> GetCutoffUnmetBooks(PagingResource<BookResource> pagingResource)
{
var pagingSpec = new PagingSpec<Book>
{
@ -37,7 +37,7 @@ namespace Readarr.Api.V1.Wanted
SortDirection = pagingResource.SortDirection
};
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
var filter = pagingResource.Filters.FirstOrDefault(f => f.Key == "monitored");
if (filter != null && filter.Value == "false")
@ -49,7 +49,7 @@ namespace Readarr.Api.V1.Wanted
pagingSpec.FilterExpressions.Add(v => v.Monitored == true && v.Author.Value.Monitored == true);
}
var resource = ApplyToPage(_albumCutoffService.BooksWhereCutoffUnmet, pagingSpec, v => MapToResource(v, includeArtist));
var resource = ApplyToPage(_bookCutoffService.BooksWhereCutoffUnmet, pagingSpec, v => MapToResource(v, includeAuthor));
return resource;
}

@ -5,25 +5,25 @@ using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaCover;
using NzbDrone.SignalR;
using Readarr.Api.V1.Albums;
using Readarr.Api.V1.Books;
using Readarr.Http;
using Readarr.Http.Extensions;
namespace Readarr.Api.V1.Wanted
{
public class MissingModule : AlbumModuleWithSignalR
public class MissingModule : BookModuleWithSignalR
{
public MissingModule(IBookService bookService,
IAuthorStatisticsService artistStatisticsService,
IAuthorStatisticsService authorStatisticsService,
IMapCoversToLocal coverMapper,
IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(bookService, artistStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster, "wanted/missing")
: base(bookService, authorStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster, "wanted/missing")
{
GetResourcePaged = GetMissingAlbums;
GetResourcePaged = GetMissingBooks;
}
private PagingResource<AlbumResource> GetMissingAlbums(PagingResource<AlbumResource> pagingResource)
private PagingResource<BookResource> GetMissingBooks(PagingResource<BookResource> pagingResource)
{
var pagingSpec = new PagingSpec<Book>
{
@ -33,7 +33,7 @@ namespace Readarr.Api.V1.Wanted
SortDirection = pagingResource.SortDirection
};
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
var includeAuthor = Request.GetBooleanQueryParameter("includeAuthor");
var monitoredFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "monitored");
if (monitoredFilter != null && monitoredFilter.Value == "false")
@ -45,7 +45,7 @@ namespace Readarr.Api.V1.Wanted
pagingSpec.FilterExpressions.Add(v => v.Monitored == true && v.Author.Value.Monitored == true);
}
var resource = ApplyToPage(_bookService.BooksWithoutFiles, pagingSpec, v => MapToResource(v, includeArtist));
var resource = ApplyToPage(_bookService.BooksWithoutFiles, pagingSpec, v => MapToResource(v, includeAuthor));
return resource;
}

Loading…
Cancel
Save