Using epubgo to handle epubs

This commit is contained in:
Las Zenow 2013-04-01 14:05:04 +02:00
parent d46c3b72b6
commit d994d6b91f
6 changed files with 167 additions and 108 deletions

View file

@ -2,10 +2,12 @@ package main
import ( import (
"bytes" "bytes"
"git.gitorious.org/go-pkg/epub.git" "git.gitorious.org/go-pkg/epubgo.git"
"github.com/nfnt/resize" "github.com/nfnt/resize"
"image" "image"
"image/jpeg" "image/jpeg"
"io"
"io/ioutil"
"log" "log"
"os" "os"
"regexp" "regexp"
@ -13,51 +15,68 @@ import (
"unicode/utf8" "unicode/utf8"
) )
func GetCover(e *epub.Epub, title string) (string, string) { func GetCover(e *epubgo.Epub, title string) (string, string) {
/* Try first common names */ imgPath, smallPath := searchCommonCoverNames(e, title)
for _, p := range []string{"cover.jpg", "Images/cover.jpg", "cover.jpeg", "cover1.jpg", "cover1.jpeg"} { if imgPath != "" {
img := e.Data(p) return imgPath, smallPath
if len(img) != 0 {
return storeImg(img, title, ".jpg")
}
} }
/* search for img on the text */ /* search for img on the text */
exp, _ := regexp.Compile("<ima?g.*[(src)(href)]=[\"']([^\"']*(\\.[^\\.\"']*))[\"']") exp, _ := regexp.Compile("<ima?g.*[(src)(href)]=[\"']([^\"']*(\\.[^\\.\"']*))[\"']")
it := e.Iterator(epub.EITERATOR_SPINE) it, errNext := e.Spine()
defer it.Close() for errNext == nil {
var err error = nil file, err := it.Open()
txt := it.Curr() if err != nil {
for err == nil { break
res := exp.FindStringSubmatch(txt) }
defer file.Close()
txt, err := ioutil.ReadAll(file)
if err != nil {
break
}
res := exp.FindSubmatch(txt)
if res != nil { if res != nil {
urlPart := strings.Split(it.CurrUrl(), "/") href := string(res[1])
urlPart := strings.Split(it.Url(), "/")
url := strings.Join(urlPart[:len(urlPart)-1], "/") url := strings.Join(urlPart[:len(urlPart)-1], "/")
if res[1][:3] == "../" { if href[:3] == "../" {
res[1] = res[1][3:] href = href[3:]
url = strings.Join(urlPart[:len(urlPart)-2], "/") url = strings.Join(urlPart[:len(urlPart)-2], "/")
} }
res[1] = strings.Replace(res[1], "%20", " ", -1) href = strings.Replace(href, "%20", " ", -1)
res[1] = strings.Replace(res[1], "%27", "'", -1) href = strings.Replace(href, "%27", "'", -1)
res[1] = strings.Replace(res[1], "%28", "(", -1) href = strings.Replace(href, "%28", "(", -1)
res[1] = strings.Replace(res[1], "%29", ")", -1) href = strings.Replace(href, "%29", ")", -1)
if url == "" { if url == "" {
url = res[1] url = href
} else { } else {
url = url + "/" + res[1] url = url + "/" + href
} }
img := e.Data(url) img, err := e.OpenFile(url)
if len(img) != 0 { if err == nil {
return storeImg(img, title, res[2]) defer img.Close()
return storeImg(img, title, string(res[2]))
} }
} }
txt, err = it.Next() errNext = it.Next()
} }
return "", "" return "", ""
} }
func storeImg(img []byte, title, extension string) (string, string) { func searchCommonCoverNames(e *epubgo.Epub, title string) (string, string) {
for _, p := range []string{"cover.jpg", "Images/cover.jpg", "cover.jpeg", "cover1.jpg", "cover1.jpeg"} {
img, err := e.OpenFile(p)
if err == nil {
defer img.Close()
return storeImg(img, title, ".jpg")
}
}
return "", ""
}
func storeImg(img io.Reader, title, extension string) (string, string) {
r, _ := utf8.DecodeRuneInString(title) r, _ := utf8.DecodeRuneInString(title)
folder := string(r) folder := string(r)
if _, err := os.Stat(COVER_PATH + folder); err != nil { if _, err := os.Stat(COVER_PATH + folder); err != nil {
@ -86,8 +105,10 @@ func storeImg(img []byte, title, extension string) (string, string) {
defer fSmall.Close() defer fSmall.Close()
/* resize img */ /* resize img */
var img2 bytes.Buffer
img1 := io.TeeReader(img, &img2)
jpgOptions := jpeg.Options{IMG_QUALITY} jpgOptions := jpeg.Options{IMG_QUALITY}
imgResized, err := resizeImg(img, IMG_WIDTH_BIG) imgResized, err := resizeImg(img1, IMG_WIDTH_BIG)
if err != nil { if err != nil {
log.Println("Error resizing big image:", err.Error()) log.Println("Error resizing big image:", err.Error())
return "", "" return "", ""
@ -97,7 +118,7 @@ func storeImg(img []byte, title, extension string) (string, string) {
log.Println("Error encoding big image:", err.Error()) log.Println("Error encoding big image:", err.Error())
return "", "" return "", ""
} }
imgSmallResized, err := resizeImg(img, IMG_WIDTH_SMALL) imgSmallResized, err := resizeImg(&img2, IMG_WIDTH_SMALL)
if err != nil { if err != nil {
log.Println("Error resizing small image:", err.Error()) log.Println("Error resizing small image:", err.Error())
return "", "" return "", ""
@ -111,9 +132,8 @@ func storeImg(img []byte, title, extension string) (string, string) {
return imgPath, imgPathSmall return imgPath, imgPathSmall
} }
func resizeImg(imgBuff []byte, width uint) (image.Image, error) { func resizeImg(imgReader io.Reader, width uint) (image.Image, error) {
reader := bytes.NewReader(imgBuff) img, _, err := image.Decode(imgReader)
img, _, err := image.Decode(reader)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -106,7 +106,7 @@ func (d *DB) IncDownload(path string) error {
} }
/* optional parameters: length and start index /* optional parameters: length and start index
* *
* Returns: list of books, number found and err * Returns: list of books, number found and err
*/ */
func (d *DB) GetBooks(query bson.M, r ...int) (books []Book, num int, err error) { func (d *DB) GetBooks(query bson.M, r ...int) (books []Book, num int, err error) {
@ -161,7 +161,7 @@ func (d *DB) GetDownloadedBooks(num int) (books []Book, err error) {
} }
/* optional parameters: length and start index /* optional parameters: length and start index
* *
* Returns: list of books, number found and err * Returns: list of books, number found and err
*/ */
func (d *DB) GetNewBooks(r ...int) (books []Book, num int, err error) { func (d *DB) GetNewBooks(r ...int) (books []Book, num int, err error) {

124
reader.go
View file

@ -1,7 +1,8 @@
package main package main
import ( import (
"git.gitorious.org/go-pkg/epub.git" "git.gitorious.org/go-pkg/epubgo.git"
"io"
"labix.org/v2/mgo/bson" "labix.org/v2/mgo/bson"
"net/http" "net/http"
"regexp" "regexp"
@ -67,52 +68,75 @@ func cleanLink(link string) string {
return link return link
} }
/* return next and prev urls from document and the list of chapters */ func getNextPrev(e *epubgo.Epub, file string, id string, base string) (string, string) {
func chapterList(e *epub.Epub, file string, id string, base string) (string, string, []chapter) { spine, err := e.Spine()
var chapters []chapter if err != nil {
return "", ""
}
prev := "" prev := ""
next := "" next := ""
tit := e.Titerator(epub.TITERATOR_NAVMAP) for err == nil {
defer tit.Close() if cleanLink(spine.Url()) == file {
break
}
prev = spine.Url()
err = spine.Next()
}
if err != nil {
return "", ""
}
activeIndx := -1 prev = genLink(id, base, prev)
depth := 0 if spine.Next() == nil {
for ; tit.Valid(); tit.Next() { next = genLink(id, base, spine.Url())
}
return next, prev
}
func getChapters(e *epubgo.Epub, file string, id string, base string) []chapter {
nav, err := e.Navigation()
if err != nil {
return nil
}
chapters := listChapters(nav, 0)
for i, c := range chapters {
chapters[i].Link = genLink(id, base, c.Link)
if cleanLink(c.Link) == file {
chapters[i].Active = true
}
}
return chapters
}
func listChapters(nav *epubgo.NavigationIterator, depth int) []chapter {
var chapters []chapter
var err error = nil
for err == nil {
var c chapter var c chapter
c.Label = tit.Label() c.Label = nav.Title()
c.Link = genLink(id, base, tit.Link()) c.Link = nav.Url()
if cleanLink(tit.Link()) == file { c.Depth = depth
c.Active = true
activeIndx = len(chapters)
}
c.Depth = tit.Depth()
for c.Depth > depth {
c.In = append(c.In, true)
depth++
}
for c.Depth < depth { for c.Depth < depth {
c.Out = append(c.Out, true) c.Out = append(c.Out, true)
depth-- depth--
} }
chapters = append(chapters, c) chapters = append(chapters, c)
}
/* if is the same chapter check the previous */ if nav.HasChildren() {
i := activeIndx - 1 nav.In()
for i >= 0 && strings.Contains(chapters[i].Link, "#") { children := listChapters(nav, depth+1)
i-- children[0].In = []bool{true}
children[len(children)-1].Out = []bool{true}
chapters = append(chapters, children...)
nav.Out()
}
err = nav.Next()
} }
if i >= 0 { chapters[0].In = []bool{true}
prev = chapters[i].Link chapters[len(chapters)-1].Out = []bool{true}
} return chapters
i = activeIndx + 1
for i < len(chapters) && strings.Contains(chapters[i].Link, "#") {
i++
}
if i < len(chapters) {
next = chapters[i].Link
}
return next, prev, chapters
} }
func readHandler(w http.ResponseWriter, r *http.Request) { func readHandler(w http.ResponseWriter, r *http.Request) {
@ -138,17 +162,25 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
data.Back = "/book/" + id data.Back = "/book/" + id
bookPath = BOOKS_PATH + data.Book.Path bookPath = BOOKS_PATH + data.Book.Path
} }
e, _ := epub.Open(bookPath, 0) e, err := epubgo.Open(bookPath)
if err != nil {
http.NotFound(w, r)
return
}
defer e.Close() defer e.Close()
if file == "" { if file == "" {
it := e.Iterator(epub.EITERATOR_LINEAR) it, err := e.Spine()
defer it.Close() if err != nil {
http.Redirect(w, r, base+id+"/"+it.CurrUrl(), http.StatusTemporaryRedirect) http.NotFound(w, r)
return
}
http.Redirect(w, r, base+id+"/"+it.Url(), http.StatusTemporaryRedirect)
return return
} }
data.S = GetStatus(w, r) data.S = GetStatus(w, r)
data.Next, data.Prev, data.Chapters = chapterList(e, file, id, base) data.Next, data.Prev = getNextPrev(e, file, id, base)
data.Chapters = getChapters(e, file, id, base)
data.Content = genLink(id, "/content/", file) data.Content = genLink(id, "/content/", file)
loadTemplate(w, "read", data) loadTemplate(w, "read", data)
} }
@ -172,12 +204,18 @@ func contentHandler(w http.ResponseWriter, r *http.Request) {
} else { } else {
bookPath = BOOKS_PATH + book.Path bookPath = BOOKS_PATH + book.Path
} }
e, _ := epub.Open(bookPath, 0) e, _ := epubgo.Open(bookPath)
defer e.Close() defer e.Close()
if file == "" { if file == "" {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
w.Write(e.Data(file)) html, err := e.OpenFile(file)
if err != nil {
http.NotFound(w, r)
return
}
defer html.Close()
io.Copy(w, html)
} }

View file

@ -1,7 +1,7 @@
package main package main
import ( import (
"git.gitorious.org/go-pkg/epub.git" "git.gitorious.org/go-pkg/epubgo.git"
"io" "io"
"log" "log"
"os" "os"
@ -15,28 +15,35 @@ import (
func ParseFile(path string) (string, error) { func ParseFile(path string) (string, error) {
book := map[string]interface{}{} book := map[string]interface{}{}
e, err := epub.Open(NEW_PATH+path, 0) e, err := epubgo.Open(NEW_PATH + path)
if err != nil { if err != nil {
return "", err return "", err
} }
defer e.Close() defer e.Close()
title := cleanStr(strings.Join(e.Metadata(epub.EPUB_TITLE), ", ")) for _, m := range e.MetadataFields() {
book["title"] = title data, err := e.Metadata(m)
book["author"] = parseAuthr(e.Metadata(epub.EPUB_CREATOR)) if err != nil {
book["contributor"] = cleanStr(strings.Join(e.Metadata(epub.EPUB_CONTRIB), ", ")) continue
book["publisher"] = cleanStr(strings.Join(e.Metadata(epub.EPUB_PUBLISHER), ", ")) }
book["description"] = parseDescription(e.Metadata(epub.EPUB_DESCRIPTION)) switch m {
book["subject"] = parseSubject(e.Metadata(epub.EPUB_SUBJECT)) case "creator":
book["date"] = parseDate(e.Metadata(epub.EPUB_DATE)) book["author"] = parseAuthr(data)
book["lang"] = e.Metadata(epub.EPUB_LANG) case "description":
book["type"] = strings.Join(e.Metadata(epub.EPUB_TYPE), ", ") book[m] = parseDescription(data)
book["format"] = strings.Join(e.Metadata(epub.EPUB_FORMAT), ", ") case "subject":
book["source"] = strings.Join(e.Metadata(epub.EPUB_SOURCE), ", ") book[m] = parseSubject(data)
book["relation"] = strings.Join(e.Metadata(epub.EPUB_RELATION), ", ") case "date":
book["coverage"] = strings.Join(e.Metadata(epub.EPUB_COVERAGE), ", ") book[m] = parseDate(data)
book["rights"] = strings.Join(e.Metadata(epub.EPUB_RIGHTS), ", ") case "language":
book["meta"] = strings.Join(e.Metadata(epub.EPUB_META), ", ") book["lang"] = data
case "title", "contributor", "publisher":
book[m] = cleanStr(strings.Join(data, ", "))
default:
book[m] = strings.Join(data, ", ")
}
}
title, _ := book["title"].(string)
book["path"] = path book["path"] = path
cover, coverSmall := GetCover(e, title) cover, coverSmall := GetCover(e, title)
book["cover"] = cover book["cover"] = cover
@ -55,14 +62,8 @@ func StoreNewFile(name string, file io.Reader) (string, error) {
} }
defer fw.Close() defer fw.Close()
const size = 1024 _, err = io.Copy(fw, file)
var n int = size return path, err
buff := make([]byte, size)
for n == size {
n, err = file.Read(buff)
fw.Write(buff)
}
return path, nil
} }
func StoreBook(book Book) (path string, err error) { func StoreBook(book Book) (path string, err error) {

View file

@ -19,13 +19,13 @@
<div class="row"> <div class="row">
<div class="span4"> <div class="span4">
{{range .Chapters}} {{range .Chapters}}
{{range .Out}}
</ul>
{{end}}
{{range .In}} {{range .In}}
<ul id="bookMenu" class="nav nav-list hidden-phone"> <ul id="bookMenu" class="nav nav-list hidden-phone">
{{end}} {{end}}
<li {{if .Active}}class="active"{{end}}><a href="{{.Link}}">{{.Label}}</a></li> <li {{if .Active}}class="active"{{end}}><a href="{{.Link}}">{{.Label}}</a></li>
{{range .Out}}
</ul>
{{end}}
{{end}} {{end}}
{{if .Chapters}} {{if .Chapters}}
</ul> </ul>

View file

@ -44,7 +44,7 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) {
title, err := ParseFile(path) title, err := ParseFile(path)
if err != nil { if err != nil {
os.Remove(NEW_PATH + path) os.Remove(NEW_PATH + path)
sess.Notify("Problem uploading!", "The file '"+path+"' is not a well formed epub", "error") sess.Notify("Problem uploading!", "The file '"+path+"' is not a well formed epub: "+err.Error(), "error")
} else { } else {
uploaded = uploaded + " '" + title + "'" uploaded = uploaded + " '" + title + "'"
} }