2 Commits

Author SHA1 Message Date
lapwat
2be32cd50f fix: display images in epub 2021-12-09 23:48:25 +01:00
lapwat
4b9840e356 bump version in readme 2021-10-13 00:09:00 +02:00
7 changed files with 56 additions and 119 deletions

View File

@@ -1,10 +0,0 @@
install:
go install
format:
gofmt -s -w .
clean:
find . -maxdepth 1 -not -name 'README.md' -name '*.md' -delete
find . -maxdepth 1 -name '*.epub' -delete
find . -maxdepth 1 -name '*.mobi' -delete

View File

@@ -19,16 +19,14 @@ Available Commands:
version Print the version number of papeer version Print the version number of papeer
Flags: Flags:
-a, --author string book author
-d, --delay int time to wait before downloading next chapter, in milliseconds (default -1) -d, --delay int time to wait before downloading next chapter, in milliseconds (default -1)
-f, --format string file format [stdout, md, epub, mobi] (default "stdout") -f, --format string file format [stdout, md, epub, mobi] (default "stdout")
-h, --help help for papeer -h, --help help for papeer
--images retrieve images only --images retrieve images only
-i, --include include URL as first chapter, in resursive mode -i, --include include URL as first chapter, in resursive mode
-l, --limit int limit number of chapters, in recursive mode (default -1) -l, --limit int limit number of chapters, in recursive mode (default -1)
-n, --name string book name (default: page title)
-o, --offset int skip first chapters, in recursive mode -o, --offset int skip first chapters, in recursive mode
--output string file name (default: book name) --output string output file
-r, --recursive create one chapter per natigation item -r, --recursive create one chapter per natigation item
-s, --selector string table of content CSS selector, in resursive mode -s, --selector string table of content CSS selector, in resursive mode
-t, --threads int download concurrency, in recursive mode (default -1) -t, --threads int download concurrency, in recursive mode (default -1)
@@ -97,14 +95,14 @@ go get -u github.com/lapwat/papeer
```sh ```sh
platform=linux # use platform=darwin for MacOS platform=linux # use platform=darwin for MacOS
curl -L https://github.com/lapwat/papeer/releases/download/v0.3.1/papeer-v0.3.1-$platform-amd64 > papeer curl -L https://github.com/lapwat/papeer/releases/download/v0.3.0/papeer-v0.3.0-$platform-amd64 > papeer
chmod +x papeer chmod +x papeer
sudo mv papeer /usr/local/bin sudo mv papeer /usr/local/bin
``` ```
### On Windows ### On Windows
Download [latest release](https://github.com/lapwat/papeer/releases/download/v0.3.1/papeer-v0.3.1-windows-amd64.exe). Download [latest release](https://github.com/lapwat/papeer/releases/download/v0.3.0/papeer-v0.3.0-windows-amd64.exe).
## Install kindlegen to export websites to MOBI (optional) ## Install kindlegen to export websites to MOBI (optional)

View File

@@ -14,34 +14,25 @@ import (
colly "github.com/gocolly/colly/v2" colly "github.com/gocolly/colly/v2"
) )
func NewBookFromURL(url, selector, name, author string, recursive, include bool, limit, offset, delay, threads int) book { func NewBookFromURL(url, selector string, recursive, include, images bool, limit, offset, delay, threads int) book {
var chapters []chapter
var home chapter
if recursive { if recursive {
chapters, home = tableOfContent(url, selector, limit, offset, delay, threads, include) chapters := tableOfContent(url, selector, limit, offset, delay, threads, include, images)
b := New(chapters[0].Name(), chapters[0].Author())
for _, c := range chapters {
b.AddChapter(c)
}
return b
} else { } else {
chapters = []chapter{NewChapterFromURL(url)} c := NewChapterFromURL(url, images)
home = chapters[0] b := New(c.Name(), c.Author())
}
if len(name) == 0 {
name = home.Name()
}
if len(author) == 0 {
author = home.Author()
}
b := New(name, author)
for _, c := range chapters {
b.AddChapter(c) b.AddChapter(c)
return b
} }
return b
} }
func NewChapterFromURL(url string) chapter { func NewChapterFromURL(url string, images bool) chapter {
article, err := readability.FromURL(url, 30*time.Second) article, err := readability.FromURL(url, 30*time.Second)
if err != nil { if err != nil {
log.Fatalf("failed to parse %s, %v\n", url, err) log.Fatalf("failed to parse %s, %v\n", url, err)
@@ -49,31 +40,31 @@ func NewChapterFromURL(url string) chapter {
content := strings.ReplaceAll(article.Content, "\n", "") content := strings.ReplaceAll(article.Content, "\n", "")
// if images { if images {
// // parse html content // parse html content
// doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
// if err != nil { if err != nil {
// log.Fatal(err) log.Fatal(err)
// } }
// // extract images only // extract images only
// content = "" content = ""
// doc.Find("img").Each(func(i int, s *goquery.Selection) { doc.Find("img").Each(func(i int, s *goquery.Selection) {
// newContent, _ := goquery.OuterHtml(s) newContent, _ := goquery.OuterHtml(s)
// content += newContent content += newContent
// }) })
// } }
return chapter{article.Title, article.Byline, content} return chapter{article.Title, article.Byline, content}
} }
func tableOfContent(url, selector string, limit, offset, delay, threads int, include bool) ([]chapter, chapter) { func tableOfContent(url, selector string, limit, offset, delay, threads int, include, images bool) []chapter {
base, err := urllib.Parse(url) base, err := urllib.Parse(url)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
links, home, err := GetLinks(base, selector, limit, offset, include) links, err := GetLinks(base, selector, limit, offset, include)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -91,7 +82,7 @@ func tableOfContent(url, selector string, limit, offset, delay, threads int, inc
log.Fatal(err) log.Fatal(err)
} }
chapters[index] = NewChapterFromURL(u.String()) chapters[index] = NewChapterFromURL(u.String(), images)
progress.Incr(index) progress.Incr(index)
// short sleep for last chapter to let the progress bar update // short sleep for last chapter to let the progress bar update
@@ -125,7 +116,7 @@ func tableOfContent(url, selector string, limit, offset, delay, threads int, inc
log.Fatal(err) log.Fatal(err)
} }
chapters[index] = NewChapterFromURL(u.String()) chapters[index] = NewChapterFromURL(u.String(), images)
progress.Incr(index) progress.Incr(index)
<-semaphore <-semaphore
@@ -133,8 +124,7 @@ func tableOfContent(url, selector string, limit, offset, delay, threads int, inc
} }
wg.Wait() wg.Wait()
} }
return chapters
return chapters, home
} }
func GetPath(elm *goquery.Selection) string { func GetPath(elm *goquery.Selection) string {
@@ -154,7 +144,7 @@ func GetPath(elm *goquery.Selection) string {
return join return join
} }
func GetLinks(url *urllib.URL, selector string, limit, offset int, include bool) ([]link, chapter, error) { func GetLinks(url *urllib.URL, selector string, limit, offset int, include bool) ([]link, error) {
selectorSet := true selectorSet := true
if selector == "" { if selector == "" {
selector = "a" selector = "a"
@@ -192,7 +182,7 @@ func GetLinks(url *urllib.URL, selector string, limit, offset int, include bool)
links := pathLinks[pathMax] links := pathLinks[pathMax]
if len(links) == 0 { if len(links) == 0 {
return []link{}, chapter{}, fmt.Errorf("no link found for selector: %s", selector) return []link{}, fmt.Errorf("no link found for selector: %s", selector)
} }
end := len(links) end := len(links)
@@ -202,12 +192,11 @@ func GetLinks(url *urllib.URL, selector string, limit, offset int, include bool)
links = links[offset:end] links = links[offset:end]
home := NewChapterFromURL(url.String())
if include { if include {
l := NewLink(url.String(), home.Name()) c := NewChapterFromURL(url.String(), false)
l := NewLink(url.String(), c.Name())
links = append([]link{l}, links...) links = append([]link{l}, links...)
} }
return links, home, nil return links, nil
} }

View File

@@ -17,7 +17,7 @@ import (
) )
var recursive, include, images bool var recursive, include, images bool
var format, output, selector, name, author string var format, output, selector string
var limit, offset, delay, threads int var limit, offset, delay, threads int
var getCmd = &cobra.Command{ var getCmd = &cobra.Command{
@@ -77,7 +77,7 @@ var getCmd = &cobra.Command{
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
url := args[0] url := args[0]
b := book.NewBookFromURL(url, selector, name, author, recursive, include, limit, offset, delay, threads) b := book.NewBookFromURL(url, selector, recursive, include, images, limit, offset, delay, threads)
if len(output) == 0 { if len(output) == 0 {
// set default output // set default output
@@ -136,12 +136,6 @@ var getCmd = &cobra.Command{
e.SetAuthor(b.Author()) e.SetAuthor(b.Author())
for _, c := range b.Chapters() { for _, c := range b.Chapters() {
var content string
if images == false {
content = c.Content()
}
// parse content // parse content
doc, err := goquery.NewDocumentFromReader(strings.NewReader(c.Content())) doc, err := goquery.NewDocumentFromReader(strings.NewReader(c.Content()))
if err != nil { if err != nil {
@@ -149,23 +143,20 @@ var getCmd = &cobra.Command{
} }
// retrieve images and download it // retrieve images and download it
contentWithLocalImages := c.Content()
doc.Find("img").Each(func(i int, s *goquery.Selection) { doc.Find("img").Each(func(i int, s *goquery.Selection) {
src, _ := s.Attr("src") src, _ := s.Attr("src")
imagePath, _ := e.AddImage(src, "") imagePath, _ := e.AddImage(src, "")
if images { contentWithLocalImages = strings.ReplaceAll(contentWithLocalImages, src, imagePath)
imageTag, _ := goquery.OuterHtml(s)
content += imageTag
}
content = strings.ReplaceAll(content, src, imagePath)
}) })
html := fmt.Sprintf("<h1>%s</h1>%s", c.Name(), content) html := fmt.Sprintf("<h1>%s</h1>%s", c.Name(), contentWithLocalImages)
_, err = e.AddSection(html, c.Name(), "", "") _, err = e.AddSection(html, c.Name(), "", "")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
err := e.Write(output) err := e.Write(output)
@@ -180,37 +171,8 @@ var getCmd = &cobra.Command{
e := epub.NewEpub(b.Name()) e := epub.NewEpub(b.Name())
e.SetAuthor(b.Author()) e.SetAuthor(b.Author())
for _, c := range b.Chapters() { for _, chapter := range b.Chapters() {
var content string e.AddSection(chapter.Content(), chapter.Name(), "", "")
if images == false {
content = c.Content()
}
// parse content
doc, err := goquery.NewDocumentFromReader(strings.NewReader(c.Content()))
if err != nil {
log.Fatal(err)
}
// retrieve images and download it
doc.Find("img").Each(func(i int, s *goquery.Selection) {
src, _ := s.Attr("src")
imagePath, _ := e.AddImage(src, "")
if images {
imageTag, _ := goquery.OuterHtml(s)
content += imageTag
}
content = strings.ReplaceAll(content, src, imagePath)
})
html := fmt.Sprintf("<h1>%s</h1>%s", c.Name(), content)
_, err = e.AddSection(html, c.Name(), "", "")
if err != nil {
log.Fatal(err)
}
} }
outputEPUB := strings.ReplaceAll(output, ".mobi", ".epub") outputEPUB := strings.ReplaceAll(output, ".mobi", ".epub")
@@ -221,16 +183,16 @@ var getCmd = &cobra.Command{
} }
exec.Command("kindlegen", outputEPUB).Run() exec.Command("kindlegen", outputEPUB).Run()
// exec command always return status 1 even if it succeed // exec command always return status 1 even if it fails
// if err != nil { // if err != nil {
// log.Fatal(err) // log.Fatal(err)
// } // }
fmt.Printf("Ebook saved to \"%s\"\n", output) fmt.Printf("Ebook saved to \"%s\"\n", output)
err = os.Remove(outputEPUB) err2 := os.Remove(outputEPUB)
if err != nil { if err2 != nil {
log.Fatal(err) log.Fatal(err2)
} }
} }
}, },

View File

@@ -27,7 +27,7 @@ var listCmd = &cobra.Command{
log.Fatal(err) log.Fatal(err)
} }
links, _, err := book.GetLinks(base, selector, limit, offset, include) links, err := book.GetLinks(base, selector, limit, offset, include)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@@ -23,10 +23,8 @@ func Execute() {
} }
func init() { func init() {
rootCmd.PersistentFlags().StringVarP(&name, "name", "n", "", "book name (default: page title)")
rootCmd.PersistentFlags().StringVarP(&author, "author", "a", "", "book author")
rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "stdout", "file format [stdout, md, epub, mobi]") rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "stdout", "file format [stdout, md, epub, mobi]")
rootCmd.PersistentFlags().StringVarP(&output, "output", "", "", "file name (default: book name)") rootCmd.PersistentFlags().StringVarP(&output, "output", "", "", "output file")
rootCmd.PersistentFlags().StringVarP(&selector, "selector", "s", "", "table of content CSS selector, in resursive mode") rootCmd.PersistentFlags().StringVarP(&selector, "selector", "s", "", "table of content CSS selector, in resursive mode")
rootCmd.PersistentFlags().BoolVarP(&recursive, "recursive", "r", false, "create one chapter per natigation item") rootCmd.PersistentFlags().BoolVarP(&recursive, "recursive", "r", false, "create one chapter per natigation item")
rootCmd.PersistentFlags().BoolVarP(&include, "include", "i", false, "include URL as first chapter, in resursive mode") rootCmd.PersistentFlags().BoolVarP(&include, "include", "i", false, "include URL as first chapter, in resursive mode")

View File

@@ -14,6 +14,6 @@ var versionCmd = &cobra.Command{
Use: "version", Use: "version",
Short: "Print the version number of papeer", Short: "Print the version number of papeer",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
fmt.Println("papeer v0.3.1") fmt.Println("papeer v0.3.0")
}, },
} }