feat(indexer): add book/audiobook search criteria

Add search criteria classes and update request generators:
- BookSearchCriteria (Author, Title, ISBN, Publisher, Year)
- AudiobookSearchCriteria (Author, Title, Narrator, ASIN, ISBN)
- Updated IIndexerRequestGenerator interface
- Implemented book/audiobook search in NewznabRequestGenerator
- Added stub implementations to all other request generators
This commit is contained in:
admin 2025-12-18 13:25:20 -06:00
parent 86faa9aef7
commit ef296bc95e
12 changed files with 202 additions and 0 deletions

View file

@ -0,0 +1,34 @@
namespace NzbDrone.Core.IndexerSearch.Definitions
{
public class AudiobookSearchCriteria : SearchCriteriaBase
{
public string Author { get; set; }
public string Title { get; set; }
public string Narrator { get; set; }
public string ASIN { get; set; }
public string ISBN { get; set; }
public int? Year { get; set; }
public bool? Abridged { get; set; }
public override string ToString()
{
if (!string.IsNullOrWhiteSpace(Author) && !string.IsNullOrWhiteSpace(Title))
{
var result = $"[{Author} - {Title}";
if (!string.IsNullOrWhiteSpace(Narrator))
{
result += $" (narrated by {Narrator})";
}
return result + "]";
}
if (!string.IsNullOrWhiteSpace(ASIN))
{
return $"[ASIN: {ASIN}]";
}
return $"[{Title ?? Author ?? "Unknown"}]";
}
}
}

View file

@ -0,0 +1,26 @@
namespace NzbDrone.Core.IndexerSearch.Definitions
{
public class BookSearchCriteria : SearchCriteriaBase
{
public string Author { get; set; }
public string Title { get; set; }
public string ISBN { get; set; }
public string Publisher { get; set; }
public int? Year { get; set; }
public override string ToString()
{
if (!string.IsNullOrWhiteSpace(Author) && !string.IsNullOrWhiteSpace(Title))
{
return $"[{Author} - {Title}]";
}
if (!string.IsNullOrWhiteSpace(ISBN))
{
return $"[ISBN: {ISBN}]";
}
return $"[{Title ?? Author ?? "Unknown"}]";
}
}
}

View file

@ -40,6 +40,16 @@ public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
private IEnumerable<IndexerRequest> GetRequest(string searchType, string parameters)
{
if (Settings.Categories.Empty())

View file

@ -34,6 +34,16 @@ public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
private bool TryAddSearchParameters(TorrentQuery query, SearchCriteriaBase searchCriteria)
{
if (searchCriteria.Movie.MovieMetadata.Value.ImdbId.IsNullOrWhiteSpace())

View file

@ -8,6 +8,8 @@ public interface IIndexerRequestGenerator
{
IndexerPageableRequestChain GetRecentRequests();
IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria);
IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria);
IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria);
Func<IDictionary<string, string>> GetCookies { get; set; }
Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

View file

@ -23,6 +23,16 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
private IEnumerable<IndexerRequest> GetRssRequests()
{
yield return new IndexerRequest(Settings.BaseUrl, HttpAccept.Rss);

View file

@ -113,6 +113,66 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
var categories = Settings.Categories.Any()
? Settings.Categories
: new[] { NewznabStandardCategory.Books, NewznabStandardCategory.BooksEBook };
if (!string.IsNullOrWhiteSpace(searchCriteria.ISBN))
{
pageableRequests.Add(GetPagedRequests(MaxPages, categories, "book", $"&q={Uri.EscapeDataString(searchCriteria.ISBN)}"));
}
else if (!string.IsNullOrWhiteSpace(searchCriteria.Author) && !string.IsNullOrWhiteSpace(searchCriteria.Title))
{
var query = $"{searchCriteria.Author} {searchCriteria.Title}";
if (searchCriteria.Year.HasValue)
{
query += $" {searchCriteria.Year}";
}
pageableRequests.Add(GetPagedRequests(MaxPages, categories, "book", $"&q={Uri.EscapeDataString(query)}"));
}
else if (!string.IsNullOrWhiteSpace(searchCriteria.Title))
{
pageableRequests.Add(GetPagedRequests(MaxPages, categories, "book", $"&q={Uri.EscapeDataString(searchCriteria.Title)}"));
}
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
var categories = Settings.Categories.Any()
? Settings.Categories
: new[] { NewznabStandardCategory.AudioAudiobook, NewznabStandardCategory.Audio };
if (!string.IsNullOrWhiteSpace(searchCriteria.ASIN))
{
pageableRequests.Add(GetPagedRequests(MaxPages, categories, "search", $"&q={Uri.EscapeDataString(searchCriteria.ASIN)}"));
}
else if (!string.IsNullOrWhiteSpace(searchCriteria.Author) && !string.IsNullOrWhiteSpace(searchCriteria.Title))
{
var query = $"{searchCriteria.Author} {searchCriteria.Title}";
if (!string.IsNullOrWhiteSpace(searchCriteria.Narrator))
{
query += $" {searchCriteria.Narrator}";
}
pageableRequests.Add(GetPagedRequests(MaxPages, categories, "search", $"&q={Uri.EscapeDataString(query)}"));
}
else if (!string.IsNullOrWhiteSpace(searchCriteria.Title))
{
pageableRequests.Add(GetPagedRequests(MaxPages, categories, "search", $"&q={Uri.EscapeDataString(searchCriteria.Title)}"));
}
return pageableRequests;
}
private void AddMovieIdPageableRequests(IndexerPageableRequestChain chain, int maxPages, IEnumerable<int> categories, SearchCriteriaBase searchCriteria)
{
var includeTmdbSearch = SupportsTmdbSearch && searchCriteria.Movie.MovieMetadata.Value.TmdbId > 0;

View file

@ -30,6 +30,16 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
private IEnumerable<IndexerRequest> GetPagedRequests(string term)
{
var baseUrl = $"{Settings.BaseUrl.TrimEnd('/')}/?page=rss{Settings.AdditionalParameters}";

View file

@ -43,6 +43,16 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
{
var request =

View file

@ -28,6 +28,16 @@ public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public virtual IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

View file

@ -74,6 +74,16 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

View file

@ -24,6 +24,16 @@ public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchC
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
public IndexerPageableRequestChain GetSearchRequests(AudiobookSearchCriteria searchCriteria)
{
return new IndexerPageableRequestChain();
}
private IEnumerable<IndexerRequest> GetRssRequests(string searchParameters)
{
var request = new IndexerRequest(Settings.BaseUrl.Trim().TrimEnd('/'), HttpAccept.Rss);