arnaucube
arnaucube/link2epubPublic Self-hosted repository
link2epub/main.go
main.goRaw
  1package main
  2
  3import (
  4	"flag"
  5	"fmt"
  6	"io/ioutil"
  7	"log"
  8	"net/http"
  9	"os"
 10	"os/exec"
 11	"regexp"
 12	"strconv"
 13	"strings"
 14
 15	readability "github.com/go-shiori/go-readability"
 16)
 17
 18const tmpDir = "link2epubtmpdir"
 19
 20func main() {
 21	// var typeFlag string
 22	linkFlag := flag.String("l", "", "Link to download")
 23	typeFlag := flag.String("type", "mobi", "Type of epub. Available: mobi (default), epub")
 24	titleFlag := flag.String("title", "", "Title is automatically getted from article, if want to change it, use this flag")
 25
 26	flag.Parse()
 27
 28	if *typeFlag != "mobi" && *typeFlag != "epub" {
 29		log.Fatal("not valid type")
 30	}
 31	err := os.Mkdir(tmpDir, os.ModePerm)
 32	if err != nil {
 33		log.Fatalf("error creating tmp dir %s: %v\n", tmpDir, err)
 34	}
 35
 36	// get link
 37	fmt.Println("\n> getting the link")
 38	resp, err := http.Get(*linkFlag)
 39	if err != nil {
 40		log.Fatalf("failed to download %s: %v\n", *linkFlag, err)
 41	}
 42	defer resp.Body.Close()
 43
 44	// convert the html to simple html with go-readability
 45	article, err := readability.FromReader(resp.Body, *linkFlag)
 46	if err != nil {
 47		log.Fatalf("failed to parse %s: %v\n", *linkFlag, err)
 48	}
 49
 50	fmt.Printf("	URL     : %s\n", *linkFlag)
 51	fmt.Printf("	Title   : %s\n", article.Title)
 52	fmt.Printf("	Author  : %s\n", article.Byline)
 53	fmt.Printf("	Length  : %d\n", article.Length)
 54	fmt.Printf("	Excerpt : %s\n", article.Excerpt)
 55	fmt.Printf("	SiteName: %s\n", article.SiteName)
 56	fmt.Printf("	Image   : %s\n", article.Image)
 57	fmt.Printf("	Favicon : %s\n", article.Favicon)
 58
 59	// get images
 60	fmt.Println("\n>getting the images")
 61	imgRegex := regexp.MustCompile(`(<img )([^>]*)(src=")([^"]*)"`)
 62	imgs := imgRegex.FindAllSubmatch([]byte(article.Content), -1)
 63	for i, img := range imgs {
 64		fmt.Println("	img", i, string(img[4]))
 65		filename, err := downloadImg(string(img[4]), strconv.Itoa(i))
 66		if err != nil {
 67			log.Fatalf("error in downloadImg %s: %v\n", img[4], err)
 68		}
 69
 70		// replace in the article.Content the current img by new filename
 71		article.Content = strings.Replace(article.Content, string(img[4]), filename, -1)
 72	}
 73
 74	if *titleFlag != "" {
 75		article.Title = *titleFlag
 76	}
 77
 78	// add title to content
 79	article.Content = `
 80		<h1>` + article.Title + `</h1>
 81		<h2 style="text-align:right;">` + article.Byline + `</h2>
 82		<br>
 83	` + article.Content
 84
 85	// store html file
 86	filename := article.Title + " - " + article.Byline
 87	out, err := os.Create(tmpDir + "/" + filename + ".html")
 88	if err != nil {
 89		log.Fatalf("failed creating index.xhtml: %v\n", err)
 90	}
 91	defer out.Close()
 92
 93	_, err = out.Write([]byte(article.Content))
 94	if err != nil {
 95		log.Fatalf("failed writting index.html: %v\n", err)
 96	}
 97	out.Sync()
 98
 99	// call calibre to convert the html to epub/mobi
100	fmt.Println("\n>converting to", *typeFlag)
101	cmd := exec.Command("ebook-convert", tmpDir+"/"+filename+".html", filename+"."+*typeFlag)
102	if err := cmd.Run(); err != nil {
103		log.Fatalf("failed converting the html to %s: %v\n", *typeFlag, err)
104	}
105
106	// delete tmp dir
107	cmd = exec.Command("rm", "-rf", tmpDir)
108	if err := cmd.Run(); err != nil {
109		log.Fatalf("failed removing the tmp dir %s: %v\n", tmpDir, err)
110	}
111}
112
113func downloadImg(url string, path string) (string, error) {
114	url = strings.Replace(url, "/max/60/", "/max/1000/", -1) // for "medium.com" api
115	resp, err := http.Get(url)
116	if err != nil {
117		return "", err
118	}
119	defer resp.Body.Close()
120	body, err := ioutil.ReadAll(resp.Body)
121	if err != nil {
122		return "", err
123	}
124
125	contentType := http.DetectContentType(body)
126	filename := path + "." + strings.Replace(contentType, "image/", "", -1)
127
128	out, err := os.Create(tmpDir + "/" + filename)
129	if err != nil {
130		return "", err
131	}
132	defer out.Close()
133
134	_, err = out.Write(body)
135	if err != nil {
136		return "", err
137	}
138	out.Sync()
139
140	return filename, nil
141}