Using epubgo to handle epubs
This commit is contained in:
parent
d46c3b72b6
commit
d994d6b91f
6 changed files with 167 additions and 108 deletions
86
cover.go
86
cover.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
124
reader.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
53
store.go
53
store.go
|
@ -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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 + "'"
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue