forked from Ivasoft/opds-proxy
refactor
This commit is contained in:
109
html/feed.go
Normal file
109
html/feed.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/evan-buss/kobo-opds-proxy/opds"
|
||||
)
|
||||
|
||||
type FeedViewModel struct {
|
||||
Title string
|
||||
Search string
|
||||
Navigation []NavigationViewModel
|
||||
Links []LinkViewModel
|
||||
}
|
||||
type NavigationViewModel struct {
|
||||
Href string
|
||||
Label string
|
||||
}
|
||||
|
||||
type LinkViewModel struct {
|
||||
Title string
|
||||
Author string
|
||||
ImageURL string
|
||||
Content string
|
||||
Href string
|
||||
IsDownload bool
|
||||
}
|
||||
|
||||
func convertFeed(p *FeedParams) FeedViewModel {
|
||||
vm := FeedViewModel{
|
||||
Title: p.Feed.Title,
|
||||
Search: "",
|
||||
Links: make([]LinkViewModel, 0),
|
||||
Navigation: make([]NavigationViewModel, 0),
|
||||
}
|
||||
|
||||
for _, link := range p.Feed.Links {
|
||||
if link.Rel == "search" {
|
||||
vm.Search = resolveHref(p.URL, link.Href)
|
||||
}
|
||||
|
||||
if link.TypeLink == "application/atom+xml;type=feed;profile=opds-catalog" {
|
||||
vm.Navigation = append(vm.Navigation, NavigationViewModel{
|
||||
Href: resolveHref(p.URL, link.Href),
|
||||
Label: strings.ToUpper(link.Rel[:1]) + link.Rel[1:],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range p.Feed.Entries {
|
||||
vm.Links = append(vm.Links, constructLink(p.URL, entry))
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
|
||||
func constructLink(url string, entry opds.Entry) LinkViewModel {
|
||||
vm := LinkViewModel{
|
||||
Title: entry.Title,
|
||||
Content: entry.Content.Content,
|
||||
}
|
||||
|
||||
authors := make([]string, 0)
|
||||
for _, author := range entry.Author {
|
||||
authors = append(authors, author.Name)
|
||||
}
|
||||
vm.Author = strings.Join(authors, " & ")
|
||||
|
||||
for _, link := range entry.Links {
|
||||
vm.IsDownload = link.IsDownload()
|
||||
if link.IsNavigation() || link.IsDownload() {
|
||||
vm.Href = resolveHref(url, link.Href)
|
||||
}
|
||||
|
||||
// Prefer the first "thumbnail" image we find
|
||||
if vm.ImageURL == "" && link.IsImage("thumbnail") {
|
||||
vm.ImageURL = resolveHref(url, link.Href)
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a thumbnail, use the first image we find
|
||||
if vm.ImageURL == "" {
|
||||
for _, link := range entry.Links {
|
||||
if link.IsImage("") {
|
||||
vm.ImageURL = resolveHref(url, link.Href)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
func resolveHref(feedUrl string, relativePath string) string {
|
||||
baseUrl, err := url.Parse(feedUrl)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
relativeUrl, err := url.Parse(relativePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resolved := baseUrl.ResolveReference(relativeUrl).String()
|
||||
fmt.Println("Resolved URL: ", resolved)
|
||||
return resolved
|
||||
}
|
||||
@@ -3,7 +3,11 @@
|
||||
<nav class="navigation">
|
||||
{{if .Search }}
|
||||
<div class="search">
|
||||
<a href="{{.Search}}" tabindex="-1">
|
||||
<a
|
||||
href="{{.Search}}"
|
||||
tabindex="-1"
|
||||
hx-get="?q={{.Search}}&partial=search.html"
|
||||
>
|
||||
<svg
|
||||
width="30"
|
||||
height="30"
|
||||
@@ -50,4 +54,5 @@
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
|
||||
{{end}}
|
||||
|
||||
119
html/html.go
119
html/html.go
@@ -2,12 +2,8 @@ package html
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/evan-buss/kobo-opds-proxy/opds"
|
||||
)
|
||||
@@ -17,11 +13,12 @@ var files embed.FS
|
||||
|
||||
var (
|
||||
home = parse("home.html")
|
||||
feed = parse("feed.html")
|
||||
feed = parse("feed.html", "partials/search.html")
|
||||
)
|
||||
|
||||
func parse(file string) *template.Template {
|
||||
return template.Must(template.New("layout.html").ParseFS(files, "layout.html", file))
|
||||
func parse(file ...string) *template.Template {
|
||||
file = append(file, "layout.html")
|
||||
return template.Must(template.New("layout.html").ParseFS(files, file...))
|
||||
}
|
||||
|
||||
type FeedParams struct {
|
||||
@@ -29,111 +26,12 @@ type FeedParams struct {
|
||||
Feed *opds.Feed
|
||||
}
|
||||
|
||||
type FeedViewModel struct {
|
||||
Title string
|
||||
Search string
|
||||
Navigation []NavigationViewModel
|
||||
Links []LinkViewModel
|
||||
}
|
||||
type NavigationViewModel struct {
|
||||
Href string
|
||||
Label string
|
||||
}
|
||||
|
||||
type LinkViewModel struct {
|
||||
Title string
|
||||
Author string
|
||||
ImageURL string
|
||||
Content string
|
||||
Href string
|
||||
IsDownload bool
|
||||
}
|
||||
|
||||
func convertFeed(url string, feed *opds.Feed) FeedViewModel {
|
||||
vm := FeedViewModel{
|
||||
Title: feed.Title,
|
||||
Search: "",
|
||||
Links: make([]LinkViewModel, 0),
|
||||
Navigation: make([]NavigationViewModel, 0),
|
||||
}
|
||||
|
||||
for _, link := range feed.Links {
|
||||
if link.Rel == "search" {
|
||||
vm.Search = resolveHref(url, link.Href)
|
||||
}
|
||||
|
||||
if link.TypeLink == "application/atom+xml;type=feed;profile=opds-catalog" {
|
||||
vm.Navigation = append(vm.Navigation, NavigationViewModel{
|
||||
Href: resolveHref(url, link.Href),
|
||||
Label: strings.ToUpper(link.Rel[:1]) + link.Rel[1:],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range feed.Entries {
|
||||
vm.Links = append(vm.Links, constructLink(url, entry))
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
|
||||
func constructLink(url string, entry opds.Entry) LinkViewModel {
|
||||
vm := LinkViewModel{
|
||||
Title: entry.Title,
|
||||
Content: entry.Content.Content,
|
||||
}
|
||||
|
||||
authors := make([]string, 0)
|
||||
for _, author := range entry.Author {
|
||||
authors = append(authors, author.Name)
|
||||
}
|
||||
vm.Author = strings.Join(authors, " & ")
|
||||
|
||||
for _, link := range entry.Links {
|
||||
vm.IsDownload = link.IsDownload()
|
||||
if link.IsNavigation() || link.IsDownload() {
|
||||
vm.Href = resolveHref(url, link.Href)
|
||||
}
|
||||
|
||||
// Prefer the first "thumbnail" image we find
|
||||
if vm.ImageURL == "" && link.IsImage("thumbnail") {
|
||||
vm.ImageURL = resolveHref(url, link.Href)
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a thumbnail, use the first image we find
|
||||
if vm.ImageURL == "" {
|
||||
for _, link := range entry.Links {
|
||||
if link.IsImage("") {
|
||||
vm.ImageURL = resolveHref(url, link.Href)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
func resolveHref(feedUrl string, relativePath string) string {
|
||||
baseUrl, err := url.Parse(feedUrl)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
relativeUrl, err := url.Parse(relativePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resolved := baseUrl.ResolveReference(relativeUrl).String()
|
||||
fmt.Println("Resolved URL: ", resolved)
|
||||
return resolved
|
||||
}
|
||||
|
||||
func Feed(w io.Writer, p FeedParams, partial string) error {
|
||||
if partial == "" {
|
||||
partial = "layout.html"
|
||||
}
|
||||
|
||||
vm := convertFeed(p.URL, p.Feed)
|
||||
vm := convertFeed(&p)
|
||||
|
||||
return feed.ExecuteTemplate(w, partial, vm)
|
||||
}
|
||||
@@ -143,8 +41,11 @@ type FeedInfo struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
func Home(w io.Writer, p []FeedInfo) error {
|
||||
return home.ExecuteTemplate(w, "layout.html", p)
|
||||
func Home(w io.Writer, vm []FeedInfo, partial string) error {
|
||||
if partial == "" {
|
||||
partial = "layout.html"
|
||||
}
|
||||
return home.ExecuteTemplate(w, partial, vm)
|
||||
}
|
||||
|
||||
func StaticFiles() embed.FS {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{{block "title" .}}Kobo OPDS Proxy{{end}}</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<main id="container">
|
||||
{{block "content" .}}{{end}}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
<script
|
||||
src="https://unpkg.com/htmx.org@2.0.0"
|
||||
integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<main id="container">{{block "content" .}}{{end}}</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
16
html/partials/search.html
Normal file
16
html/partials/search.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{{define "search-partial"}}
|
||||
|
||||
<form method="get">
|
||||
<input type="hidden" name="q" value="{{.Search}}" />
|
||||
<input type="search" placeholder="Search" name="search" />
|
||||
|
||||
<script>
|
||||
const searchInput = document.querySelector("input[name=search]");
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if (searchParams.has("search") && !!searchInput) {
|
||||
searchInput.value = searchParams.get("search");
|
||||
}
|
||||
</script>
|
||||
</form>
|
||||
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user