Advertisement
gargantuesque

BRUTE

Jun 6th, 2016
338
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 37.30 KB | None | 0 0
  1. /*
  2.  
  3. # brute
  4.  
  5. BRUTE stands for: Bibliotik Retail Uploader Torrent Epubs.
  6. It does not mean anything, but BRUTEs are not known to be particularly
  7. articulate.
  8.  
  9. In short, BRUTE allows uploading a retail epub to Bib from the comfort of the
  10. command line.
  11.  
  12. It's not the prettiest thing, being hacked together and mashed up in a single
  13. file for ease of sharing, but BRUTE will try its best to help make quality
  14. uploads.
  15. This means it will automate the boring stuff, and let the user focus on making
  16. sure the torrent metadata is accurate and complete.
  17.  
  18. BRUTE retrieves metadata from the epub and Goodreads automatically, and forces
  19. the user to merge and edit the results as needed.
  20. Everything is saved to a yaml file, for (optional) manual editing before the
  21. actual upload.
  22.  
  23. ## Building
  24.  
  25. You need a working Go installation: see https://golang.org/doc/install.
  26. Put this file in a 'brute' directory in your $GO_PATH, cd to it and just run:
  27.  
  28.     $ go get .
  29.     $ go install ...brute
  30.  
  31. or if you don't want to install it:
  32.  
  33.     $ go get .
  34.     $ go run brute.go
  35.  
  36. To update its dependancies, rebuild from time to time:
  37.  
  38.     $ go get -u .
  39.     $ go install ...brute
  40.  
  41. BRUTE was tested on Linux, but it should probably work wherever you can install
  42. Go.
  43.  
  44. ## Requirements
  45.  
  46. - a WhatIMG account
  47. - a GoodReads account for which you've requested an API Key: see
  48.   https://www.goodreads.com/api/keys
  49.  
  50. ## Usage
  51.  
  52. BRUTE only requires one existing epub file as argument.
  53.  
  54.     $ brute /path/to/book.epub [/path/to/cover.jpg]
  55.  
  56. It will:
  57.  
  58.  - read epub metadata
  59.  - retrieve information from Goodreads if ISBN is found
  60.  - allow (alright, force) the user to edit the merged information to prepare
  61.    the bibliotik upload form
  62.  - save that information to /path/to/book.yaml for manual editing if deemed
  63.    necessary
  64.  - upload the cover to whatimg and retrieve its url
  65.  
  66. At this point BRUTE will ask if you want to upload directly or not.
  67. If you say no, you will be able to manually edit the yaml file as you wish.
  68. Run BRUTE again with the same argument, and it will load the yaml file with your
  69. changes, and proceed to:
  70.  
  71.  - generate the .torrent from the epub with the user's passkey
  72.  - upload the torrent with the cover and information collected in previous steps
  73.    or loaded from a previously saved yaml file
  74.  - copy the .torrent and .epub where you want to begin seeding
  75.  - archive everything for later reference: epub, cover, torrent, and yaml in a
  76.    tarball.
  77.  - (optionally) remove the original files
  78.  
  79. Conventions:
  80. - if the cover jpg is not given as argument, the script assumes
  81. /path/to/book.jpg exists and is the cover for this epub.
  82. - it will create /path/to/book.torrent and /path/to/book.yaml
  83.  
  84. ## Configuration
  85.  
  86. BRUTE needs a configuration file ("config.yaml") with the following information:
  87.  
  88.     whatimg_login: xx
  89.     whatimg_password: xx
  90.     imgur_client_id: xx
  91.     ptpimg_email: xx
  92.     ptpimg_password: xx
  93.     bibliotik_login: xx
  94.     bibliotik_password: xx
  95.     bibliotik_passkey: https://bibliotik.me/announce.php?passkey=xx
  96.     torrent_watch_directory: /home/user/torrents
  97.     torrent_seed_directory: /home/user/downloads
  98.     archive_directory: /home/user/uploads
  99.     goodreads_api_key: xx
  100.     tag_aliases:
  101.         science-fiction:
  102.         - sf
  103.         - sci-fi
  104.  
  105. You must configure at least whatimg, imgur or ptpimg to upload covers.
  106. If more than one are defined, BRUTE will ask you which to use.
  107.  
  108. You can request an anonymous imgur client_id at: http://imgur.com/register/api_anon
  109. You need to head over PTP and look in the forums if you want to use PTPIMG.
  110. WhatIMG is gone.
  111.  
  112. tag_aliases allows automatic removal of duplicate tags (from duplicate shelves
  113. on Goodreads).
  114.  
  115. This configuration file must be in the working directory.
  116. Lots of information in a clear text file, I know.
  117. Keep it close to your heart.
  118.  
  119. ## Things BRUTE isn't good at
  120.  
  121. BRUTE is not good at:
  122. - retrieving information when it cannot find an ISBN number or if Goodreads
  123. does not know about an ISBN
  124. - retrieving editors, contributors, translators, languages (defaults to english)
  125. - it will try to extract tags from Goodreads shelves, and clean out the most
  126. blatantly awful ones. ALWAYS EDIT TAGS MANUALLY.
  127.  
  128. Fortunately, you can overcome these shortcomings by manually editing
  129. /path/to/book.yaml!
  130. No excuses!
  131.  
  132. ## Things BRUTE is useless at
  133.  
  134. BRUTE can't help you if you're dealing with anything other than epubs.
  135.  
  136. You could use it to upload non-retail epubs, but then you MUST manually set the
  137. RetailField to "0" in the yaml file.
  138.  
  139. ## Changelog
  140.  
  141. v8: Updated to work with latest version of third party packages.
  142. v7: Added support for PTPIMG. Irrationally reluctant to remove WhatIMG support for now.
  143. v6: Added support for imgur (anonymous uploads) now that WhatIMG is down :(
  144. v5: Quick search for duplicates before uploading; code cleanups.
  145. v4: Menus have changed a little; updated to work with latest version of third
  146. party packages.
  147. v3: ask for ISBN if none found; better info if upload form submission fails and
  148. cleanup of generated torrent file; option to upload anonymously; a jpg file can
  149. be given as second parameter, to be used as a cover.
  150. v2: Updated to work with latest version of third party packages, added fetching
  151. cover from GR and asking the user which version to upload.
  152. v1.0.6: Updated to work with latest version of third party packages, and fixed
  153. bug with publishers containing ",". Added possibility to directly edit the yaml
  154. file, using $EDITOR (falling back to nano). When merging epub and GR metadata,
  155. it is safe to ignore the 'type' field.
  156. v1.0.5: Updated to work with latest version of third party packages.
  157. v1.0.4: Updated to work with latest version of third party packages.
  158. v1.0.3: Updated to work with latest version of third party packages.
  159. v1.0.2: In description, title is in italics and author name in bold.
  160. v1.0.1: BRUTE should now correctly retrieve edition year instead of original
  161. publication year from GR.
  162. v1.0: original release.
  163.  
  164. */
  165.  
  166. package main
  167.  
  168. import (
  169.     "bytes"
  170.     "errors"
  171.     "fmt"
  172.     "io"
  173.     "io/ioutil"
  174.     "log"
  175.     "mime/multipart"
  176.     "net/http"
  177.     "net/http/cookiejar"
  178.     "net/url"
  179.     "os"
  180.     "path/filepath"
  181.     "regexp"
  182.     "strings"
  183.     "time"
  184.  
  185.     "github.com/PuerkitoBio/goquery"
  186.     "github.com/anacrolix/torrent/bencode"
  187.     "github.com/anacrolix/torrent/metainfo"
  188.     b "github.com/barsanuphe/endive/book"
  189.     e "github.com/barsanuphe/endive/endive"
  190.     u "github.com/barsanuphe/helpers/ui"
  191.     h "github.com/barsanuphe/helpers"
  192.     "github.com/jhoonb/archivex"
  193.     "github.com/skratchdot/open-golang/open"
  194.     "github.com/spf13/viper"
  195.     "golang.org/x/net/publicsuffix"
  196.     "gopkg.in/yaml.v2"
  197.     "github.com/mattn/go-scan"
  198.     "encoding/json"
  199. )
  200.  
  201. const (
  202.     BRUTE   = "BRUTE"
  203.     BibRoot = "https://bibliotik.me"
  204.     ptpimgURL = "https://ptpimg.me/"
  205.  
  206.     EPUBFormat      = "15"
  207.     EnglishLanguage = "1"
  208.  
  209.     EPUBExtension = ".epub"
  210.     JPGExtension  = ".jpg"
  211.     Usage         = "Usage: brute book.epub [cover.jpg]"
  212.  
  213.     UploadImageAgain                 = "Image URL found, upload again"
  214.     UploadEPUBCover                  = "Upload epub cover"
  215.     UploadAnonymously                = "Do you want the upload to be anonymous"
  216.     UseExistingYAML                  = "Info file already exists! Load"
  217.     AllInfoRetrievedLastChanceToEdit = "\nAll relevant information has been retrieved.\n" +
  218.         "If you are satisfied everything is perfect, you can upload to bibliotik directly.\n" +
  219.         "If unsure, it is not too late to fix things manually.\n"
  220.     FullManualEdit    = "Do you want to manually edit the YAML file before uploading?"
  221.     SearchingForDupes = "\nNow checking if the book you want to upload already exists on bibliotik.\n" +
  222.         "If something is found, check the links to make sure you are not uploading a duplicate" +
  223.         "(check the official rules about what is and what is not considered a dupe).\n" +
  224.         "Search uses exact author and title only, it may not find relevant books if your information " +
  225.         "is different from existing books on bibliotik, or show different editions.\n"
  226.     ConfirmNotADupe   = "After checking, are you certain you can upload this book"
  227.     ConfirmUpload     = "Upload to bibliotik"
  228.     GRCoverDownloaded = "\nThe Goodreads cover was downloaded alongside the epub cover.\n" +
  229.         "Please take a moment to choose the best version.\n"
  230.  
  231.     ErrorDirectoryDoesNotExist = "%s directory does not exist"
  232.     ErrorCouldNotLogIn         = "Could not login"
  233.     ErrorUploadFailed          = "Upload failed. Check the yaml file for incorrect or forbidden information and try again."
  234.     ErrorAddingToTar           = "Error adding %s"
  235. )
  236.  
  237. func main() {
  238.     var ui u.UserInterface
  239.     var epubFilename, coverFilename string
  240.     ui = &u.UI{}
  241.  
  242.     // 1. load configuration
  243.     cfg := Config{}
  244.     if err := cfg.load("config.yaml"); err != nil {
  245.         fmt.Println("Error loading config.")
  246.         return
  247.     }
  248.     if err := cfg.check(); err != nil {
  249.         fmt.Println("Error checking config: " + err.Error())
  250.         return
  251.     }
  252.     // 2. check arguments
  253.     switch len(os.Args) {
  254.     case 2:
  255.         epubFilename = os.Args[1]
  256.         coverFilename = strings.TrimSuffix(os.Args[1], EPUBExtension) + JPGExtension
  257.     case 3:
  258.         epubFilename = os.Args[1]
  259.         coverFilename = os.Args[2]
  260.     default:
  261.         ui.Error("Incorrect input!")
  262.         ui.Info(Usage)
  263.         return
  264.     }
  265.     c := UploadCandidate{
  266.         epubFile:    epubFilename,
  267.         torrentFile: strings.TrimSuffix(epubFilename, EPUBExtension) + ".torrent",
  268.         imageFile:   coverFilename,
  269.         imageFileGR: strings.TrimSuffix(coverFilename, JPGExtension) + "_gr" + JPGExtension,
  270.         infoFile:    strings.TrimSuffix(epubFilename, EPUBExtension) + ".yaml",
  271.     }
  272.     if err := c.check(); err != nil {
  273.         fmt.Println(err.Error())
  274.         return
  275.     }
  276.     // 3. get info from file + GR
  277.     if err := c.getInfo(cfg, ui); err != nil {
  278.         ui.Error(err.Error())
  279.         return
  280.     }
  281.     // 4. upload cover if necessary
  282.     if c.info.ImageField == "" || ui.Accept(UploadImageAgain) {
  283.         // retrieve GR cover
  284.         coverToUpload := c.imageFile
  285.         if err := c.downloadCover(); err != nil {
  286.             ui.Error(err.Error())
  287.             ui.Info("Could not download cover from GR, uploading epub version.")
  288.         } else {
  289.             ui.Title(GRCoverDownloaded)
  290.             if ui.Accept(UploadEPUBCover) {
  291.                 ui.Info("Uploading epub cover.")
  292.             } else {
  293.                 ui.Info("Uploading GR cover.")
  294.                 coverToUpload = c.imageFileGR
  295.             }
  296.  
  297.         }
  298.         whatimg, imgur, ptpimg := cfg.knownCoverHosts()
  299.         var uploadErr error
  300.         var uploaded bool
  301.         if !uploaded && whatimg && ui.Accept("Upload cover with WhatIMG?") {
  302.             uploadErr = c.uploadCoverToWhatIMG(coverToUpload, cfg)
  303.             uploaded = (uploadErr == nil)
  304.         }
  305.         if !uploaded && ptpimg && ui.Accept("Upload cover with PTPIMG?") {
  306.             uploadErr = c.uploadCoverToPTPIMG(coverToUpload, cfg)
  307.             uploaded = (uploadErr == nil)
  308.         }
  309.         if !uploaded && imgur && ui.Accept("Upload cover with Imgur?") {
  310.             uploadErr = c.uploadCoverToImgur(coverToUpload, cfg)
  311.             uploaded = (uploadErr == nil)
  312.         }
  313.         if uploadErr != nil {
  314.             fmt.Println(uploadErr.Error())
  315.             return
  316.         }
  317.     }
  318.     // 5. save info
  319.     if ui.Accept(UploadAnonymously) {
  320.         c.makeAnonymous()
  321.     }
  322.     err := c.saveInfo()
  323.     if err != nil {
  324.         fmt.Println(err.Error())
  325.     }
  326.     // 6. manual edit
  327.     ui.Title(AllInfoRetrievedLastChanceToEdit)
  328.     if ui.Accept(FullManualEdit) {
  329.         accepted := false
  330.         for !accepted {
  331.             if err := c.manualEdit(ui); err == nil {
  332.                 if ui.Accept("Confirm all fields are awesome") {
  333.                     accepted = true
  334.                 }
  335.             }
  336.         }
  337.     }
  338.     // 7. check bib for existing uploads
  339.     client, err := c.loginBib(cfg)
  340.     if err != nil {
  341.         ui.Error(err.Error())
  342.         return
  343.     }
  344.     ui.Title(SearchingForDupes)
  345.     foundExisting, err := c.checkBib(client)
  346.     if err != nil {
  347.         ui.Error(err.Error())
  348.         return
  349.     }
  350.     if foundExisting {
  351.         if !ui.Accept(ConfirmNotADupe) {
  352.             ui.Info("Stopping everything.")
  353.             return
  354.         }
  355.     } else {
  356.         ui.Info("Nothing found.")
  357.     }
  358.  
  359.     if ui.Accept(ConfirmUpload) {
  360.         // 8. generate torrent
  361.         if err := c.generateTorrent(cfg); err != nil {
  362.             ui.Error(err.Error())
  363.             return
  364.         }
  365.         // 9. upload to bibliotik
  366.         if err := c.uploadTorrent(cfg, client); err != nil {
  367.             ui.Error(err.Error())
  368.             if err := os.Remove(c.torrentFile); err != nil {
  369.                 ui.Error("Error cleaning torrent file")
  370.             }
  371.             return
  372.         }
  373.         // 10. copy torrent to watch dir && epub to incoming
  374.         if err := c.seed(cfg); err != nil {
  375.             ui.Error(err.Error())
  376.             return
  377.         }
  378.         // 11. backup all relevant files to zip with date
  379.         if err := c.archive(cfg); err != nil {
  380.             ui.Error(err.Error())
  381.             return
  382.         }
  383.         // 12. clean up
  384.         if ui.Accept("Remove original files") {
  385.             if err := c.cleanUp(); err != nil {
  386.                 ui.Error(err.Error())
  387.                 return
  388.             }
  389.         }
  390.     } else if ui.Accept("Launch this book's Goodreads page in your browser for reference?") {
  391.         if err := open.Start("https://www.goodreads.com/search?q=" + c.info.IsbnField); err != nil {
  392.             ui.Error("Could not open goodreads page!")
  393.         }
  394.     }
  395. }
  396.  
  397. //------------------------------------------------------------------------------
  398.  
  399. type Config struct {
  400.     bibUser         string
  401.     bibPassword     string
  402.     bibPasskey      string
  403.     whatimgUser     string
  404.     whatimgPassword string
  405.     ptpimgEmail    string
  406.     ptpimgPassword string
  407.     imgurClientID   string
  408.     torrentWatchDir string
  409.     torrentSeedDir  string
  410.     archiveDir      string
  411.     grApiKey        string
  412.     TagAliases      map[string][]string
  413. }
  414.  
  415. func (c *Config) load(path string) (err error) {
  416.     conf := viper.New()
  417.     conf.SetConfigType("yaml")
  418.     conf.SetConfigFile(path)
  419.  
  420.     err = conf.ReadInConfig()
  421.     if err != nil {
  422.         return
  423.     }
  424.     c.bibUser = conf.GetString("bibliotik_login")
  425.     c.bibPassword = conf.GetString("bibliotik_password")
  426.     c.bibPasskey = conf.GetString("bibliotik_passkey")
  427.     c.whatimgUser = conf.GetString("whatimg_login")
  428.     c.whatimgPassword = conf.GetString("whatimg_password")
  429.     c.ptpimgEmail = conf.GetString("ptpimg_email")
  430.     c.ptpimgPassword = conf.GetString("ptpimg_password")
  431.     c.imgurClientID = conf.GetString("imgur_client_id")
  432.     c.torrentSeedDir = conf.GetString("torrent_seed_directory")
  433.     c.torrentWatchDir = conf.GetString("torrent_watch_directory")
  434.     c.archiveDir = conf.GetString("archive_directory")
  435.     c.grApiKey = conf.GetString("goodreads_api_key")
  436.     c.TagAliases = conf.GetStringMapStringSlice("tag_aliases")
  437.     return
  438. }
  439.  
  440. func (c *Config) check() error {
  441.     if !h.DirectoryExists(c.torrentWatchDir) {
  442.         return fmt.Errorf(ErrorDirectoryDoesNotExist, "Torrent watch")
  443.     }
  444.     if !h.DirectoryExists(c.torrentSeedDir) {
  445.         return fmt.Errorf(ErrorDirectoryDoesNotExist, "Torrent download")
  446.     }
  447.     if !h.DirectoryExists(c.archiveDir) {
  448.         return fmt.Errorf(ErrorDirectoryDoesNotExist, "Archive")
  449.     }
  450.     if whatimg, imgur, ptpimg := c.knownCoverHosts(); !whatimg && !imgur && !ptpimg {
  451.         return errors.New("Image hosting service must be configured to upload covers")
  452.     }
  453.     return nil
  454. }
  455.  
  456. func (c *Config) knownCoverHosts() (whatimg, imgur, ptpimg bool) {
  457.     if c.whatimgPassword != "" && c.whatimgUser != "" {
  458.         whatimg = true
  459.     }
  460.     if c.ptpimgPassword != "" && c.ptpimgEmail != "" {
  461.         ptpimg = true
  462.     }
  463.     if c.imgurClientID != "" {
  464.         imgur = true
  465.     }
  466.     return
  467. }
  468.  
  469. //------------------------------------------------------------------------------
  470.  
  471. // newTrue allows setting the value of a *bool in a struct.
  472. // it is arguably awful.
  473. func newTrue() *bool {
  474.     b := true
  475.     return &b
  476. }
  477.  
  478. func checkErrors(errs ...error) error {
  479.     for _, err := range errs {
  480.         if err != nil {
  481.             return err
  482.         }
  483.     }
  484.     return nil
  485. }
  486.  
  487. func login(siteUrl, username, password string) (hc *http.Client, returnData string, err error) {
  488.     form := url.Values{}
  489.     form.Add("username", username)
  490.     form.Add("password", password)
  491.     req, err := http.NewRequest("POST", siteUrl, strings.NewReader(form.Encode()))
  492.     if err != nil {
  493.         fmt.Println(err.Error())
  494.     }
  495.     req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
  496.  
  497.     options := cookiejar.Options{
  498.         PublicSuffixList: publicsuffix.List,
  499.     }
  500.     jar, err := cookiejar.New(&options)
  501.     if err != nil {
  502.         log.Fatal(err)
  503.     }
  504.     hc = &http.Client{Jar: jar}
  505.  
  506.     resp, err := hc.Do(req)
  507.     if err != nil {
  508.         fmt.Println(err.Error())
  509.     }
  510.     defer resp.Body.Close()
  511.  
  512.     if resp.StatusCode != http.StatusOK {
  513.         err = errors.New("Returned status: " + resp.Status)
  514.     }
  515.  
  516.     data, err := ioutil.ReadAll(resp.Body)
  517.     if err != nil {
  518.         log.Fatal(err)
  519.     }
  520.     returnData = string(data)
  521.     return
  522. }
  523.  
  524. func uploadImage(client *http.Client, uploadUrl, image string) (responseData string, err error) {
  525.     // preparing a form
  526.     b := new(bytes.Buffer)
  527.     w := multipart.NewWriter(b)
  528.     // adding image to form
  529.     f, err := os.Open(image)
  530.     if err != nil {
  531.         return "", err
  532.     }
  533.     defer f.Close()
  534.     fw, err := w.CreateFormFile("userfile[]", filepath.Base(image))
  535.     if err != nil {
  536.         return "", err
  537.     }
  538.     if _, err = io.Copy(fw, f); err != nil {
  539.         return
  540.     }
  541.     if err = w.WriteField("upload_to", "0"); err != nil {
  542.         return
  543.     }
  544.     if err = w.WriteField("private_upload", "1"); err != nil {
  545.         return
  546.     }
  547.     if err = w.WriteField("upload_type", "standard"); err != nil {
  548.         return
  549.     }
  550.     w.Close()
  551.  
  552.     req, err := http.NewRequest("POST", uploadUrl, b)
  553.     if err != nil {
  554.         return "", err
  555.     }
  556.     req.Header.Set("Content-Type", w.FormDataContentType())
  557.  
  558.     resp, err := client.Do(req)
  559.     if err != nil {
  560.         return "", err
  561.     }
  562.     defer resp.Body.Close()
  563.  
  564.     if resp.StatusCode != http.StatusOK {
  565.         err = errors.New("Returned status: " + resp.Status)
  566.     }
  567.  
  568.     data, err := ioutil.ReadAll(resp.Body)
  569.     if err != nil {
  570.         return "", err
  571.     }
  572.     responseData = string(data)
  573.     return
  574. }
  575.  
  576. func loginPTPImg(siteUrl, username, password string) (hc *http.Client, returnData string, err error) {
  577.     form := url.Values{}
  578.     form.Add("email", username)
  579.     form.Add("pass", password)
  580.     req, err := http.NewRequest("POST", siteUrl, strings.NewReader(form.Encode()))
  581.     if err != nil {
  582.         fmt.Println(err.Error())
  583.     }
  584.     req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
  585.  
  586.     options := cookiejar.Options{
  587.         PublicSuffixList: publicsuffix.List,
  588.     }
  589.     jar, err := cookiejar.New(&options)
  590.     if err != nil {
  591.         log.Fatal(err)
  592.     }
  593.     hc = &http.Client{Jar: jar}
  594.  
  595.     resp, err := hc.Do(req)
  596.     if err != nil {
  597.         fmt.Println(err.Error())
  598.     }
  599.     defer resp.Body.Close()
  600.  
  601.     if resp.StatusCode != http.StatusOK {
  602.         err = errors.New("Returned status: " + resp.Status)
  603.     }
  604.  
  605.     data, err := ioutil.ReadAll(resp.Body)
  606.     if err != nil {
  607.         log.Fatal(err)
  608.     }
  609.     returnData = string(data)
  610.     return
  611. }
  612.  
  613. func uploadImageToPTPIMG(client *http.Client, uploadUrl, image, apiKey string) (link string, err error) {
  614.     // preparing a form
  615.     b := new(bytes.Buffer)
  616.     w := multipart.NewWriter(b)
  617.     // adding image to form
  618.     f, err := os.Open(image)
  619.     if err != nil {
  620.         return "", err
  621.     }
  622.     defer f.Close()
  623.     fw, err := w.CreateFormFile("file-upload[]", filepath.Base(image))
  624.     if err != nil {
  625.         return "", err
  626.     }
  627.     if _, err = io.Copy(fw, f); err != nil {
  628.         return
  629.     }
  630.     if err = w.WriteField("api_key", apiKey); err != nil {
  631.         return
  632.     }
  633.     w.Close()
  634.  
  635.     req, err := http.NewRequest("POST", uploadUrl, b)
  636.     if err != nil {
  637.         return "", err
  638.     }
  639.     req.Header.Set("Content-Type", w.FormDataContentType())
  640.  
  641.     resp, err := client.Do(req)
  642.     if err != nil {
  643.         return "", err
  644.     }
  645.     defer resp.Body.Close()
  646.  
  647.     if resp.StatusCode != http.StatusOK {
  648.         err = errors.New("Returned status: " + resp.Status)
  649.     }
  650.  
  651.     data, err := ioutil.ReadAll(resp.Body)
  652.     if err != nil {
  653.         log.Fatal(err)
  654.     }
  655.     ptpimgJSON := []map[string]string{}
  656.     if err := json.Unmarshal(data, &ptpimgJSON); err != nil {
  657.         return "", err
  658.     }
  659.     link = fmt.Sprintf("%s%s.%s", ptpimgURL, ptpimgJSON[0]["code"], ptpimgJSON[0]["ext"])
  660.     return
  661. }
  662.  
  663.  
  664. //------------------------------------------------------------------------------
  665.  
  666. // BUInfo tracks all of the upload form fields.
  667. type BUInfo struct {
  668.     TorrentFileField  string
  669.     authkey           string
  670.     upload            string // default: empty
  671.     TitleField        string
  672.     EditorsField      string
  673.     ContributorsField string
  674.     TranslatorsField  string
  675.     PublishersField   string
  676.     PagesField        string
  677.     AuthorsField      string
  678.     FormatField       string // EPUB == "15"
  679.     IsbnField         string
  680.     TagsField         string
  681.     DescriptionField  string
  682.     RetailField       string // retail == "1"
  683.     NotifyField       string // default "1"
  684.     LanguageField     string // english == "1"
  685.     YearField         string
  686.     ImageField        string
  687.     AnonymousField    string // default "0"
  688. }
  689.  
  690. // ShowInfo returns a table with relevant information about a book.
  691. func (i *BUInfo) ShowInfo() string {
  692.     var rows [][]string
  693.     rows = append(rows, []string{"TorrentFile", i.TorrentFileField})
  694.     rows = append(rows, []string{"Title", i.TitleField})
  695.     rows = append(rows, []string{"Authors", i.AuthorsField})
  696.     rows = append(rows, []string{"Editors", i.EditorsField})
  697.     rows = append(rows, []string{"Contributors", i.ContributorsField})
  698.     rows = append(rows, []string{"Translators", i.TranslatorsField})
  699.     rows = append(rows, []string{"Publishers", i.PublishersField})
  700.     rows = append(rows, []string{"Isbn", i.IsbnField})
  701.     rows = append(rows, []string{"Pages", i.PagesField})
  702.     rows = append(rows, []string{"Year", i.YearField})
  703.     rows = append(rows, []string{"Format", i.FormatField})
  704.     rows = append(rows, []string{"Language", i.LanguageField})
  705.     rows = append(rows, []string{"Retail", i.RetailField})
  706.     rows = append(rows, []string{"Tags", i.TagsField})
  707.     rows = append(rows, []string{"Image", i.ImageField})
  708.     rows = append(rows, []string{"Description", i.DescriptionField})
  709.     rows = append(rows, []string{"Notify", i.NotifyField})
  710.     rows = append(rows, []string{"Anonymous", i.AnonymousField})
  711.     return e.TabulateRows(rows, "Info", "Book")
  712. }
  713.  
  714. func (i *BUInfo) load(path string) (err error) {
  715.     data, err := ioutil.ReadFile(path)
  716.     if err != nil {
  717.         return
  718.     }
  719.     return yaml.Unmarshal(data, i)
  720. }
  721.  
  722. func (i *BUInfo) save(path string) (err error) {
  723.     d, err := yaml.Marshal(i)
  724.     if err != nil {
  725.         return err
  726.     }
  727.     return ioutil.WriteFile(path, d, 0777)
  728. }
  729.  
  730. func (i *BUInfo) fill(m b.Metadata, torrentPath string) {
  731.     i.TorrentFileField = torrentPath
  732.     i.authkey = ""
  733.     i.upload = ""
  734.     if m.HasAny() {
  735.         seriesInfo := ""
  736.         if m.Series.HasAny() {
  737.             for i := range m.Series {
  738.                 seriesInfo += fmt.Sprintf(" (%s, Book %s)", m.Series[i].Name, m.Series[i].Position)
  739.             }
  740.         }
  741.         i.TitleField = m.Title() + seriesInfo
  742.         i.EditorsField = ""      // TODO
  743.         i.ContributorsField = "" // TODO
  744.         i.TranslatorsField = ""  // TODO
  745.         // some publishers in the form 'X, Inc' make the upload form angry
  746.         i.PublishersField = strings.Replace(m.Publisher, ",", " ", -1)
  747.         i.PagesField = m.NumPages
  748.         i.AuthorsField = strings.Join(m.Authors, ", ")
  749.         i.FormatField = EPUBFormat
  750.         i.IsbnField = m.ISBN
  751.         i.TagsField = m.Category + ", " + m.Tags.String()
  752.  
  753.         r := strings.NewReplacer(
  754.             i.AuthorsField, "[b]"+i.AuthorsField+"[/b]",
  755.             m.Title(), "[i]"+m.Title()+"[/i]",
  756.         )
  757.         i.DescriptionField = r.Replace(m.Description)
  758.         i.RetailField = "1"
  759.         i.NotifyField = "1"
  760.         i.AnonymousField = "0"
  761.         i.LanguageField = EnglishLanguage
  762.         i.YearField = m.EditionYear
  763.     }
  764. }
  765.  
  766. func (i *BUInfo) generateUploadRequest(uploadURL string) (req *http.Request, err error) {
  767.     // setting up the form
  768.     b := new(bytes.Buffer)
  769.     w := multipart.NewWriter(b)
  770.     // adding the torrent file
  771.     f, err := os.Open(i.TorrentFileField)
  772.     if err != nil {
  773.         return nil, err
  774.     }
  775.     defer f.Close()
  776.     fw, err := w.CreateFormFile("TorrentFileField", filepath.Base(i.TorrentFileField))
  777.     if err != nil {
  778.         return nil, err
  779.     }
  780.     if _, err = io.Copy(fw, f); err != nil {
  781.         return
  782.     }
  783.     errors := []error{}
  784.     errors = append(errors, w.WriteField("authkey", i.authkey))
  785.     errors = append(errors, w.WriteField("upload", i.upload))
  786.     errors = append(errors, w.WriteField("TitleField", i.TitleField))
  787.     errors = append(errors, w.WriteField("EditorsField", i.EditorsField))
  788.     errors = append(errors, w.WriteField("ContributorsField", i.ContributorsField))
  789.     errors = append(errors, w.WriteField("TranslatorsField", i.TranslatorsField))
  790.     errors = append(errors, w.WriteField("PublishersField", i.PublishersField))
  791.     errors = append(errors, w.WriteField("PagesField", i.PagesField))
  792.     errors = append(errors, w.WriteField("AuthorsField", i.AuthorsField))
  793.     errors = append(errors, w.WriteField("FormatField", i.FormatField))
  794.     errors = append(errors, w.WriteField("IsbnField", i.IsbnField))
  795.     errors = append(errors, w.WriteField("TagsField", i.TagsField))
  796.     errors = append(errors, w.WriteField("DescriptionField", i.DescriptionField))
  797.     errors = append(errors, w.WriteField("RetailField", i.RetailField))
  798.     errors = append(errors, w.WriteField("NotifyField", i.NotifyField))
  799.     errors = append(errors, w.WriteField("LanguageField", i.LanguageField))
  800.     errors = append(errors, w.WriteField("YearField", i.YearField))
  801.     errors = append(errors, w.WriteField("ImageField", i.ImageField))
  802.     errors = append(errors, w.WriteField("AnonymousField", i.AnonymousField))
  803.     if err := checkErrors(errors...); err != nil {
  804.         return nil, err
  805.     }
  806.     w.Close()
  807.  
  808.     req, err = http.NewRequest("POST", uploadURL, b)
  809.     if err != nil {
  810.         return nil, err
  811.     }
  812.     req.Header.Set("Content-Type", w.FormDataContentType())
  813.     return
  814. }
  815.  
  816. //------------------------------------------------------------------------------
  817.  
  818. type UploadCandidate struct {
  819.     epubFile    string
  820.     torrentFile string
  821.     imageFile   string
  822.     imageFileGR string
  823.     imageURLGR  string
  824.     infoFile    string
  825.     info        BUInfo
  826. }
  827.  
  828. func (t *UploadCandidate) loadInfo() error {
  829.     return t.info.load(t.infoFile)
  830. }
  831.  
  832. func (t *UploadCandidate) saveInfo() error {
  833.     fmt.Println("Saving all gathered information to " + t.infoFile)
  834.     return t.info.save(t.infoFile)
  835. }
  836.  
  837. func (t *UploadCandidate) makeAnonymous() {
  838.     t.info.AnonymousField = "1"
  839. }
  840.  
  841. func (t *UploadCandidate) manualEdit(ui u.UserInterface) error {
  842.     data, err := ioutil.ReadFile(t.infoFile)
  843.     if err != nil {
  844.         return err
  845.     }
  846.     if output, err := ui.Edit(string(data)); err == nil {
  847.         if err := ioutil.WriteFile(t.infoFile, []byte(output), 0777); err != nil {
  848.             ui.Error("error writing file")
  849.             return err
  850.         }
  851.     }
  852.     if err := t.loadInfo(); err != nil {
  853.         return err
  854.     }
  855.     fmt.Println(t.info.ShowInfo())
  856.     return nil
  857. }
  858.  
  859. func (t *UploadCandidate) check() error {
  860.     //  assert epub exists
  861.     if _, err := h.FileExists(t.epubFile); err != nil {
  862.         return errors.New("Epub does not exist!")
  863.     }
  864.     // assert it's an epub
  865.     if !strings.HasSuffix(strings.ToLower(t.epubFile), EPUBExtension) {
  866.         return errors.New(t.epubFile + " is not an epub")
  867.     }
  868.     // assert jpg exists
  869.     if _, err := h.FileExists(t.imageFile); err != nil {
  870.         return errors.New("Cover " + t.imageFile + " does not exist!")
  871.     }
  872.     // assert it's a jpg
  873.     if !strings.HasSuffix(strings.ToLower(t.imageFile), JPGExtension) {
  874.         return errors.New(t.imageFile + " is not an jpg")
  875.     }
  876.     //  assert t does not exist yet
  877.     if _, err := h.FileExists(t.torrentFile); err == nil {
  878.         return errors.New("Torrent " + t.torrentFile + " should not exist!")
  879.     }
  880.     return nil
  881. }
  882.  
  883. func (t *UploadCandidate) getInfo(cfg Config, ui u.UserInterface) (err error) {
  884.     loadedFromFile := false
  885.     if _, err := h.FileExists(t.infoFile); err == nil {
  886.         if ui.Accept(UseExistingYAML) {
  887.             if err := t.loadInfo(); err != nil {
  888.                 return err
  889.             }
  890.             fmt.Println(t.info.ShowInfo())
  891.             if !ui.Accept("Confirm") {
  892.                 return errors.New("Incorrect book information rejected.")
  893.             } else {
  894.                 loadedFromFile = true
  895.             }
  896.         }
  897.     }
  898.     if !loadedFromFile {
  899.         bk := b.NewBook(ui, 0, t.epubFile, e.Config{GoodReadsAPIKey: cfg.grApiKey, TagAliases: cfg.TagAliases}, true)
  900.         // read epub metadata
  901.         bk.Metadata, err = bk.RetailEpub.ReadMetadata()
  902.         if err != nil {
  903.             if err.Error() == "ISBN not found in epub" {
  904.                 isbn, isbnInputErr := e.AskForISBN(ui)
  905.                 if isbnInputErr != nil {
  906.                     return err
  907.                 }
  908.                 bk.Metadata.ISBN = isbn
  909.             } else {
  910.                 return err
  911.             }
  912.         }
  913.         // find GoodReads metadata and merge
  914.         fields := []string{"author", "title", "edition_year", "publisher", "description", "category", "tags", "series", "isbn"}
  915.         err = bk.Metadata.SearchOnline(ui, bk.Config, fields...)
  916.         if err != nil {
  917.             return err
  918.         }
  919.         t.imageURLGR = bk.Metadata.ImageURL
  920.         t.info.fill(bk.Metadata, t.torrentFile)
  921.         fmt.Println(t.info.ShowInfo())
  922.         // review/edit field by field, confirm
  923.         for !ui.Accept("Confirm") {
  924.             for _, f := range fields {
  925.                 if err := bk.EditField(f); err != nil {
  926.                     fmt.Println("Could not assign new value to field " + f + ", continuing.")
  927.                 }
  928.             }
  929.             t.info.fill(bk.Metadata, t.torrentFile)
  930.             fmt.Println(t.info.ShowInfo())
  931.         }
  932.     }
  933.     return
  934. }
  935.  
  936. func (t *UploadCandidate) downloadCover() error {
  937.     if t.imageURLGR == "" {
  938.         return errors.New("unknown image url")
  939.     }
  940.     if _, err := h.FileExists(t.imageFileGR); err == nil {
  941.         // already downloaded
  942.         return nil
  943.     }
  944.     response, err := http.Get(t.imageURLGR)
  945.     if err != nil {
  946.         return err
  947.     }
  948.     defer response.Body.Close()
  949.     file, err := os.Create(t.imageFileGR)
  950.     if err != nil {
  951.         return err
  952.     }
  953.     defer file.Close()
  954.     _, err = io.Copy(file, response.Body)
  955.     return err
  956. }
  957.  
  958.  
  959. func (t *UploadCandidate) uploadCoverToPTPIMG(cover string, cfg Config) error {
  960.     // login
  961.     hc, data, err := loginPTPImg(ptpimgURL + "login.php?1", cfg.ptpimgEmail, cfg.ptpimgPassword)
  962.     if err != nil {
  963.         return err
  964.     }
  965.     // extract api_key
  966.     var apiKey string
  967.     r := regexp.MustCompile(`name='api_key' value='(.*)'`)
  968.     if r.MatchString(data) {
  969.         apiKey = r.FindStringSubmatch(data)[1]
  970.     } else {
  971.         return errors.New("Could not find apî_key: " + ErrorCouldNotLogIn)
  972.     }
  973.  
  974.     // upload image and get link
  975.     fmt.Printf("Uploading " + filepath.Base(cover) + " to PTPIMG... ")
  976.     link, err := uploadImageToPTPIMG(hc, ptpimgURL + "upload.php", cover, apiKey)
  977.     if err != nil {
  978.         fmt.Println("KO")
  979.         fmt.Println(err.Error())
  980.         return err
  981.     }
  982.     t.info.ImageField = link
  983.     fmt.Println("OK: " + t.info.ImageField)
  984.     return nil
  985. }
  986.  
  987. func (t *UploadCandidate) uploadCoverToImgur(cover string, cfg Config) error {
  988.     // preparing a form
  989.     b := new(bytes.Buffer)
  990.     w := multipart.NewWriter(b)
  991.     // adding image to form
  992.     f, err := os.Open(cover)
  993.     if err != nil {
  994.         return err
  995.     }
  996.     defer f.Close()
  997.     fw, err := w.CreateFormFile("image", filepath.Base(cover))
  998.     if err != nil {
  999.         return err
  1000.     }
  1001.     if _, err = io.Copy(fw, f); err != nil {
  1002.         return err
  1003.     }
  1004.     w.Close()
  1005.     // post image
  1006.     fmt.Printf("Uploading " + filepath.Base(cover) + " to Imgur... ")
  1007.     req, err := http.NewRequest("POST", "https://api.imgur.com/3/image", b)
  1008.     if err != nil {
  1009.         return err
  1010.     }
  1011.     req.Header.Set("Content-Type", w.FormDataContentType())
  1012.     req.Header.Add("Authorization", "Client-ID " + cfg.imgurClientID)
  1013.     client := &http.Client{}
  1014.     resp, err := client.Do(req)
  1015.     if err != nil {
  1016.         return err
  1017.     }
  1018.     defer resp.Body.Close()
  1019.     // parse response
  1020.     if resp.StatusCode != http.StatusOK {
  1021.         return errors.New("Returned status: " + resp.Status)
  1022.     }
  1023.     // retrieve link from response
  1024.     var link string
  1025.     if err := scan.ScanJSON(resp.Body, "data/link", &link); err != nil {
  1026.         fmt.Println("KO")
  1027.         err = errors.New("Could not find image URL.")
  1028.     }
  1029.     t.info.ImageField = link
  1030.     fmt.Println("OK: " + t.info.ImageField)
  1031.     return nil
  1032. }
  1033.  
  1034. func (t *UploadCandidate) uploadCoverToWhatIMG(cover string, cfg Config) error {
  1035.     // login
  1036.     hc, data, err := login("https://whatimg.com/users.php?act=login-d", cfg.whatimgUser, cfg.whatimgPassword)
  1037.     if err != nil {
  1038.         return err
  1039.     }
  1040.     if !strings.Contains(data, "You have been successfully logged in.") {
  1041.         return errors.New(ErrorCouldNotLogIn)
  1042.     }
  1043.     // upload image and get link
  1044.     fmt.Printf("Uploading " + filepath.Base(cover) + " to WhatIMG... ")
  1045.     data, err = uploadImage(hc, "https://whatimg.com/upload.php", cover)
  1046.     r := regexp.MustCompile(".*value=\"(https://whatimg.com/i/[[:alnum:]]+?.jpg)\".*")
  1047.     if r.MatchString(data) {
  1048.         t.info.ImageField = r.FindStringSubmatch(data)[1]
  1049.         fmt.Println("OK: " + t.info.ImageField)
  1050.     } else {
  1051.         fmt.Println("KO")
  1052.         err = errors.New("Could not find image URL.")
  1053.     }
  1054.     return nil
  1055. }
  1056.  
  1057. func (t *UploadCandidate) generateTorrent(cfg Config) error {
  1058.     fmt.Println("Building torrent file " + filepath.Base(t.torrentFile))
  1059.     mi := metainfo.MetaInfo{Announce: cfg.bibPasskey}
  1060.     mi.Comment = "for bibliotik"
  1061.     mi.CreatedBy = BRUTE
  1062.     mi.CreationDate = time.Now().Unix()
  1063.     i := metainfo.Info{PieceLength: 256 * 1024, Private: newTrue()}
  1064.     if err := i.BuildFromFilePath(t.epubFile); err != nil {
  1065.         return err
  1066.     }
  1067.     bytes, err := bencode.Marshal(i)
  1068.     if err != nil {
  1069.         return err
  1070.     }
  1071.     mi.InfoBytes = bytes
  1072.     f, err := os.Create(t.torrentFile)
  1073.     if err != nil {
  1074.         return err
  1075.     }
  1076.     defer f.Close()
  1077.     return mi.Write(f)
  1078. }
  1079.  
  1080. func (t *UploadCandidate) loginBib(cfg Config) (*http.Client, error) {
  1081.     // login
  1082.     client, data, err := login(BibRoot, cfg.bibUser, cfg.bibPassword)
  1083.     if err != nil {
  1084.         return nil, errors.New(ErrorCouldNotLogIn)
  1085.     }
  1086.     // getting authkey
  1087.     r := regexp.MustCompile(".*authkey=([[:alnum:]]{40}).*")
  1088.     if r.MatchString(data) {
  1089.         t.info.authkey = r.FindStringSubmatch(data)[1]
  1090.     } else {
  1091.         return nil, errors.New("Could not find authkey: " + ErrorCouldNotLogIn)
  1092.     }
  1093.     return client, err
  1094. }
  1095.  
  1096. func (t *UploadCandidate) generateSearchRequest(searchURL string) (string, error) {
  1097.     u, err := url.Parse(searchURL)
  1098.     if err != nil {
  1099.         return "", err
  1100.     }
  1101.     q := u.Query()
  1102.     // split authors if there are more than one
  1103.     // otherwise bib will only return hits if the authors are in the same order in its database.
  1104.     authors := []string{}
  1105.     for _, a := range strings.Split(t.info.AuthorsField, ",") {
  1106.         authors = append(authors, `@authors "`+a+`"`)
  1107.     }
  1108.     q.Set("search", fmt.Sprintf(`%s @title "%s"`, strings.Join(authors, " "), t.info.TitleField))
  1109.     q.Set("for[]", EPUBFormat)
  1110.     u.RawQuery = q.Encode()
  1111.     return u.String(), nil
  1112. }
  1113.  
  1114. func (t *UploadCandidate) checkBib(client *http.Client) (bool, error) {
  1115.     foundExisting := false
  1116.     searchURL, err := t.generateSearchRequest(BibRoot + "/torrents/")
  1117.     if err != nil {
  1118.         return foundExisting, err
  1119.     }
  1120.  
  1121.     resp, err := client.Get(searchURL)
  1122.     if err != nil {
  1123.         return foundExisting, errors.New("Could not search for book")
  1124.     }
  1125.     defer resp.Body.Close()
  1126.  
  1127.     if resp.StatusCode != http.StatusOK {
  1128.         return foundExisting, errors.New("Returned status: " + resp.Status)
  1129.     }
  1130.     doc, err := goquery.NewDocumentFromResponse(resp)
  1131.     if err != nil {
  1132.         return foundExisting, err
  1133.     }
  1134.     // find torrents
  1135.     doc.Find(".torrent").Each(func(i int, s *goquery.Selection) {
  1136.         title := s.Find(".title a").Text()
  1137.         link, _ := s.Find(".title a").Attr("href")
  1138.         authors := []string{}
  1139.         s.Find(".authorLink").Each(func(i int, s *goquery.Selection) {
  1140.             authors = append(authors, s.Text())
  1141.         })
  1142.         publisher := s.Find(".publisherLink").Text()
  1143.         year := s.Find(".torYear").Text()
  1144.         retail := s.Find(".torRetail").Text()
  1145.         if retail == "" {
  1146.             retail = "[NON RETAIL]"
  1147.         }
  1148.         fmt.Printf("%d. %s %s %s [%s] %s\n\t%s\n", i+1, strings.Join(authors, ", "), year, title, publisher, retail, BibRoot+link)
  1149.         foundExisting = true
  1150.     })
  1151.     if foundExisting {
  1152.         fmt.Println()
  1153.     }
  1154.     return foundExisting, err
  1155. }
  1156.  
  1157. func (t *UploadCandidate) uploadTorrent(cfg Config, client *http.Client) error {
  1158.     // prepare request
  1159.     req, err := t.info.generateUploadRequest(BibRoot + "/upload/ebooks")
  1160.     if err != nil {
  1161.         return errors.New("Could not prepare upload form")
  1162.     }
  1163.     // submit the request
  1164.     resp, err := client.Do(req)
  1165.     if err != nil {
  1166.         return errors.New("Could not upload torrent")
  1167.     }
  1168.     defer resp.Body.Close()
  1169.  
  1170.     // check what URL the response came from
  1171.     finalURL := resp.Request.URL.String()
  1172.     if finalURL == BibRoot+"/upload/ebooks" {
  1173.         // response was from the upload form, meaning the fields contained errors and were rejected.
  1174.         return errors.New(ErrorUploadFailed)
  1175.     }
  1176.     return err
  1177. }
  1178.  
  1179. func (t *UploadCandidate) seed(cfg Config) (err error) {
  1180.     fmt.Println("Copying files to begin seeding.")
  1181.     err = h.CopyFile(t.epubFile, filepath.Join(cfg.torrentSeedDir, filepath.Base(t.epubFile)))
  1182.     if err != nil {
  1183.         return
  1184.     }
  1185.     return h.CopyFile(t.torrentFile, filepath.Join(cfg.torrentWatchDir, filepath.Base(t.torrentFile)))
  1186. }
  1187.  
  1188. func (t *UploadCandidate) archive(cfg Config) error {
  1189.     // generate archive name
  1190.     currentTime := time.Now().Local()
  1191.     currentDay := currentTime.Format("2006-01-02")
  1192.     archiveName := filepath.Join(cfg.archiveDir, fmt.Sprintf("%s - %s - %s (%s) %s.tar.gz", currentDay, t.info.IsbnField, t.info.AuthorsField, t.info.YearField, t.info.TitleField))
  1193.     if _, err := h.FileExists(archiveName); err == nil {
  1194.         return errors.New("Archive " + archiveName + " already exists")
  1195.     }
  1196.     fmt.Println("Backing up files to " + filepath.Base(archiveName))
  1197.  
  1198.     // generate archive
  1199.     tar := new(archivex.TarFile)
  1200.     if err := tar.Create(archiveName); err != nil {
  1201.         return errors.New("Error creating tar.")
  1202.     }
  1203.     if err := tar.AddFile(t.epubFile); err != nil {
  1204.         return fmt.Errorf(ErrorAddingToTar, t.epubFile)
  1205.     }
  1206.     if err := tar.AddFile(t.imageFile); err != nil {
  1207.         return fmt.Errorf(ErrorAddingToTar, t.imageFile)
  1208.     }
  1209.     if err := tar.AddFile(t.imageFileGR); err != nil {
  1210.         fmt.Println("Error adding GR cover, probably it could not be downloaded.")
  1211.     }
  1212.     if err := tar.AddFile(t.torrentFile); err != nil {
  1213.         return fmt.Errorf(ErrorAddingToTar, t.torrentFile)
  1214.     }
  1215.     if err := tar.AddFile(t.infoFile); err != nil {
  1216.         return fmt.Errorf(ErrorAddingToTar, t.infoFile)
  1217.     }
  1218.     if err := tar.Close(); err != nil {
  1219.         return errors.New("Error writing tar.")
  1220.     }
  1221.     return nil
  1222. }
  1223.  
  1224. func (t *UploadCandidate) cleanUp() error {
  1225.     if err := os.Remove(t.imageFile); err != nil {
  1226.         return errors.New("Could not remove " + t.imageFile)
  1227.     }
  1228.     if err := os.Remove(t.imageFileGR); err != nil {
  1229.         fmt.Println("Error removing GR cover, probably it could not be downloaded.")
  1230.     }
  1231.     if err := os.Remove(t.torrentFile); err != nil {
  1232.         return errors.New("Could not remove " + t.torrentFile)
  1233.     }
  1234.     if err := os.Remove(t.epubFile); err != nil {
  1235.         return errors.New("Could not remove " + t.epubFile)
  1236.     }
  1237.     if err := os.Remove(t.infoFile); err != nil {
  1238.         return errors.New("Could not remove " + t.infoFile)
  1239.     }
  1240.     return nil
  1241. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement