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