From 2130c31e300b7d4cea465cf90f6d1f4e7e292e95 Mon Sep 17 00:00:00 2001 From: Nick White Date: Tue, 16 Mar 2021 12:13:25 +0000 Subject: dlgbook: add new tool to wrap around getgbook, automatically setting the author, year and title and naming the directory appropriately --- cmd/dlgbook/main.go | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 cmd/dlgbook/main.go diff --git a/cmd/dlgbook/main.go b/cmd/dlgbook/main.go new file mode 100644 index 0000000..3aa8015 --- /dev/null +++ b/cmd/dlgbook/main.go @@ -0,0 +1,175 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "os/exec" + "path" + "strings" + "unicode" +) + +const usage = `Usage: dlgbook bookid [-a author] [-y year] [-t title] [savedir] + +Downloads all pages from a Google Book, using the getgbook +tool, extracting the date, author name and title from +Google Books (unless given as arguments to dlgbook), and +saves them into a directory named YEAR_AUTHORSURNAME_Title +` + +// formatAuthors formats a list of authors by just selecting +// the first one listed, and returning the uppercased final +// name. +func formatAuthors(authors []string) string { + if len(authors) == 0 { + return "" + } + + s := authors[0] + + parts := strings.Fields(s) + if len(parts) > 1 { + s = parts[len(parts)-1] + } + + s = strings.ToUpper(s) + + return s +} + +// mapTitle is a function for strings.Map to strip out +// unwanted characters from the title. +func mapTitle(r rune) rune { + if !unicode.IsLetter(r) { + return -1 + } + return r +} + +// formatTitle formats a title to our preferences, notably +// by stripping spaces and punctuation characters. +func formatTitle(title string) string { + return strings.Map(mapTitle, title) +} + +// getMetadata queries Google Books for metadata we care about +// and returns it formatted as we need it. +func getMetadata(id string) (string, string, string, error) { + var author, title, year string + url := fmt.Sprintf("https://www.googleapis.com/books/v1/volumes/%s", id) + + // designed to be unmarshalled by encoding/json's Unmarshal() + type bookInfo struct { + VolumeInfo struct { + Title string + Authors []string + PublishedDate string + } + } + + resp, err := http.Get(url) + if err != nil { + return author, title, year, fmt.Errorf("Error downloading metadata %s: %v", url, err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return author, title, year, fmt.Errorf("Error downloading metadata %s: %v", url, err) + } + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return author, title, year, fmt.Errorf("Error reading metadata %s: %v", url, err) + } + + v := bookInfo{} + err = json.Unmarshal(b, &v) + if err != nil { + return author, title, year, fmt.Errorf("Error parsing metadata %s: %v", url, err) + } + + author = formatAuthors(v.VolumeInfo.Authors) + title = formatTitle(v.VolumeInfo.Title) + year = v.VolumeInfo.PublishedDate + + return author, title, year, nil +} + +func main() { + author := flag.String("author", "", "Set author, rather than autodetecting") + title := flag.String("title", "", "Set title, rather than autodetecting") + year := flag.String("year", "", "Set year, rather than autodetecting") + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), usage) + flag.PrintDefaults() + } + flag.Parse() + + if flag.NArg() < 1 { + flag.Usage() + return + } + bookid := flag.Arg(0) + + if *author == "" || *title == "" || *year == "" { + a, t, y, err := getMetadata(bookid) + if err != nil { + log.Fatal(err) + } + if *author == "" { + *author = a + } + if *title == "" { + *title = t + } + if *year == "" { + *year = y + } + } + + dir := fmt.Sprintf("%s_%s_%s", *year, *author, *title) + err := os.MkdirAll(dir, 0755) + if err != nil { + log.Fatalf("Couldn't create directory %s: %v", dir, err) + } + cmd := exec.Command("getgbook", bookid) + cmd.Dir = dir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + log.Fatalf("Error running getgbook %s: %v", bookid, err) + } + + // getgbook downloads into bookid directory, so move files out of + // there directly into dir + tmpdir := path.Join(dir, bookid) + f, err := os.Open(tmpdir) + if err != nil { + log.Fatalf("Failed to open %s to move files: %v", tmpdir, err) + } + files, err := f.Readdir(0) + if err != nil { + log.Fatalf("Failed to readdir %s to move files: %v", tmpdir, err) + } + for _, v := range files { + orig := path.Join(tmpdir, v.Name()) + new := path.Join(dir, v.Name()) + err = os.Rename(orig, new) + if err != nil { + log.Fatalf("Failed to move %s to %s: %v", orig, new, err) + } + } + + err = os.Remove(tmpdir) + if err != nil { + log.Fatalf("Failed to remove temporary directory %s: %v", tmpdir, err) + } + + fmt.Printf("Successfully download to %s\n", dir) +} -- cgit v1.2.1-24-ge1ad