OPDS Support (#526)
* Added some basic OPDS implementation * Fixed an issue with feed href * More changes * Added library routes and moved user code to a method so we can hack in fixed code without authentication * Images now load on the OPDS reusing our existing Image infrastructure. * Added the ability to download and moved some download code to a dedicated service * Download is working, pagination is implemented. * Refactored libraries to use pagination * Laid foundation for OpenSearch implementation * Fixed up some serialization issues and some old code that wasn't referencing helper methods * Ensure chapters are sorted when we send them over OPDS * OpenSearch implemented * Removed any support for OPDS-PS due to lack of apps supporting it. * Don't distribute development.json nor stats directory on build. * Implemented In Progress feed as well. * Ability to enable OPDS for server. OPDS now accepts initial call as POST in case app uses username/password. * UI now properly renders state for OPDS enablement. Added Collections routes. * Fixed pagination startIndex on OPDS feeds when there is less than 1 page. * Chunky Reader now works. It only accepts UTF-8 encodings * More Chunky fixes * More chunky changes, such a fussy client. * Implemented the ability to have a custom api key assigned to a user and use that api key as your authentication token against OPDS routing. * Implemented the ability to reset your API Key * Fixed favicon not being sent back correctly * Fixed an issue where images wouldn't send on OPDS feed. * Implemented Page streaming and fixed a pagination bug * Hooked in the ability to save progress in Kavita when Page Streaming
This commit is contained in:
parent
2a63e5e9e2
commit
6069d93c38
50 changed files with 2409 additions and 116 deletions
12
API/DTOs/OPDS/Author.cs
Normal file
12
API/DTOs/OPDS/Author.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace API.DTOs.OPDS
|
||||
{
|
||||
public class Author
|
||||
{
|
||||
[XmlElement("name")]
|
||||
public string Name { get; set; }
|
||||
[XmlElement("uri")]
|
||||
public string Uri { get; set; }
|
||||
}
|
||||
}
|
||||
62
API/DTOs/OPDS/Feed.cs
Normal file
62
API/DTOs/OPDS/Feed.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace API.DTOs.OPDS
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[XmlRoot("feed", Namespace = "http://www.w3.org/2005/Atom")]
|
||||
public class Feed
|
||||
{
|
||||
[XmlElement("updated")]
|
||||
public string Updated { get; init; } = DateTime.UtcNow.ToString("s");
|
||||
|
||||
[XmlElement("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[XmlElement("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[XmlElement("icon")]
|
||||
public string Icon { get; set; } = "/favicon.ico";
|
||||
|
||||
[XmlElement("author")]
|
||||
public Author Author { get; set; } = new Author()
|
||||
{
|
||||
Name = "Kavita",
|
||||
Uri = "https://kavitareader.com"
|
||||
};
|
||||
|
||||
[XmlElement("totalResults", Namespace = "http://a9.com/-/spec/opensearch/1.1/")]
|
||||
public int? Total { get; set; } = null;
|
||||
|
||||
[XmlElement("itemsPerPage", Namespace = "http://a9.com/-/spec/opensearch/1.1/")]
|
||||
public int? ItemsPerPage { get; set; } = null;
|
||||
|
||||
[XmlElement("startIndex", Namespace = "http://a9.com/-/spec/opensearch/1.1/")]
|
||||
public int? StartIndex { get; set; } = null;
|
||||
|
||||
[XmlElement("link")]
|
||||
public List<FeedLink> Links { get; set; } = new List<FeedLink>() ;
|
||||
|
||||
[XmlElement("entry")]
|
||||
public List<FeedEntry> Entries { get; set; } = new List<FeedEntry>();
|
||||
|
||||
public bool ShouldSerializeTotal()
|
||||
{
|
||||
return Total.HasValue;
|
||||
}
|
||||
|
||||
public bool ShouldSerializeItemsPerPage()
|
||||
{
|
||||
return ItemsPerPage.HasValue;
|
||||
}
|
||||
|
||||
public bool ShouldSerializeStartIndex()
|
||||
{
|
||||
return StartIndex.HasValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
API/DTOs/OPDS/FeedEntry.cs
Normal file
51
API/DTOs/OPDS/FeedEntry.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace API.DTOs.OPDS
|
||||
{
|
||||
public class FeedEntry
|
||||
{
|
||||
[XmlElement("updated")]
|
||||
public string Updated { get; init; } = DateTime.UtcNow.ToString("s");
|
||||
|
||||
[XmlElement("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[XmlElement("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[XmlElement("summary")]
|
||||
public string Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Represents Size of the Entry
|
||||
/// Tag: , ElementName = "dcterms:extent"
|
||||
/// <example>2 MB</example>
|
||||
/// </summary>
|
||||
[XmlElement("extent", Namespace = "http://purl.org/dc/terms/")]
|
||||
public string Extent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Format of the file
|
||||
/// https://dublincore.org/specifications/dublin-core/dcmi-terms/
|
||||
/// </summary>
|
||||
[XmlElement("format", Namespace = "http://purl.org/dc/terms/format")]
|
||||
public string Format { get; set; }
|
||||
|
||||
[XmlElement("language", Namespace = "http://purl.org/dc/terms/")]
|
||||
public string Language { get; set; }
|
||||
|
||||
[XmlElement("content")]
|
||||
public FeedEntryContent Content { get; set; }
|
||||
|
||||
[XmlElement("link")]
|
||||
public List<FeedLink> Links = new List<FeedLink>();
|
||||
|
||||
// [XmlElement("author")]
|
||||
// public List<FeedAuthor> Authors = new List<FeedAuthor>();
|
||||
|
||||
// [XmlElement("category")]
|
||||
// public List<FeedCategory> Categories = new List<FeedCategory>();
|
||||
}
|
||||
}
|
||||
12
API/DTOs/OPDS/FeedEntryContent.cs
Normal file
12
API/DTOs/OPDS/FeedEntryContent.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace API.DTOs.OPDS
|
||||
{
|
||||
public class FeedEntryContent
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public string Type = "text";
|
||||
[XmlText]
|
||||
public string Text;
|
||||
}
|
||||
}
|
||||
33
API/DTOs/OPDS/FeedLink.cs
Normal file
33
API/DTOs/OPDS/FeedLink.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace API.DTOs.OPDS
|
||||
{
|
||||
public class FeedLink
|
||||
{
|
||||
/// <summary>
|
||||
/// Relation on the Link
|
||||
/// </summary>
|
||||
[XmlAttribute("rel")]
|
||||
public string Rel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should be any of the types here <see cref="FeedLinkType"/>
|
||||
/// </summary>
|
||||
[XmlAttribute("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[XmlAttribute("href")]
|
||||
public string Href { get; set; }
|
||||
|
||||
[XmlAttribute("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[XmlAttribute("count", Namespace = "http://vaemendis.net/opds-pse/ns")]
|
||||
public int TotalPages { get; set; } = 0;
|
||||
|
||||
public bool ShouldSerializeTotalPages()
|
||||
{
|
||||
return TotalPages > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
API/DTOs/OPDS/FeedLinkRelation.cs
Normal file
24
API/DTOs/OPDS/FeedLinkRelation.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
namespace API.DTOs.OPDS
|
||||
{
|
||||
public static class FeedLinkRelation
|
||||
{
|
||||
public const string Debug = "debug";
|
||||
public const string Search = "search";
|
||||
public const string Self = "self";
|
||||
public const string Start = "start";
|
||||
public const string Next = "next";
|
||||
public const string Prev = "prev";
|
||||
public const string Alternate = "alternate";
|
||||
public const string SubSection = "subsection";
|
||||
public const string Related = "related";
|
||||
public const string Image = "http://opds-spec.org/image";
|
||||
public const string Thumbnail = "http://opds-spec.org/image/thumbnail";
|
||||
/// <summary>
|
||||
/// This will allow for a download to occur
|
||||
/// </summary>
|
||||
public const string Acquisition = "http://opds-spec.org/acquisition/open-access";
|
||||
#pragma warning disable S1075
|
||||
public const string Stream = "http://vaemendis.net/opds-pse/stream";
|
||||
#pragma warning restore S1075
|
||||
}
|
||||
}
|
||||
11
API/DTOs/OPDS/FeedLinkType.cs
Normal file
11
API/DTOs/OPDS/FeedLinkType.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace API.DTOs.OPDS
|
||||
{
|
||||
public static class FeedLinkType
|
||||
{
|
||||
public const string Atom = "application/atom+xml";
|
||||
public const string AtomSearch = "application/opensearchdescription+xml";
|
||||
public const string AtomNavigation = "application/atom+xml;profile=opds-catalog;kind=navigation";
|
||||
public const string AtomAcquisition = "application/atom+xml;profile=opds-catalog;kind=acquisition";
|
||||
public const string Image = "image/jpeg";
|
||||
}
|
||||
}
|
||||
42
API/DTOs/OPDS/OpenSearchDescription.cs
Normal file
42
API/DTOs/OPDS/OpenSearchDescription.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace API.DTOs.OPDS
|
||||
{
|
||||
[XmlRoot("OpenSearchDescription", Namespace = "http://a9.com/-/spec/opensearch/1.1/")]
|
||||
public class OpenSearchDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a brief human-readable title that identifies this search engine.
|
||||
/// </summary>
|
||||
public string ShortName { get; set; }
|
||||
/// <summary>
|
||||
/// Contains an extended human-readable title that identifies this search engine.
|
||||
/// </summary>
|
||||
public string LongName { get; set; }
|
||||
/// <summary>
|
||||
/// Contains a human-readable text description of the search engine.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// https://github.com/dewitt/opensearch/blob/master/opensearch-1-1-draft-6.md#the-url-element
|
||||
/// </summary>
|
||||
public SearchLink Url { get; set; }
|
||||
/// <summary>
|
||||
/// Contains a set of words that are used as keywords to identify and categorize this search content.
|
||||
/// Tags must be a single word and are delimited by the space character (' ').
|
||||
/// </summary>
|
||||
public string Tags { get; set; }
|
||||
/// <summary>
|
||||
/// Contains a URL that identifies the location of an image that can be used in association with this search content.
|
||||
/// <example><Image height="64" width="64" type="image/png">http://example.com/websearch.png</Image></example>
|
||||
/// </summary>
|
||||
public string Image { get; set; }
|
||||
public string InputEncoding { get; set; } = "UTF-8";
|
||||
public string OutputEncoding { get; set; } = "UTF-8";
|
||||
/// <summary>
|
||||
/// Contains the human-readable name or identifier of the creator or maintainer of the description document.
|
||||
/// </summary>
|
||||
public string Developer { get; set; } = "kavitareader.com";
|
||||
|
||||
}
|
||||
}
|
||||
16
API/DTOs/OPDS/SearchLink.cs
Normal file
16
API/DTOs/OPDS/SearchLink.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System.Xml.Serialization;
|
||||
|
||||
namespace API.DTOs.OPDS
|
||||
{
|
||||
public class SearchLink
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[XmlAttribute("rel")]
|
||||
public string Rel { get; set; } = "results";
|
||||
|
||||
[XmlAttribute("template")]
|
||||
public string Template { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue