2023-10-05 23:56:19 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
log "github.com/sirupsen/logrus"
|
2024-01-11 01:23:36 +00:00
|
|
|
"reichard.io/antholume/database"
|
|
|
|
"reichard.io/antholume/opds"
|
2023-10-05 23:56:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var mimeMapping map[string]string = map[string]string{
|
|
|
|
"epub": "application/epub+zip",
|
|
|
|
"azw": "application/vnd.amazon.mobi8-ebook",
|
|
|
|
"mobi": "application/x-mobipocket-ebook",
|
|
|
|
"pdf": "application/pdf",
|
|
|
|
"zip": "application/zip",
|
|
|
|
"txt": "text/plain",
|
|
|
|
"rtf": "application/rtf",
|
|
|
|
"htm": "text/html",
|
|
|
|
"html": "text/html",
|
|
|
|
"doc": "application/msword",
|
|
|
|
"lit": "application/x-ms-reader",
|
|
|
|
}
|
|
|
|
|
2023-11-25 23:38:18 +00:00
|
|
|
func (api *API) opdsEntry(c *gin.Context) {
|
|
|
|
// Build & Return XML
|
|
|
|
mainFeed := &opds.Feed{
|
|
|
|
Title: "AnthoLume OPDS Server",
|
|
|
|
Updated: time.Now().UTC(),
|
|
|
|
Links: []opds.Link{
|
|
|
|
{
|
|
|
|
Title: "Search AnthoLume",
|
|
|
|
Rel: "search",
|
|
|
|
TypeLink: "application/opensearchdescription+xml",
|
|
|
|
Href: "/api/opds/search.xml",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
Entries: []opds.Entry{
|
|
|
|
{
|
|
|
|
Title: "AnthoLume - All Documents",
|
|
|
|
Content: &opds.Content{
|
|
|
|
Content: "AnthoLume - All Documents",
|
|
|
|
ContentType: "text",
|
|
|
|
},
|
|
|
|
Links: []opds.Link{
|
|
|
|
{
|
2023-11-27 02:41:17 +00:00
|
|
|
Href: "/api/opds/documents",
|
2023-11-25 23:38:18 +00:00
|
|
|
TypeLink: "application/atom+xml;type=feed;profile=opds-catalog",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
c.XML(http.StatusOK, mainFeed)
|
|
|
|
}
|
|
|
|
|
2023-10-05 23:56:19 +00:00
|
|
|
func (api *API) opdsDocuments(c *gin.Context) {
|
2024-01-10 02:08:40 +00:00
|
|
|
var auth authData
|
|
|
|
if data, _ := c.Get("Authorization"); data != nil {
|
|
|
|
auth = data.(authData)
|
2023-10-05 23:56:19 +00:00
|
|
|
}
|
|
|
|
|
2023-11-27 02:41:17 +00:00
|
|
|
// Potential URL Parameters (Default Pagination - 100)
|
|
|
|
qParams := bindQueryParams(c, 100)
|
2023-10-05 23:56:19 +00:00
|
|
|
|
2023-11-25 23:38:18 +00:00
|
|
|
// Possible Query
|
|
|
|
var query *string
|
|
|
|
if qParams.Search != nil && *qParams.Search != "" {
|
|
|
|
search := "%" + *qParams.Search + "%"
|
|
|
|
query = &search
|
|
|
|
}
|
|
|
|
|
2023-10-05 23:56:19 +00:00
|
|
|
// Get Documents
|
|
|
|
documents, err := api.DB.Queries.GetDocumentsWithStats(api.DB.Ctx, database.GetDocumentsWithStatsParams{
|
2024-01-10 02:08:40 +00:00
|
|
|
UserID: auth.UserName,
|
2023-11-25 23:38:18 +00:00
|
|
|
Query: query,
|
2023-10-05 23:56:19 +00:00
|
|
|
Offset: (*qParams.Page - 1) * *qParams.Limit,
|
|
|
|
Limit: *qParams.Limit,
|
|
|
|
})
|
|
|
|
if err != nil {
|
2024-01-27 01:45:07 +00:00
|
|
|
log.Error("GetDocumentsWithStats DB Error:", err)
|
2023-10-05 23:56:19 +00:00
|
|
|
c.AbortWithStatus(http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build OPDS Entries
|
|
|
|
var allEntries []opds.Entry
|
|
|
|
for _, doc := range documents {
|
|
|
|
// Require File
|
|
|
|
if doc.Filepath != nil {
|
|
|
|
splitFilepath := strings.Split(*doc.Filepath, ".")
|
|
|
|
fileType := splitFilepath[len(splitFilepath)-1]
|
|
|
|
|
2023-11-06 02:20:15 +00:00
|
|
|
title := "N/A"
|
|
|
|
if doc.Title != nil {
|
|
|
|
title = *doc.Title
|
|
|
|
}
|
|
|
|
|
|
|
|
author := "N/A"
|
|
|
|
if doc.Author != nil {
|
|
|
|
author = *doc.Author
|
|
|
|
}
|
|
|
|
|
|
|
|
description := "N/A"
|
|
|
|
if doc.Description != nil {
|
|
|
|
description = *doc.Description
|
|
|
|
}
|
|
|
|
|
2023-10-05 23:56:19 +00:00
|
|
|
item := opds.Entry{
|
2023-11-25 23:38:18 +00:00
|
|
|
Title: title,
|
2023-10-05 23:56:19 +00:00
|
|
|
Author: []opds.Author{
|
|
|
|
{
|
2023-11-06 02:20:15 +00:00
|
|
|
Name: author,
|
2023-10-05 23:56:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Content: &opds.Content{
|
2023-11-06 02:20:15 +00:00
|
|
|
Content: description,
|
2023-10-05 23:56:19 +00:00
|
|
|
ContentType: "text",
|
|
|
|
},
|
|
|
|
Links: []opds.Link{
|
|
|
|
{
|
|
|
|
Rel: "http://opds-spec.org/acquisition",
|
2023-11-25 23:38:18 +00:00
|
|
|
Href: fmt.Sprintf("/api/opds/documents/%s/file", doc.ID),
|
2023-10-05 23:56:19 +00:00
|
|
|
TypeLink: mimeMapping[fileType],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Rel: "http://opds-spec.org/image",
|
2023-11-25 23:38:18 +00:00
|
|
|
Href: fmt.Sprintf("/api/opds/documents/%s/cover", doc.ID),
|
2023-10-05 23:56:19 +00:00
|
|
|
TypeLink: "image/jpeg",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
allEntries = append(allEntries, item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-25 23:38:18 +00:00
|
|
|
feedTitle := "All Documents"
|
|
|
|
if query != nil {
|
|
|
|
feedTitle = "Search Results"
|
|
|
|
}
|
|
|
|
|
2023-10-05 23:56:19 +00:00
|
|
|
// Build & Return XML
|
|
|
|
searchFeed := &opds.Feed{
|
2023-11-25 23:38:18 +00:00
|
|
|
Title: feedTitle,
|
2023-10-05 23:56:19 +00:00
|
|
|
Updated: time.Now().UTC(),
|
|
|
|
Entries: allEntries,
|
|
|
|
}
|
|
|
|
|
|
|
|
c.XML(http.StatusOK, searchFeed)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *API) opdsSearchDescription(c *gin.Context) {
|
|
|
|
rawXML := `<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
2023-11-04 23:40:32 +00:00
|
|
|
<ShortName>Search AnthoLume</ShortName>
|
|
|
|
<Description>Search AnthoLume</Description>
|
2023-11-27 02:41:17 +00:00
|
|
|
<Url type="application/atom+xml;profile=opds-catalog;kind=acquisition" template="/api/opds/documents?search={searchTerms}"/>
|
2023-10-05 23:56:19 +00:00
|
|
|
</OpenSearchDescription>`
|
|
|
|
c.Data(http.StatusOK, "application/xml", []byte(rawXML))
|
|
|
|
}
|