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 = "tmp"
 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
 25	flag.Parse()
 26
 27	if *typeFlag != "mobi" && *typeFlag != "epub" {
 28		log.Fatal("not valid type")
 29	}
 30	err := os.Mkdir(tmpDir, os.ModePerm)
 31	if err != nil {
 32		log.Fatalf("error creating tmp dir %s: %v\n", tmpDir, err)
 33	}
 34
 35	// get link
 36	fmt.Println("\n> getting the link")
 37	resp, err := http.Get(*linkFlag)
 38	if err != nil {
 39		log.Fatalf("failed to download %s: %v\n", *linkFlag, err)
 40	}
 41	defer resp.Body.Close()
 42
 43	// convert the html to simple html with go-readability
 44	article, err := readability.FromReader(resp.Body, *linkFlag)
 45	if err != nil {
 46		log.Fatalf("failed to parse %s: %v\n", *linkFlag, err)
 47	}
 48
 49	fmt.Printf("	URL     : %s\n", *linkFlag)
 50	fmt.Printf("	Title   : %s\n", article.Title)
 51	fmt.Printf("	Author  : %s\n", article.Byline)
 52	fmt.Printf("	Length  : %d\n", article.Length)
 53	fmt.Printf("	Excerpt : %s\n", article.Excerpt)
 54	fmt.Printf("	SiteName: %s\n", article.SiteName)
 55	fmt.Printf("	Image   : %s\n", article.Image)
 56	fmt.Printf("	Favicon : %s\n", article.Favicon)
 57
 58	// get images
 59	fmt.Println("\n>getting the images")
 60	imgRegex := regexp.MustCompile(`(<img )([^>]*)(src=")([^"]*)"`)
 61	imgs := imgRegex.FindAllSubmatch([]byte(article.Content), -1)
 62	for i, img := range imgs {
 63		fmt.Println("	img", i, string(img[4]))
 64		filename, err := downloadImg(string(img[4]), strconv.Itoa(i))
 65		if err != nil {
 66			log.Fatalf("error in downloadImg %s: %v\n", img[4], err)
 67		}
 68
 69		// replace in the article.Content the current img by new filename
 70		article.Content = strings.Replace(article.Content, string(img[4]), filename, -1)
 71	}
 72
 73	// store html file
 74	filename := article.Title + " - " + article.Byline
 75	out, err := os.Create(tmpDir + "/" + filename + ".html")
 76	if err != nil {
 77		log.Fatalf("failed creating index.xhtml: %v\n", err)
 78	}
 79	defer out.Close()
 80
 81	_, err = out.Write([]byte(article.Content))
 82	if err != nil {
 83		log.Fatalf("failed writting index.html: %v\n", err)
 84	}
 85	out.Sync()
 86
 87	// call calibre to convert the html to epub/mobi
 88	fmt.Println("\n>converting to", *typeFlag)
 89	cmd := exec.Command("ebook-convert", tmpDir+"/"+filename+".html", filename+"."+*typeFlag)
 90	if err := cmd.Run(); err != nil {
 91		log.Fatalf("failed converting the html to %s: %v\n", *typeFlag, err)
 92	}
 93
 94	// delete tmp dir
 95	cmd = exec.Command("rm", "-rf", tmpDir)
 96	if err := cmd.Run(); err != nil {
 97		log.Fatalf("failed removing the tmp dir %s: %v\n", tmpDir, err)
 98	}
 99}
100
101func downloadImg(url string, path string) (string, error) {
102	url = strings.Replace(url, "/max/60/", "/max/1000/", -1) // for "medium.com" api
103	resp, err := http.Get(url)
104	if err != nil {
105		return "", err
106	}
107	defer resp.Body.Close()
108	body, err := ioutil.ReadAll(resp.Body)
109	if err != nil {
110		return "", err
111	}
112
113	contentType := http.DetectContentType(body)
114	filename := path + "." + strings.Replace(contentType, "image/", "", -1)
115
116	out, err := os.Create(tmpDir + "/" + filename)
117	if err != nil {
118		return "", err
119	}
120	defer out.Close()
121
122	_, err = out.Write(body)
123	if err != nil {
124		return "", err
125	}
126	out.Sync()
127
128	return filename, nil
129}