forked from Ivasoft/opds-proxy
refactor: simplify template by using a view model
Convert the raw OPDS format to a view model that is easier to work with. Biggest change is that we construct a single "Link" from an entry with an array of Links with different "TypeLinks" and "Rel" attributes.
This commit is contained in:
@@ -1,20 +1,24 @@
|
||||
{{define "title"}}{{.Title}}{{end}}
|
||||
{{define "content"}}
|
||||
<ul>
|
||||
{{range .Feed.Links}}
|
||||
<li><a href="{{.Href}}" rel="{{.Rel}}">{{.Title}}{{.Rel}}</a></li>
|
||||
{{if .Search }}
|
||||
<li><a href="{{.Search}}">Search</a></li>
|
||||
{{end}}
|
||||
|
||||
{{range .Feed.Entries}}
|
||||
{{range .Navigation}}
|
||||
<li><a href="{{.Href}}">{{.Label}}</a></li>
|
||||
{{end}}
|
||||
|
||||
{{range .Links}}
|
||||
<li class="link">
|
||||
{{$title := .Title}}
|
||||
{{range .Links}}
|
||||
{{if .IsImage "image/thumbnail"}}
|
||||
<img src="/acquisition{{.Href}}" alt="{{$title}}" height="40">
|
||||
{{else if .IsDownload}}
|
||||
<a href="/acquisition{{.Href}}" download>{{$title}}</a>
|
||||
{{else if .IsNavigation }}
|
||||
<a href="{{.Href}}">{{$title}}</a>
|
||||
{{end}}
|
||||
{{if .ImageURL}}
|
||||
<img src="/acquisition{{.ImageURL}}" alt="{{.Title}}" height="40">
|
||||
{{end}}
|
||||
|
||||
{{if .IsDownload}}
|
||||
<a href="/acquisition{{.Href}}" download>{{.Title}}</a>
|
||||
{{else}}
|
||||
<a href="{{.Href}}">{{.Title}}</a>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
|
||||
82
html/html.go
82
html/html.go
@@ -4,6 +4,7 @@ import (
|
||||
"embed"
|
||||
"html/template"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/evan-buss/kobo-opds-proxy/opds"
|
||||
)
|
||||
@@ -23,12 +24,91 @@ 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
|
||||
ImageURL string
|
||||
Content string
|
||||
Href string
|
||||
IsDownload bool
|
||||
}
|
||||
|
||||
func convertFeed(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 = link.Href
|
||||
}
|
||||
|
||||
if link.TypeLink == "application/atom+xml;type=feed;profile=opds-catalog" {
|
||||
vm.Navigation = append(vm.Navigation, NavigationViewModel{
|
||||
Href: link.Href,
|
||||
Label: strings.ToUpper(link.Rel[:1]) + link.Rel[1:],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, entry := range feed.Entries {
|
||||
vm.Links = append(vm.Links, constructLink(entry))
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
|
||||
func constructLink(entry opds.Entry) LinkViewModel {
|
||||
vm := LinkViewModel{
|
||||
Title: entry.Title,
|
||||
Content: entry.Content.Content,
|
||||
}
|
||||
|
||||
for _, link := range entry.Links {
|
||||
if link.IsNavigation() || link.IsDownload() {
|
||||
vm.Href = link.Href
|
||||
}
|
||||
|
||||
// Prefer the first "thumbnail" image we find
|
||||
if vm.ImageURL == "" && link.IsImage("thumbnail") {
|
||||
vm.ImageURL = 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 = link.Href
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vm
|
||||
}
|
||||
|
||||
func Feed(w io.Writer, p FeedParams, partial string) error {
|
||||
if partial == "" {
|
||||
partial = "layout.html"
|
||||
}
|
||||
|
||||
return feed.ExecuteTemplate(w, partial, p)
|
||||
vm := convertFeed(p.Feed)
|
||||
|
||||
return feed.ExecuteTemplate(w, partial, vm)
|
||||
}
|
||||
|
||||
func StaticFiles() embed.FS {
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
|
||||
#container > ul > li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: start;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
16
opds/opds.go
16
opds/opds.go
@@ -17,22 +17,16 @@ func ParseFeed(r io.Reader) (*Feed, error) {
|
||||
return &feed, nil
|
||||
}
|
||||
|
||||
func (link Link) IsDownload() string {
|
||||
if link.Rel == "http://opds-spec.org/acquisition" {
|
||||
return link.Href
|
||||
}
|
||||
|
||||
return ""
|
||||
func (link Link) IsDownload() bool {
|
||||
return link.Rel == "http://opds-spec.org/acquisition"
|
||||
}
|
||||
|
||||
func (link Link) IsImage(category string) string {
|
||||
func (link Link) IsImage(category string) bool {
|
||||
if strings.HasPrefix(link.TypeLink, "image") {
|
||||
if strings.Contains(link.Rel, category) {
|
||||
return link.Href
|
||||
}
|
||||
return strings.Contains(link.Rel, category)
|
||||
}
|
||||
|
||||
return ""
|
||||
return false
|
||||
}
|
||||
|
||||
func (link Link) IsNavigation() bool {
|
||||
|
||||
Reference in New Issue
Block a user