Skip to content
Snippets Groups Projects
Commit d4b72fa0 authored by ale's avatar ale
Browse files

generate thumbnails

parent 3599ba3c
No related branches found
No related tags found
No related merge requests found
...@@ -168,11 +168,9 @@ func doSearch(db *liber.Database, query string) { ...@@ -168,11 +168,9 @@ func doSearch(db *liber.Database, query string) {
} }
func doHttpServer(db *liber.Database, addr string) { func doHttpServer(db *liber.Database, addr string) {
storage := &liber.FileStorage{ storage := liber.NewFileStorage(expandTilde(*bookDir), 2)
Root: *bookDir, cache := liber.NewFileStorage(filepath.Join(expandTilde(*databaseDir), "cache"), 2)
Nesting: 2, server := liber.NewHttpServer(db, storage, cache, addr)
}
server := liber.NewHttpServer(db, storage, addr)
log.Fatal(server.ListenAndServe()) log.Fatal(server.ListenAndServe())
} }
...@@ -217,7 +215,7 @@ func main() { ...@@ -217,7 +215,7 @@ func main() {
log.SetOutput(logf) log.SetOutput(logf)
} }
doUpdate(db, *bookDir) doUpdate(db, expandTilde(*bookDir))
} else if *remotesync != "" { } else if *remotesync != "" {
doSync(db, *remotesync) doSync(db, *remotesync)
} else if *search { } else if *search {
......
...@@ -2,6 +2,7 @@ package liber ...@@ -2,6 +2,7 @@ package liber
import ( import (
"bytes" "bytes"
cryptorand "crypto/rand"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"errors" "errors"
...@@ -34,6 +35,13 @@ func (id BookId) Key() []byte { ...@@ -34,6 +35,13 @@ func (id BookId) Key() []byte {
return buf.Bytes() return buf.Bytes()
} }
func init() {
// Seed the RNG to a random value.
var seed int64
binary.Read(cryptorand.Reader, binary.LittleEndian, &seed)
rand.Seed(seed)
}
func NewID() BookId { func NewID() BookId {
return BookId(rand.Int63()) return BookId(rand.Int63())
} }
......
...@@ -11,6 +11,13 @@ type FileStorage struct { ...@@ -11,6 +11,13 @@ type FileStorage struct {
Nesting int Nesting int
} }
func NewFileStorage(root string, nesting int) *FileStorage {
return &FileStorage{
Root: root,
Nesting: nesting,
}
}
// Path of the file corresponding to the given key, relative to the // Path of the file corresponding to the given key, relative to the
// root directory. // root directory.
func (s *FileStorage) Path(key string) string { func (s *FileStorage) Path(key string) string {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="row"> <div class="row">
<div class="book-icon"> <div class="book-icon">
{{if .CoverPath}} {{if .CoverPath}}
<img src="/book/cover/{{.Id}}"> <img src="/book/thumb/{{.Id}}">
{{end}} {{end}}
</div> </div>
<div class="book-body"> <div class="book-body">
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
"os/exec"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
...@@ -26,6 +27,7 @@ var ( ...@@ -26,6 +27,7 @@ var (
type uiServer struct { type uiServer struct {
db *Database db *Database
storage *FileStorage storage *FileStorage
cache *FileStorage
} }
type pagination struct { type pagination struct {
...@@ -186,6 +188,46 @@ func (s *uiServer) handleShowCover(book *Book, w http.ResponseWriter, req *http. ...@@ -186,6 +188,46 @@ func (s *uiServer) handleShowCover(book *Book, w http.ResponseWriter, req *http.
http.ServeContent(w, req, "", time.Time{}, f) http.ServeContent(w, req, "", time.Time{}, f)
} }
const thumbnailSize = "150x150"
func (s *uiServer) handleShowThumbnail(book *Book, w http.ResponseWriter, req *http.Request) {
if book.CoverPath == "" {
http.NotFound(w, req)
return
}
w.Header().Set("Cache-Control", "public")
cachedPath := s.cache.Path(fmt.Sprintf("%d.thumb.jpg", book.Id))
if cachedFile, err := s.cache.Open(cachedPath); err == nil {
// Thumbnail is cached.
defer cachedFile.Close()
w.Header().Set("ETag", "T"+book.Id.String())
http.ServeContent(w, req, "", time.Time{}, cachedFile)
return
}
cachedFile, err := s.cache.Create(cachedPath)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer cachedFile.Close()
data, err := exec.Command("convert", book.CoverPath, "-geometry", thumbnailSize,
"-quality", "60", "jpeg:-").Output()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
w.Header().Set("ETag", "T"+book.Id.String())
cachedFile.Write(data)
w.Write(data)
}
func (s *uiServer) handleHome(w http.ResponseWriter, req *http.Request) { func (s *uiServer) handleHome(w http.ResponseWriter, req *http.Request) {
render("index.html", w, nil) render("index.html", w, nil)
} }
...@@ -199,7 +241,7 @@ func render(templateName string, w http.ResponseWriter, ctx interface{}) { ...@@ -199,7 +241,7 @@ func render(templateName string, w http.ResponseWriter, ctx interface{}) {
io.Copy(w, &buf) io.Copy(w, &buf)
} }
func NewHttpServer(db *Database, storage *FileStorage, addr string) *http.Server { func NewHttpServer(db *Database, storage, cache *FileStorage, addr string) *http.Server {
var err error var err error
tpl, err = template.New("liber").Funcs(template.FuncMap{ tpl, err = template.New("liber").Funcs(template.FuncMap{
"join": strings.Join, "join": strings.Join,
...@@ -209,7 +251,7 @@ func NewHttpServer(db *Database, storage *FileStorage, addr string) *http.Server ...@@ -209,7 +251,7 @@ func NewHttpServer(db *Database, storage *FileStorage, addr string) *http.Server
} }
syncsrv := &syncServer{db, storage} syncsrv := &syncServer{db, storage}
uisrv := &uiServer{db, storage} uisrv := &uiServer{db, storage, cache}
r := mux.NewRouter() r := mux.NewRouter()
r.Handle("/static/{path:.*}", http.StripPrefix("/static/", r.Handle("/static/{path:.*}", http.StripPrefix("/static/",
...@@ -217,6 +259,7 @@ func NewHttpServer(db *Database, storage *FileStorage, addr string) *http.Server ...@@ -217,6 +259,7 @@ func NewHttpServer(db *Database, storage *FileStorage, addr string) *http.Server
r.HandleFunc("/api/sync/upload", syncsrv.handleSyncUpload).Methods("POST") r.HandleFunc("/api/sync/upload", syncsrv.handleSyncUpload).Methods("POST")
r.HandleFunc("/api/sync/diff", syncsrv.handleDiffRequest).Methods("POST") r.HandleFunc("/api/sync/diff", syncsrv.handleDiffRequest).Methods("POST")
r.Handle("/book/cover/{id:[0-9]+}", uisrv.withBook(uisrv.handleShowCover)) r.Handle("/book/cover/{id:[0-9]+}", uisrv.withBook(uisrv.handleShowCover))
r.Handle("/book/thumb/{id:[0-9]+}", uisrv.withBook(uisrv.handleShowThumbnail))
r.Handle("/book/{id:[0-9]+}", uisrv.withBook(uisrv.handleShowBook)) r.Handle("/book/{id:[0-9]+}", uisrv.withBook(uisrv.handleShowBook))
r.Handle("/dl/{id:[0-9]+}", uisrv.withBook(uisrv.handleDownloadBook)) r.Handle("/dl/{id:[0-9]+}", uisrv.withBook(uisrv.handleDownloadBook))
r.HandleFunc("/suggest", uisrv.handleAutocomplete) r.HandleFunc("/suggest", uisrv.handleAutocomplete)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment