parent
0922982d3d
commit
702910f0e4
@ -0,0 +1,106 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using DryIoc.ImTools;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using Readarr.Http;
|
||||
|
||||
namespace Readarr.Api.V1.OPDS
|
||||
{
|
||||
[V1ApiController]
|
||||
public class OPDSController : Controller
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IAuthorService _authorService;
|
||||
private readonly IBookService _bookService;
|
||||
private readonly IEditionService _editionService;
|
||||
private readonly IContentTypeProvider _mimeTypeProvider;
|
||||
private readonly IMapCoversToLocal _coverMapper;
|
||||
|
||||
public OPDSController(IAuthorService authorService,
|
||||
IBookService bookService,
|
||||
IEditionService editionService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
IMediaFileService mediaFileService)
|
||||
{
|
||||
_authorService = authorService;
|
||||
_bookService = bookService;
|
||||
_editionService = editionService;
|
||||
_mimeTypeProvider = new FileExtensionContentTypeProvider();
|
||||
_coverMapper = coverMapper;
|
||||
_mediaFileService = mediaFileService;
|
||||
}
|
||||
|
||||
// /opds
|
||||
[HttpGet]
|
||||
public OPDSCatalogResource GetOPDSCatalog([FromQuery] bool availableBooks = true)
|
||||
{
|
||||
var metadataTask = Task.Run(() => _authorService.GetAllAuthors());
|
||||
var books = _bookService.GetAllBooks();
|
||||
var authors = metadataTask.GetAwaiter().GetResult().ToDictionary(x => x.AuthorMetadataId);
|
||||
|
||||
foreach (var book in books)
|
||||
{
|
||||
book.Author = authors[book.AuthorMetadataId];
|
||||
}
|
||||
|
||||
var catalog = OPDSResourceMapper.ToOPDSCatalogResource();
|
||||
catalog.Publications = MapToResource(books, false);
|
||||
return catalog;
|
||||
}
|
||||
|
||||
// /opds/publications
|
||||
[HttpGet("publications")]
|
||||
public OPDSPublicationsResource GetOPDSPublications()
|
||||
{
|
||||
var metadataTask = Task.Run(() => _authorService.GetAllAuthors());
|
||||
var books = _bookService.GetAllBooks();
|
||||
var authors = metadataTask.GetAwaiter().GetResult().ToDictionary(x => x.AuthorMetadataId);
|
||||
|
||||
foreach (var book in books)
|
||||
{
|
||||
book.Author = authors[book.AuthorMetadataId];
|
||||
}
|
||||
|
||||
var publications = OPDSResourceMapper.ToOPDSPublicationsResource();
|
||||
publications.Publications = MapToResource(books, false);
|
||||
return publications;
|
||||
}
|
||||
|
||||
// /opds/publications/{int:id}
|
||||
[HttpGet("publications/{id:int}")]
|
||||
public OPDSPublicationResource GetOPDSPublication(int id)
|
||||
{
|
||||
var images = new List<MediaCover>();
|
||||
var metadataTask = Task.Run(() => _authorService.GetAllAuthors());
|
||||
var book = _bookService.GetBook(id);
|
||||
var author = _authorService.GetAuthor(book.AuthorId);
|
||||
var bookfile = _mediaFileService.GetFilesByBook(book.Id);
|
||||
_coverMapper.ConvertToLocalUrls(book.Id, MediaCoverEntity.Book, images);
|
||||
book.Author = author;
|
||||
|
||||
return OPDSResourceMapper.ToOPDSPublicationResource(book, bookfile, images);
|
||||
}
|
||||
|
||||
protected List<OPDSPublicationResource> MapToResource(List<Book> books, bool availableBooks)
|
||||
{
|
||||
var pubclications = new List<OPDSPublicationResource>();
|
||||
for (var i = 0; i < books.Count; i++)
|
||||
{
|
||||
var images = new List<MediaCover>();
|
||||
var book = books[i];
|
||||
var bookfile = _mediaFileService.GetFilesByBook(book.Id);
|
||||
var edition = _editionService.GetEdition(book.Id);
|
||||
_coverMapper.ConvertToLocalUrls(book.Id, MediaCoverEntity.Book, images);
|
||||
var publication = OPDSResourceMapper.ToOPDSPublicationResource(book, bookfile, images);
|
||||
pubclications.Add(publication);
|
||||
}
|
||||
|
||||
return pubclications;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Books;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using Readarr.Http.REST;
|
||||
|
||||
namespace Readarr.Api.V1.OPDS
|
||||
{
|
||||
public class OPDSCatalogMetadataResource : IEmbeddedDocument
|
||||
{
|
||||
public string Title { get; set; }
|
||||
}
|
||||
|
||||
public class OPDSLinkResource : IEmbeddedDocument
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Rel { get; set; }
|
||||
public string Href { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
|
||||
public class OPDSPublicationMetadataResource : IEmbeddedDocument
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string @Type { get; set; }
|
||||
public string Author { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public string Language { get; set; }
|
||||
public DateTime Modified { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public class OPDSImageResource : IEmbeddedDocument
|
||||
{
|
||||
public string Href { get; set; }
|
||||
public string Type { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int Width { get; set; }
|
||||
}
|
||||
|
||||
public class OPDSPublicationResource : IEmbeddedDocument
|
||||
{
|
||||
public OPDSPublicationMetadataResource Metadata { get; set; }
|
||||
public List<OPDSLinkResource> Links { get; set; }
|
||||
public List<OPDSImageResource> Images { get; set; }
|
||||
}
|
||||
|
||||
public class OPDSCatalogResource : RestResource
|
||||
{
|
||||
public OPDSCatalogMetadataResource Metadata { get; set; }
|
||||
public List<OPDSLinkResource> Links { get; set; }
|
||||
public List<OPDSLinkResource> Navigation { get; set; }
|
||||
public List<OPDSPublicationResource> Publications { get; set; }
|
||||
}
|
||||
|
||||
public class OPDSPublicationsResource : RestResource
|
||||
{
|
||||
public OPDSCatalogMetadataResource Metadata { get; set; }
|
||||
public List<OPDSLinkResource> Links { get; set; }
|
||||
public List<OPDSPublicationResource> Publications { get; set; }
|
||||
}
|
||||
|
||||
public static class OPDSResourceMapper
|
||||
{
|
||||
public static OPDSCatalogResource ToOPDSCatalogResource()
|
||||
{
|
||||
var self = new OPDSLinkResource
|
||||
{
|
||||
Href = "/opds",
|
||||
Rel = "self",
|
||||
Title = "Readarr OPDS Catalog",
|
||||
Type = "application/opds+json"
|
||||
};
|
||||
|
||||
var links = new List<OPDSLinkResource>();
|
||||
links.Add(self);
|
||||
|
||||
var meta = new OPDSCatalogMetadataResource
|
||||
{
|
||||
Title = self.Title
|
||||
};
|
||||
|
||||
return new OPDSCatalogResource
|
||||
{
|
||||
Metadata = meta,
|
||||
Links = links,
|
||||
Navigation = new List<OPDSLinkResource>(),
|
||||
Publications = new List<OPDSPublicationResource>()
|
||||
};
|
||||
}
|
||||
|
||||
public static OPDSPublicationsResource ToOPDSPublicationsResource()
|
||||
{
|
||||
var self = new OPDSLinkResource
|
||||
{
|
||||
Href = "/opds/publications",
|
||||
Rel = "self",
|
||||
Title = "Readarr OPDS Publications",
|
||||
Type = "application/opds+json"
|
||||
};
|
||||
|
||||
var links = new List<OPDSLinkResource>();
|
||||
links.Add(self);
|
||||
|
||||
var meta = new OPDSCatalogMetadataResource
|
||||
{
|
||||
Title = self.Title
|
||||
};
|
||||
|
||||
return new OPDSPublicationsResource
|
||||
{
|
||||
Metadata = meta,
|
||||
Links = links,
|
||||
Publications = new List<OPDSPublicationResource>()
|
||||
};
|
||||
}
|
||||
|
||||
public static OPDSPublicationMetadataResource ToOPDSPublicationMetadataResource(Book book)
|
||||
{
|
||||
var edition = book.Editions?.Value.Where(x => x.Monitored).SingleOrDefault();
|
||||
return new OPDSPublicationMetadataResource
|
||||
{
|
||||
Title = book.Title,
|
||||
@Type = "http://schema.org/Book",
|
||||
Author = book.Author.Value.CleanName,
|
||||
Identifier = edition.Isbn13,
|
||||
Language = edition.Language,
|
||||
Modified = book.ReleaseDate ?? DateTime.Now,
|
||||
Description = edition.Overview
|
||||
};
|
||||
}
|
||||
|
||||
public static OPDSImageResource ToOPDSImageResource(MediaCover image)
|
||||
{
|
||||
return new OPDSImageResource
|
||||
{
|
||||
Href = image.Url,
|
||||
Type = GetImageContentType(image.Extension)
|
||||
};
|
||||
}
|
||||
|
||||
public static OPDSPublicationResource ToOPDSPublicationResource(Book book, List<BookFile> files, List<MediaCover> images)
|
||||
{
|
||||
var linkResources = new List<OPDSLinkResource>();
|
||||
var imageResources = new List<OPDSImageResource>();
|
||||
|
||||
//Must have link to self
|
||||
linkResources.Add(new OPDSLinkResource
|
||||
{
|
||||
Href = string.Format("/opds/publications/{0}", book.Id),
|
||||
Rel = "self",
|
||||
Title = book.Title,
|
||||
Type = "application/opds-publication+json"
|
||||
});
|
||||
|
||||
//we'll only add the first bookfile (for now)
|
||||
foreach (var file in files)
|
||||
{
|
||||
linkResources.Add(new OPDSLinkResource
|
||||
{
|
||||
Href = string.Format("/bookfile/download/{0}", book.Id),
|
||||
Rel = "http://opds-spec.org/acquisition",
|
||||
Title = "Readarr OPDS Catalog",
|
||||
Type = GetContentType(file.Path)
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var image in images)
|
||||
{
|
||||
var imageResource = ToOPDSImageResource(image);
|
||||
imageResources.Add(imageResource);
|
||||
}
|
||||
|
||||
return new OPDSPublicationResource
|
||||
{
|
||||
Metadata = ToOPDSPublicationMetadataResource(book),
|
||||
Links = linkResources,
|
||||
Images = imageResources
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetContentType(string filePath)
|
||||
{
|
||||
var contentType = "application/octet-stream";
|
||||
var ext = PathExtensions.GetPathExtension(filePath);
|
||||
if (ext.Contains("epub"))
|
||||
{
|
||||
contentType = "application/epub+zip";
|
||||
}
|
||||
else if (ext.Contains("azw"))
|
||||
{
|
||||
contentType = "application/vnd.amazon.ebook";
|
||||
}
|
||||
else if (ext.Contains("azw"))
|
||||
{
|
||||
contentType = "application/x-mobipocket-ebook";
|
||||
}
|
||||
else if (ext.Contains("pdf"))
|
||||
{
|
||||
contentType = "application/pdf";
|
||||
}
|
||||
|
||||
return contentType;
|
||||
}
|
||||
|
||||
private static string GetImageContentType(string ext)
|
||||
{
|
||||
var contentType = string.Format("application/{0}", ext);
|
||||
if (ext.Contains("jpg") || ext.Contains("jepg"))
|
||||
{
|
||||
contentType = "image/jpeg";
|
||||
}
|
||||
else if (ext.Contains("png"))
|
||||
{
|
||||
contentType = "image/png";
|
||||
}
|
||||
else if (ext.Contains("gif"))
|
||||
{
|
||||
contentType = "image/gif";
|
||||
}
|
||||
else if (ext.Contains("svg"))
|
||||
{
|
||||
contentType = "image/svg+xml";
|
||||
}
|
||||
|
||||
return contentType;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue