You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
3.9 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "os"
  9. "os/exec"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. readability "github.com/go-shiori/go-readability"
  14. )
  15. const version = "v0_20221002"
  16. const tmpDir = "link2epubtmpdir"
  17. func main() {
  18. versionFlag := flag.Bool("v", false, "version")
  19. linkFlag := flag.String("l", "", "Link to download")
  20. typeFlag := flag.String("type", "mobi", "Type of epub. Available: mobi (default), epub")
  21. titleFlag := flag.String("title", "", "Title is automatically getted from article, if want to change it, use this flag")
  22. flag.Parse()
  23. fmt.Println("link2epub version:", version)
  24. if *versionFlag {
  25. os.Exit(0)
  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.Printf("error creating tmp dir %s: %v\nRemoving it and continuing.", tmpDir, err)
  33. err = os.RemoveAll(tmpDir)
  34. if err != nil {
  35. log.Fatalf("err removing %s: %v\n", tmpDir, err)
  36. }
  37. }
  38. // get link
  39. fmt.Println("\n> getting the link")
  40. resp, err := http.Get(*linkFlag)
  41. if err != nil {
  42. log.Fatalf("failed to download %s: %v\n", *linkFlag, err)
  43. }
  44. defer resp.Body.Close()
  45. // convert the html to simple html with go-readability
  46. article, err := readability.FromReader(resp.Body, *linkFlag)
  47. if err != nil {
  48. log.Fatalf("failed to parse %s: %v\n", *linkFlag, err)
  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. // 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. // replace in the article.Content the current img by new filename
  69. article.Content = strings.Replace(article.Content, string(img[4]), filename, -1)
  70. }
  71. if *titleFlag != "" {
  72. article.Title = *titleFlag
  73. }
  74. // add title to content
  75. article.Content = `
  76. <h1>` + article.Title + `</h1>
  77. <h2 style="text-align:right;">` + article.Byline + `</h2>
  78. <br>
  79. ` + article.Content
  80. // store html file
  81. filename := article.Title + " - " + article.Byline
  82. out, err := os.Create(tmpDir + "/" + filename + ".html")
  83. if err != nil {
  84. log.Fatalf("failed creating index.xhtml: %v\n", err)
  85. }
  86. defer out.Close()
  87. _, err = out.Write([]byte(article.Content))
  88. if err != nil {
  89. log.Fatalf("failed writting index.html: %v\n", err)
  90. }
  91. out.Sync()
  92. // call calibre to convert the html to epub/mobi
  93. fmt.Println("\n>converting to", *typeFlag)
  94. cmd := exec.Command("ebook-convert", tmpDir+"/"+filename+".html", filename+"."+*typeFlag)
  95. if err := cmd.Run(); err != nil {
  96. log.Fatalf("failed converting the html to %s: %v\n", *typeFlag, err)
  97. }
  98. // delete tmp dir
  99. cmd = exec.Command("rm", "-rf", tmpDir)
  100. if err := cmd.Run(); err != nil {
  101. log.Fatalf("failed removing the tmp dir %s: %v\n", tmpDir, err)
  102. }
  103. }
  104. func downloadImg(url string, path string) (string, error) {
  105. url = strings.Replace(url, "/max/60/", "/max/1000/", -1) // for "medium.com" api
  106. resp, err := http.Get(url)
  107. if err != nil {
  108. return "", err
  109. }
  110. defer resp.Body.Close()
  111. body, err := ioutil.ReadAll(resp.Body)
  112. if err != nil {
  113. return "", err
  114. }
  115. contentType := http.DetectContentType(body)
  116. filename := path + "." + strings.Replace(contentType, "image/", "", -1)
  117. out, err := os.Create(tmpDir + "/" + filename)
  118. if err != nil {
  119. return "", err
  120. }
  121. defer out.Close()
  122. _, err = out.Write(body)
  123. if err != nil {
  124. return "", err
  125. }
  126. out.Sync()
  127. return filename, nil
  128. }