Improved handling of errors and included manual refreshing

Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
This commit is contained in:
2025-05-08 08:11:25 +01:00
parent fa72756aaf
commit d2566e80d3
3 changed files with 59 additions and 17 deletions

44
main.go
View File

@@ -28,24 +28,58 @@ func init() {
func main() { func main() {
latestBooks := map[string]*storygraph.Book{} latestBooks := map[string]*storygraph.Book{}
go func() { refreshingBooks := true
var err error var lastUpdated *time.Time
for {
latestBooks, err = storygraph.GetLatestBooks() updateBooks := func() {
refreshingBooks = true
defer func() { refreshingBooks = false }()
newBookList, err := storygraph.GetLatestBooks()
if err != nil { if err != nil {
fmt.Println("Error fetching latest books:", err) fmt.Println("Error fetching latest books:", err)
return return
} }
// Update each category individually with books we have managed to find
for cat, book := range newBookList {
if b, ok := newBookList[cat]; ok && b != nil {
latestBooks[cat] = book
}
}
now := time.Now()
lastUpdated = &now
fmt.Println("Updated latest book recommendations") fmt.Println("Updated latest book recommendations")
}
go func() {
for {
updateBooks()
time.Sleep(1 * time.Hour) time.Sleep(1 * time.Hour)
} }
}() }()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
type data struct {
Books map[string]*storygraph.Book
LastUpdated string
Refreshing bool
}
d := data{
Books: latestBooks,
LastUpdated: "",
Refreshing: refreshingBooks,
}
if lastUpdated != nil {
d.LastUpdated = lastUpdated.Format(time.RFC1123)
}
tmpl := template.Must(template.ParseFiles("templates/index.html")) tmpl := template.Must(template.ParseFiles("templates/index.html"))
tmpl.Execute(w, latestBooks) tmpl.Execute(w, d)
})
http.HandleFunc("/refresh", func(w http.ResponseWriter, r *http.Request) {
go updateBooks()
w.Header().Set("Location", "/")
w.Header().Set("Refresh", "0; url=/")
w.Write([]byte("Refreshing..."))
}) })
http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets")))) http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets"))))
http.ListenAndServe(fmt.Sprintf(":%s", port), nil) http.ListenAndServe(fmt.Sprintf(":%s", port), nil)

View File

@@ -23,7 +23,7 @@ func New(cookie string) *HTTPClient {
Cookie: cookie, Cookie: cookie,
client: &http.Client{}, client: &http.Client{},
rl: rate.NewLimiter(rate.Every(1*time.Second), 15), rl: rate.NewLimiter(rate.Every(1*time.Second), 10),
ctx: context.Background(), ctx: context.Background(),
retries: 3, retries: 3,
} }
@@ -52,7 +52,7 @@ func (h *HTTPClient) Get(url string) (*http.Response, error) {
if resp.StatusCode == 429 { if resp.StatusCode == 429 {
fmt.Println("Rate limit exceeded, retrying...") fmt.Println("Rate limit exceeded, retrying...")
h.retries-- h.retries--
time.Sleep(30 * time.Second) time.Sleep(15 * time.Second)
continue continue
} }

View File

@@ -54,11 +54,19 @@
<header> <header>
<h1>Next Book</h1> <h1>Next Book</h1>
<p>Suggestions for the next book to read from your Storygraph to-read pile</p> <p>Suggestions for the next book to read from your Storygraph to-read pile</p>
{{ if .Refreshing }}
<div>Refreshing recommendations...</div>
{{ else if .LastUpdated }}
<div>
Last Update: {{ .LastUpdated }}
<a href="/refresh">Refresh recommendations</a>
</div>
{{ end }}
</header> </header>
{{ if index . "Fiction" }} {{ if index .Books "Fiction" }}
<main> <main>
{{ $cat := "Fiction" }} {{ $cat := "Fiction" }}
{{ $book := index . $cat }} {{ $book := index .Books $cat }}
<div class="category" style="grid-column: span 4;"> <div class="category" style="grid-column: span 4;">
<h3>{{ $cat }}</h3> <h3>{{ $cat }}</h3>
<a href="{{ $book.Link }}" target="_blank"> <a href="{{ $book.Link }}" target="_blank">
@@ -68,7 +76,7 @@
<div class="rating">⭐ {{ $book.Rating }}</div> <div class="rating">⭐ {{ $book.Rating }}</div>
</div> </div>
{{ $cat := "Non-Fiction" }} {{ $cat := "Non-Fiction" }}
{{ $book := index . $cat }} {{ $book := index .Books $cat }}
<div class="category" style="grid-column: span 4;"> <div class="category" style="grid-column: span 4;">
<h3>{{ $cat }}</h3> <h3>{{ $cat }}</h3>
<a href="{{ $book.Link }}" target="_blank"> <a href="{{ $book.Link }}" target="_blank">
@@ -78,7 +86,7 @@
<div class="rating">⭐ {{ $book.Rating }}</div> <div class="rating">⭐ {{ $book.Rating }}</div>
</div> </div>
{{ $cat := "Comics" }} {{ $cat := "Comics" }}
{{ $book := index . $cat }} {{ $book := index .Books $cat }}
<div class="category" style="grid-column: span 4;"> <div class="category" style="grid-column: span 4;">
<h3>{{ $cat }}</h3> <h3>{{ $cat }}</h3>
<a href="{{ $book.Link }}" target="_blank"> <a href="{{ $book.Link }}" target="_blank">
@@ -88,7 +96,7 @@
<div class="rating">⭐ {{ $book.Rating }}</div> <div class="rating">⭐ {{ $book.Rating }}</div>
</div> </div>
{{ range $cat, $book := .}} {{ range $cat, $book := .Books }}
{{ if or (eq $cat "Fiction") (eq $cat "Non-Fiction") (eq $cat "Comics") }} {{ if or (eq $cat "Fiction") (eq $cat "Non-Fiction") (eq $cat "Comics") }}
{{ continue }} {{ continue }}
{{ end }} {{ end }}