pull/3344/head
adechant 4 months ago
parent 9ebbc462f3
commit 5273123706

Binary file not shown.

@ -57,7 +57,7 @@ namespace NzbDrone.Core.Books
public Author AddAuthor(Author newAuthor, bool doRefresh)
{
_cache.Clear();
_authorRepository.Insert(newAuthor);
var insertedAuthor = _authorRepository.Insert(newAuthor);
_eventAggregator.PublishEvent(new AuthorAddedEvent(GetAuthor(newAuthor.Id), doRefresh));
return newAuthor;

@ -455,7 +455,7 @@ namespace NzbDrone.Core.Datastore
var sortKey = TableMapping.Mapper.GetSortKey(pagingSpec.SortKey);
var sortDirection = pagingSpec.SortDirection == SortDirection.Descending ? "DESC" : "ASC";
var pagingOffset = Math.Max(pagingSpec.Page - 1, 0) * pagingSpec.PageSize;
builder.OrderBy($"\"{sortKey}\" {sortDirection} LIMIT {pagingSpec.PageSize} OFFSET {pagingOffset}");
builder.OrderBy($"{sortKey} {sortDirection} LIMIT {pagingSpec.PageSize} OFFSET {pagingOffset}");
return queryFunc(builder).ToList();
}

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaCover;
@ -40,88 +41,183 @@ namespace Readarr.Api.V1.OPDS
[HttpGet]
public OPDSCatalogResource GetOPDSCatalog()
{
/*var metadataTask = Task.Run(() => _authorService.GetAllAuthors());
var books = _bookService.GetAllBooks();
var authors = metadataTask.GetAwaiter().GetResult().ToDictionary(x => x.AuthorMetadataId);
var catalog = OPDSResourceMapper.ToOPDSCatalogResource();
return catalog;
}
foreach (var book in books)
protected bool IsDigitsOnly(string str)
{
foreach (var c in str)
{
book.Author = authors[book.AuthorMetadataId];
}*/
var catalog = OPDSResourceMapper.ToOPDSCatalogResource();
if (c < '0' || c > '9')
{
return false;
}
}
//catalog.Publications = MapToResource(books, wanted);
return catalog;
return true;
}
// /opds/publications
[HttpGet("publications")]
public OPDSPublicationsResource GetOPDSPublications([FromQuery] int? page,
[FromQuery] int? itemsPerPage)
// /opds/publications/search
[HttpGet("publications/search")]
public OPDSPublicationsResource GetOPDSPublicationsForQuery([FromQuery] PagingRequestResource paging, [FromQuery] string query, [FromQuery] string title, [FromQuery] string author)
{
if (itemsPerPage == null)
var pagingResource = new PagingResource<OPDSPublicationResource>(paging);
var pagingSpec = new PagingSpec<Book>
{
itemsPerPage = 10;
Page = pagingResource.Page,
PageSize = pagingResource.PageSize,
SortKey = pagingResource.SortKey,
SortDirection = pagingResource.SortDirection
};
if (query.IsNotNullOrWhiteSpace())
{
query = query.ToLower();
pagingSpec.FilterExpressions.Add(v => v.Title.Contains(query) || v.Author.Value.CleanName.Contains(query));
}
else if (itemsPerPage < 10)
else if (title.IsNotNullOrWhiteSpace() && author.IsNotNullOrWhiteSpace())
{
itemsPerPage = 10;
title = title.ToLower();
author = author.ToLower();
pagingSpec.FilterExpressions.Add(v => v.Title.Contains(title) && v.Author.Value.CleanName.Contains(author));
}
if (page == null)
else if (title.IsNotNullOrWhiteSpace())
{
page = 1;
itemsPerPage = 100000;
title = title.ToLower();
pagingSpec.FilterExpressions.Add(v => v.Title.Contains(title));
}
else if (author.IsNotNullOrWhiteSpace())
{
author = author.ToLower();
pagingSpec.FilterExpressions.Add(v => v.Author.Value.CleanName.Contains(author));
}
else
{
throw new BadRequestException("No search term specified in query");
}
pagingSpec = _bookService.BooksWithFiles(pagingSpec);
var publications = OPDSResourceMapper.ToOPDSPublicationsResource(pagingSpec.Page, pagingSpec.PageSize, pagingSpec.TotalRecords);
publications.Publications = MapToResource(pagingSpec.Records);
return publications;
}
// /opds/publications
[HttpGet("publications")]
public OPDSPublicationsResource GetOPDSPublications([FromQuery] PagingRequestResource paging)
{
var pagingResource = new PagingResource<OPDSPublicationResource>(paging);
var pagingSpec = new PagingSpec<Book>
{
Page = (int)page,
PageSize = (int)itemsPerPage,
SortKey = "Id",
SortDirection = SortDirection.Default
Page = pagingResource.Page,
PageSize = pagingResource.PageSize,
SortKey = pagingResource.SortKey,
SortDirection = pagingResource.SortDirection
};
pagingSpec = _bookService.BooksWithFiles(pagingSpec);
var publications = OPDSResourceMapper.ToOPDSPublicationsResource((int)page, (int)itemsPerPage, pagingSpec.TotalRecords);
publications.Publications = MapToResource(pagingSpec.Records, true);
var publications = OPDSResourceMapper.ToOPDSPublicationsResource(pagingSpec.Page, pagingSpec.PageSize, pagingSpec.TotalRecords);
publications.Publications = MapToResource(pagingSpec.Records);
return publications;
}
// /opds/wanted
[HttpGet("wanted")]
public OPDSPublicationsResource GetOPDSWantedPublications([FromQuery] int? page,
[FromQuery] int? itemsPerPage)
// /opds/monitored
[HttpGet("monitored")]
public OPDSPublicationsResource GetOPDSMonitoredPublications([FromQuery] PagingRequestResource paging)
{
if (itemsPerPage == null)
{
itemsPerPage = 10;
}
else if (itemsPerPage < 10)
var pagingResource = new PagingResource<OPDSPublicationResource>(paging);
var pagingSpec = new PagingSpec<Book>
{
itemsPerPage = 10;
}
Page = pagingResource.Page,
PageSize = pagingResource.PageSize,
SortKey = pagingResource.SortKey,
SortDirection = pagingResource.SortDirection
};
pagingSpec.FilterExpressions.Add(v => v.Monitored == true && v.Author.Value.Monitored == true);
pagingSpec = _bookService.BooksWithoutFiles(pagingSpec);
if (page == null)
var publications = OPDSResourceMapper.ToOPDSPublicationsResource(pagingSpec.Page, pagingSpec.PageSize, pagingSpec.TotalRecords);
publications.Publications = MapToResource(pagingSpec.Records);
return publications;
}
// /opds/monitored
[HttpGet("unmonitored")]
public OPDSPublicationsResource GetOPDSUnmonitoredPublications([FromQuery] PagingRequestResource paging)
{
var pagingResource = new PagingResource<OPDSPublicationResource>(paging);
var pagingSpec = new PagingSpec<Book>
{
page = 1;
itemsPerPage = 100000;
}
Page = pagingResource.Page,
PageSize = pagingResource.PageSize,
SortKey = pagingResource.SortKey,
SortDirection = pagingResource.SortDirection
};
pagingSpec.FilterExpressions.Add(v => v.Monitored == false);
pagingSpec = _bookService.BooksWithoutFiles(pagingSpec);
var publications = OPDSResourceMapper.ToOPDSPublicationsResource(pagingSpec.Page, pagingSpec.PageSize, pagingSpec.TotalRecords);
publications.Publications = MapToResource(pagingSpec.Records);
return publications;
}
// /opds/publications/search
[HttpGet("unmonitored/search")]
public OPDSPublicationsResource GetOPDSUnmonitoredForQuery([FromQuery] PagingRequestResource paging, [FromQuery] string query, [FromQuery] string title, [FromQuery] string author)
{
var pagingResource = new PagingResource<OPDSPublicationResource>(paging);
var pagingSpec = new PagingSpec<Book>
{
Page = (int)page,
PageSize = (int)itemsPerPage,
SortKey = "Id",
SortDirection = SortDirection.Default
Page = pagingResource.Page,
PageSize = pagingResource.PageSize,
SortKey = pagingResource.SortKey,
SortDirection = pagingResource.SortDirection
};
pagingSpec.FilterExpressions.Add(v => v.Monitored == true);
if (query.IsNotNullOrWhiteSpace())
{
query = query.ToLower();
pagingSpec.FilterExpressions.Add(v => v.Title.Contains(query) || v.Author.Value.CleanName.Contains(query));
}
else if (title.IsNotNullOrWhiteSpace() && author.IsNotNullOrWhiteSpace())
{
title = title.ToLower();
author = author.ToLower();
pagingSpec.FilterExpressions.Add(v => v.Title.Contains(title) && v.Author.Value.CleanName.Contains(author));
}
else if (title.IsNotNullOrWhiteSpace())
{
title = title.ToLower();
pagingSpec.FilterExpressions.Add(v => v.Title.Contains(title));
}
else if (author.IsNotNullOrWhiteSpace())
{
author = author.ToLower();
if (IsDigitsOnly(author))
{
var authorId = int.Parse(author);
pagingSpec.FilterExpressions.Add(v => v.AuthorMetadataId == authorId);
}
else
{
pagingSpec.FilterExpressions.Add(v => v.Author.Value.CleanName.Contains(author));
}
}
else
{
throw new BadRequestException("No search term specified in query");
}
pagingSpec.FilterExpressions.Add(v => v.Monitored == false);
pagingSpec = _bookService.BooksWithoutFiles(pagingSpec);
var publications = OPDSResourceMapper.ToOPDSPublicationsResource((int)page, (int)itemsPerPage, pagingSpec.TotalRecords);
publications.Publications = MapToResource(pagingSpec.Records, true);
var publications = OPDSResourceMapper.ToOPDSPublicationsResource(pagingSpec.Page, pagingSpec.PageSize, pagingSpec.TotalRecords);
publications.Publications = MapToResource(pagingSpec.Records);
return publications;
}
@ -150,7 +246,7 @@ namespace Readarr.Api.V1.OPDS
return OPDSResourceMapper.ToOPDSPublicationResource(book, bookfiles, ebookEdition, images);
}
protected List<OPDSPublicationResource> MapToResource(List<Book> books, bool wanted)
protected List<OPDSPublicationResource> MapToResource(List<Book> books)
{
var publications = new List<OPDSPublicationResource>();
var metadataTask = Task.Run(() => _authorService.GetAllAuthors());
@ -161,9 +257,10 @@ namespace Readarr.Api.V1.OPDS
var bookfiles = _mediaFileService.GetFilesByBook(book.Id);
var selectedEdition = book.Editions?.Value.Where(x => x.Monitored).SingleOrDefault();
var ebookEdition = book.Editions?.Value.Where(x => x.IsEbook).FirstOrDefault();
var anyEdition = book.Editions?.Value.FirstOrDefault();
var covers = selectedEdition?.Images ?? new List<MediaCover>();
_coverMapper.ConvertToLocalUrls(book.Id, MediaCoverEntity.Book, covers);
var publication = OPDSResourceMapper.ToOPDSPublicationResource(book, bookfiles, ebookEdition, covers);
var publication = OPDSResourceMapper.ToOPDSPublicationResource(book, bookfiles, ebookEdition ?? selectedEdition ?? anyEdition, covers);
publications.Add(publication);
}

@ -6,6 +6,7 @@ using NzbDrone.Core.Books;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using Readarr.Api.V1.Books;
using Readarr.Http.REST;
namespace Readarr.Api.V1.OPDS
@ -31,6 +32,7 @@ namespace Readarr.Api.V1.OPDS
public class OPDSPublicationMetadataResource : IEmbeddedDocument
{
public int Id { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
public string @Type { get; set; }
public string Author { get; set; }
@ -41,7 +43,9 @@ namespace Readarr.Api.V1.OPDS
public List<string> Genres { get; set; }
public double Rating { get; set; }
public int Votes { get; set; }
public string ForeignAuthorId { get; set; }
public string ForeignBookId { get; set; }
public string ForeignEditionId { get; set; }
}
public class OPDSImageResource : IEmbeddedDocument
@ -76,6 +80,8 @@ namespace Readarr.Api.V1.OPDS
{
public static OPDSCatalogResource ToOPDSCatalogResource()
{
var links = new List<OPDSLinkResource>();
var self = new OPDSLinkResource
{
Href = "/opds",
@ -83,16 +89,23 @@ namespace Readarr.Api.V1.OPDS
Title = "Readarr OPDS Catalog",
Type = "application/opds+json"
};
var links = new List<OPDSLinkResource>();
links.Add(self);
var nav = new List<OPDSLinkResource>();
var search = new OPDSLinkResource
{
Href = "/opds/publications/search{?query,title,author}",
Rel = "self",
Title = "Readarr Publication Search",
Type = "application/opds+json"
};
nav.Add(search);
var pubs = new OPDSLinkResource
{
Href = "/opds/publications",
Rel = "self",
Title = "Readarr OPDS Available Publications",
Title = "Available Publications",
Type = "application/opds+json"
};
nav.Add(pubs);
@ -102,21 +115,38 @@ namespace Readarr.Api.V1.OPDS
Title = self.Title
};
var wanted = new OPDSLinkResource
var monitored = new OPDSLinkResource
{
Href = "/opds/wanted",
Href = "/opds/monitored",
Rel = "self",
Title = "Readarr OPDS Wanted Publications",
Title = "Monitored Publications",
Type = "application/opds+json"
};
nav.Add(wanted);
nav.Add(monitored);
var unmonitored = new OPDSLinkResource
{
Href = "/opds/unmonitored",
Rel = "self",
Title = "Unmonitored Publications",
Type = "application/opds+json"
};
nav.Add(unmonitored);
var searchUnmonitored = new OPDSLinkResource
{
Href = "/opds/unmonitored/search{?query,title,author}",
Rel = "self",
Title = "Readarr Unmonitored Search",
Type = "application/opds+json"
};
nav.Add(searchUnmonitored);
return new OPDSCatalogResource
{
Metadata = meta,
Links = links,
Navigation = nav,
Publications = new List<OPDSPublicationResource>()
};
}
@ -149,23 +179,26 @@ namespace Readarr.Api.V1.OPDS
};
}
public static OPDSPublicationMetadataResource ToOPDSPublicationMetadataResource(Book book)
public static OPDSPublicationMetadataResource ToOPDSPublicationMetadataResource(Book book, Edition edition)
{
var edition = book.Editions?.Value.Where(x => x.Monitored).SingleOrDefault();
var resource = book.ToResource();
return new OPDSPublicationMetadataResource
{
Id = book.Id,
Title = book.Title,
Title = resource.Title,
@Type = "http://schema.org/Book",
Author = book.Author.Value?.Metadata?.Value?.SortNameLastFirst ?? book.Author.Value.Name,
Identifier = edition.Isbn13,
Language = edition.Language,
AuthorId = book.AuthorId,
Identifier = edition == null ? "" : edition.Isbn13,
Language = edition == null ? "" : edition.Language,
Modified = book.ReleaseDate ?? DateTime.Now,
Description = edition.Overview,
Description = edition == null ? "" : edition.Overview,
Genres = book.Genres,
Votes = book.Ratings.Votes,
Rating = (double)book.Ratings.Value,
ForeignAuthorId = book.Author.Value?.ForeignAuthorId,
ForeignBookId = book.ForeignBookId,
ForeignEditionId = edition == null ? "" : edition.ForeignEditionId,
};
}
@ -226,7 +259,7 @@ namespace Readarr.Api.V1.OPDS
return new OPDSPublicationResource
{
Metadata = ToOPDSPublicationMetadataResource(book),
Metadata = ToOPDSPublicationMetadataResource(book, edition),
Links = linkResources,
Images = imageResources
};

Loading…
Cancel
Save