Method, Variable, Class Renames in Readarr.Api

This commit is contained in:
Qstick 2020-05-15 17:22:44 -04:00 committed by ta264
parent 8080d375d0
commit ee4e44b81a
91 changed files with 945 additions and 948 deletions

View file

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

View file

@ -80,7 +80,7 @@ public void Setup()
private void GivenUnmonitorDeletedTracks(bool enabled)
{
Mocker.GetMock<IConfigService>()
.SetupGet(v => v.AutoUnmonitorPreviouslyDownloadedTracks)
.SetupGet(v => v.AutoUnmonitorPreviouslyDownloadedBooks)
.Returns(enabled);
}

View file

@ -26,7 +26,7 @@ public void Setup()
.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 @@ public void should_only_include_monitored_albums()
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)));
}
}

View file

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

View file

@ -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;
}
}

View file

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

View file

@ -24,11 +24,11 @@ public interface IConfigService
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; }

View file

@ -33,7 +33,7 @@ public DeletedBookFileSpecification(IDiskProvider diskProvider,
public virtual Decision IsSatisfiedBy(RemoteBook subject, SearchCriteriaBase searchCriteria)
{
if (!_configService.AutoUnmonitorPreviouslyDownloadedTracks)
if (!_configService.AutoUnmonitorPreviouslyDownloadedBooks)
{
return Decision.Accept();
}

View file

@ -33,7 +33,7 @@ public bool IsImported(TrackedDownload trackedDownload, List<History.History> hi
return false;
}
return new[] { HistoryEventType.DownloadImported, HistoryEventType.TrackFileImported }.Contains(lastHistoryItem.EventType);
return new[] { HistoryEventType.DownloadImported, HistoryEventType.BookFileImported }.Contains(lastHistoryItem.EventType);
});
return allAlbumsImportedInHistory;

View file

@ -257,7 +257,7 @@ private static TrackedDownloadState GetStateFromHistory(NzbDrone.Core.History.Hi
}
// 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;
}

View file

@ -32,14 +32,14 @@ public enum HistoryEventType
{
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
}
}

View file

@ -90,7 +90,7 @@ public List<History> GetByBook(int bookId, HistoryEventType? eventType)
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 &&

View file

@ -215,7 +215,7 @@ public void Handle(TrackImportedEvent message)
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 @@ public void Handle(BookFileDeletedEvent message)
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 @@ public void Handle(BookFileRenamedEvent message)
var history = new History
{
EventType = HistoryEventType.TrackFileRenamed,
EventType = HistoryEventType.BookFileRenamed,
Date = DateTime.UtcNow,
Quality = message.BookFile.Quality,
SourceTitle = message.OriginalPath,
@ -329,7 +329,7 @@ public void Handle(BookFileRetaggedEvent message)
var history = new History
{
EventType = HistoryEventType.TrackFileRetagged,
EventType = HistoryEventType.BookFileRetagged,
Date = DateTime.UtcNow,
Quality = message.BookFile.Quality,
SourceTitle = path,

View file

@ -22,7 +22,7 @@ public AuthorSearchService(ISearchForNzb nzbSearchService,
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);

View file

@ -47,7 +47,7 @@ private void SearchForMissingBooks(List<Book> books, bool userInvokedSearch)
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 @@ public void Execute(BookSearchCommand message)
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);

View file

@ -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 @@ public NzbSearchService(IIndexerFactory indexerFactory,
_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);

View file

@ -15,13 +15,13 @@
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;

View file

@ -3,11 +3,11 @@
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;

View file

@ -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();
}
}
}

View file

@ -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");
}
}
}

View file

@ -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();
}
}
}

View file

@ -7,7 +7,7 @@
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
public class ArtistFixture : IntegrationTest
public class AuthorFixture : IntegrationTest
{
[Test]
[Order(0)]
@ -16,15 +16,15 @@ public void add_artist_with_tags_should_store_them()
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 @@ public void add_artist_without_profileid_should_return_badrequest()
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 @@ public void add_artist_without_path_should_return_badrequest()
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 @@ public void add_artist()
{
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 @@ public void get_all_artist()
[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 @@ public void get_artist_by_unknown_id_should_return_404()
{
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 @@ public void update_artist_profile_id()
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 @@ public void update_artist_monitored()
//{
// season.Monitored = true;
//});
var result = Artist.Put(artist);
var result = Author.Put(artist);
result.Monitored.Should().BeTrue();
@ -159,22 +159,22 @@ public void update_artist_monitored()
[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 @@ public void update_artist_tags()
[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");
}
}
}

View file

@ -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");
}
}
}

View file

@ -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
{

View file

@ -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");
}

View file

@ -4,31 +4,31 @@
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 @@ public void should_be_able_to_get_albums()
[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 @@ public void should_not_be_able_to_get_unmonitored_albums()
[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();

View file

@ -47,7 +47,7 @@ private bool BeValidRelease(ReleaseResource releaseResource)
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();

View file

@ -17,7 +17,7 @@ public void Setup()
RootFolders.Post(new RootFolderResource
{
Name = "TestLibrary",
Path = ArtistRootFolder,
Path = AuthorRootFolder,
DefaultMetadataProfileId = 1,
DefaultQualityProfileId = 1,
DefaultMonitorOption = MonitorTypes.All
@ -39,7 +39,7 @@ public void missing_should_be_empty()
[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 @@ public void missing_should_have_monitored_items()
[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 @@ public void missing_should_have_artist()
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 @@ public void cutoff_should_have_monitored_items()
[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 @@ public void missing_should_not_have_unmonitored_items()
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 @@ public void cutoff_should_not_have_unmonitored_items()
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 @@ public void missing_should_have_unmonitored_items()
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");

View file

@ -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);
}
}
}

View file

@ -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)

View file

@ -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);
}
}
}

View file

@ -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 @@ private RestRequest BuildGet(string route = "artist")
return request;
}
private RestRequest BuildOptions(string route = "artist")
private RestRequest BuildOptions(string route = "author")
{
var request = new RestRequest(route, Method.OPTIONS);

View file

@ -17,20 +17,20 @@ public void should_log_on_error()
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"));
}
}
}

View file

@ -9,7 +9,7 @@ public abstract class IntegrationTest : IntegrationTestBase
{
protected NzbDroneRunner _runner;
public override string ArtistRootFolder => GetTempDirectory("ArtistRootFolder");
public override string AuthorRootFolder => GetTempDirectory("AuthorRootFolder");
protected override string RootUrl => "http://localhost:8787/";

View file

@ -17,9 +17,9 @@
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 @@ public abstract class IntegrationTestBase
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 @@ public abstract class IntegrationTestBase
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 @@ public IntegrationTestBase()
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 @@ protected virtual void InitRestClients()
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 @@ protected virtual void InitRestClients()
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 @@ public static void WaitForCompletion(Func<bool> predicate, int timeout = 10000,
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);
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 = Artist.Post(artist);
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 @@ public ArtistResource EnsureArtist(string authorId, string goodreadsBookId, stri
if (changed)
{
Artist.Put(result);
Author.Put(result);
}
return result;
@ -297,22 +297,22 @@ public ArtistResource EnsureArtist(string authorId, string goodreadsBookId, stri
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 @@ public void EnsureTrackFile(ArtistResource artist, int bookId, Quality quality)
});
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);
}

View file

@ -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; }
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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
};
}
}
}

View file

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

View file

@ -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; }

View file

@ -7,52 +7,52 @@
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 @@ private object SaveAll()
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)
{

View file

@ -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; }

View file

@ -4,14 +4,14 @@
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 @@ public ArtistImportModule(IAddAuthorService addAuthorService)
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();
}
}
}

View file

@ -5,14 +5,14 @@
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 @@ private object Search()
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;

View file

@ -19,9 +19,9 @@
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 @@ public class ArtistModule : ReadarrRestModuleWithSignalR<ArtistResource, NzbDron
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 @@ public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
_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 @@ public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
.IsValidPath()
.SetValidator(rootFolderValidator)
.SetValidator(mappedNetworkDriveValidator)
.SetValidator(artistPathValidator)
.SetValidator(artistAncestorValidator)
.SetValidator(authorPathValidator)
.SetValidator(authorAncestorValidator)
.SetValidator(systemFolderValidator)
.When(s => !s.Path.IsNullOrWhiteSpace());
@ -90,26 +90,26 @@ public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
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 @@ private ArtistResource GetArtistResource(NzbDrone.Core.Books.Author 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 @@ private void DeleteArtist(int id)
_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 @@ private void LinkArtistStatistics(ArtistResource resource, AuthorStatistics arti
// 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 @@ public void Handle(BookImportedEvent message)
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)

View file

@ -7,30 +7,30 @@
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 @@ public class ArtistResource : RestResource
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 @@ public class ArtistResource : RestResource
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 @@ public static ArtistResource ToResource(this NzbDrone.Core.Books.Author model)
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 @@ public static NzbDrone.Core.Books.Author ToModel(this ArtistResource resource)
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 @@ public static NzbDrone.Core.Books.Author ToModel(this ArtistResource resource)
};
}
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();
}

View file

@ -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
};
}
}
}

View file

@ -2,7 +2,7 @@
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 @@ public class BlacklistResource : RestResource
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 @@ public static BlacklistResource MapToResource(this NzbDrone.Core.Blacklisting.Bl
Indexer = model.Indexer,
Message = model.Message,
Artist = model.Author.ToResource()
Author = model.Author.ToResource()
};
}
}

View file

@ -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; }
}
}

View file

@ -14,9 +14,9 @@
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 @@ public class TrackFileModule : ReadarrRestModuleWithSignalR<TrackFileResource, B
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 @@ public TrackFileModule(IBroadcastSignalRMessage signalRBroadcaster,
_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 @@ private List<TrackFileResource> GetTrackFiles()
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 @@ private List<TrackFileResource> GetTrackFiles()
.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();

View file

@ -7,9 +7,9 @@
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 @@ public class TrackFileResource : RestResource
public ParsedTrackInfo AudioTags { get; set; }
}
public static class TrackFileResourceMapper
public static class BookFileResourceMapper
{
private static int QualityWeight(QualityModel quality)
{
@ -39,14 +39,14 @@ private static int QualityWeight(QualityModel quality)
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 @@ public static TrackFileResource ToResource(this BookFile model)
};
}
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 @@ public static TrackFileResource ToResource(this BookFile model, NzbDrone.Core.Bo
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)
};
}
}

View file

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

View file

@ -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; }
}
}

View file

@ -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);
}
}
}

View file

@ -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; }
}
}

View file

@ -5,14 +5,14 @@
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 @@ private object Search()
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;

View file

@ -19,9 +19,9 @@
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 @@ public class AlbumModule : AlbumModuleWithSignalR,
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 @@ private List<AlbumResource> GetAlbums()
{
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 @@ private List<AlbumResource> GetAlbums()
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 @@ private void DeleteAlbum(int id)
_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 @@ private object SetAlbumsMonitored()
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 @@ public void Handle(BookGrabbedEvent message)
public void Handle(BookEditedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, MapToResource(message.Album, true));
BroadcastResourceChange(ModelAction.Updated, MapToResource(message.Book, true));
}
public void Handle(BookUpdatedEvent message)

View file

@ -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);
}
}
}
}

View file

@ -4,13 +4,13 @@
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 @@ public class AlbumResource : RestResource
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 @@ public class AlbumResource : RestResource
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 @@ public static AlbumResource ToResource(this Book model)
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 @@ public static Book ToModel(this AlbumResource resource)
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();
}

View file

@ -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 @@ public decimal PercentOfTracks
{
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
};
}

View file

@ -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; }

View file

@ -3,21 +3,21 @@
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 @@ private List<RenameTrackResource> GetTracks()
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();
}
}
}

View file

@ -2,37 +2,37 @@
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();
}

View file

@ -4,21 +4,21 @@
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)
{

View file

@ -2,7 +2,7 @@
using System.Linq;
using Readarr.Http.REST;
namespace Readarr.Api.V1.Albums
namespace Readarr.Api.V1.Books
{
public class TagDifference
{
@ -11,31 +11,31 @@ public class TagDifference
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 @@ public static RetagTrackResource ToResource(this NzbDrone.Core.MediaFiles.RetagB
};
}
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();
}

View file

@ -61,35 +61,35 @@ private object GetCalendarFeed()
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());

View file

@ -6,32 +6,32 @@
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 @@ private List<AlbumResource> GetCalendar()
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();
}

View file

@ -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 @@ public static MediaManagementConfigResource ToResource(IConfigService model)
{
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,

View file

@ -75,11 +75,11 @@ private object GetExamples(NamingConfigResource 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);

View file

@ -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

View file

@ -6,8 +6,8 @@
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 @@ public HistoryModule(IHistoryService historyService,
GetResourcePaged = GetHistory;
Get("/since", x => GetHistorySince());
Get("/artist", x => GetArtistHistory());
Get("/author", x => GetArtistHistory());
Post("/failed", x => MarkAsFailed());
}
@ -40,12 +40,12 @@ protected HistoryResource MapToResource(NzbDrone.Core.History.History model, boo
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 @@ protected HistoryResource MapToResource(NzbDrone.Core.History.History model, boo
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 @@ private PagingResource<HistoryResource> GetHistory(PagingResource<HistoryResourc
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 @@ private List<HistoryResource> GetHistorySince()
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 @@ private List<HistoryResource> GetArtistHistory()
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 @@ private List<HistoryResource> GetArtistHistory()
{
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()

View file

@ -2,8 +2,8 @@
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 @@ public class HistoryResource : RestResource
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

View file

@ -23,7 +23,7 @@ public ImportListExclusionModule(IImportListExclusionService importListExclusion
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)

View file

@ -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 @@ public static ImportListExclusionResource ToResource(this ImportListExclusion mo
{
Id = model.Id,
ForeignId = model.ForeignId,
ArtistName = model.Name,
AuthorName = model.Name,
};
}
@ -39,7 +39,7 @@ public static ImportListExclusion ToModel(this ImportListExclusionResource resou
{
Id = resource.Id,
ForeignId = resource.ForeignId,
Name = resource.ArtistName
Name = resource.AuthorName
};
}

View file

@ -24,7 +24,7 @@ public class ReleaseModule : ReleaseModuleBase
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 @@ public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
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 @@ private List<ReleaseResource> GetReleases()
{
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 @@ private List<ReleaseResource> GetRss()
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;
}

View file

@ -28,8 +28,8 @@ public class ReleaseResource : RestResource
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 @@ public static class ReleaseResourceMapper
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 @@ public static ReleaseResource ToResource(this DownloadDecision model)
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 @@ public static ReleaseResource ToResource(this DownloadDecision model)
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,

View file

@ -70,8 +70,8 @@ private List<ManualImportResource> UpdateImportItems(List<ManualImportResource>
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,

View file

@ -4,8 +4,8 @@
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 @@ public class ManualImportResource : RestResource
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 @@ public static ManualImportResource ToResource(this ManualImportItem model)
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

View file

@ -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 @@ public MediaCoverModule(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider
_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 @@ private object GetArtistMediaCover(int authorId, string filename)
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)
{

View file

@ -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 @@ private ParseResource 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 @@ private ParseResource Parse()
return new ParseResource
{
Title = title,
ParsedAlbumInfo = parsedAlbumInfo
ParsedBookInfo = parsedAlbumInfo
};
}
}

View file

@ -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; }
}
}

View file

@ -27,8 +27,8 @@ public QueueDetailsModule(IBroadcastSignalRMessage broadcastSignalRMessage, IQue
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 @@ private List<QueueResource> GetQueue()
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 @@ private List<QueueResource> GetQueue()
.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)

View file

@ -38,20 +38,20 @@ public QueueModule(IBroadcastSignalRMessage broadcastSignalRMessage,
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 @@ private PagingResource<QueueResource> GetQueue(PagingResource<QueueResource> pag
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 @@ private PagingResource<QueueResource> GetQueue(PagingResource<QueueResource> pag
}
}
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)

View file

@ -5,8 +5,8 @@
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 @@ public class QueueResource : RestResource
{
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 @@ public static QueueResource ToResource(this NzbDrone.Core.Queue.Queue model, boo
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 @@ public static QueueResource ToResource(this NzbDrone.Core.Queue.Queue model, boo
};
}
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();
}
}
}

View file

@ -4,8 +4,8 @@
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 @@ private static IEnumerable<SearchResource> MapToResource(IEnumerable<object> res
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

View file

@ -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 @@ public class
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; }
}
}

View file

@ -5,29 +5,29 @@
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 @@ private PagingResource<AlbumResource> GetCutoffUnmetAlbums(PagingResource<AlbumR
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 @@ private PagingResource<AlbumResource> GetCutoffUnmetAlbums(PagingResource<AlbumR
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;
}

View file

@ -5,25 +5,25 @@
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 @@ private PagingResource<AlbumResource> GetMissingAlbums(PagingResource<AlbumResou
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 @@ private PagingResource<AlbumResource> GetMissingAlbums(PagingResource<AlbumResou
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;
}