diff --git a/cmd/build.go b/cmd/build.go index dc68913..0fbf9a5 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -1,12 +1,14 @@ package cmd import ( + "accorder/pkg/calibre" "encoding/xml" "fmt" "log" + "os" + "strconv" - "accorder/pkg/calibre" - + "github.com/araddon/dateparse" "github.com/beevik/etree" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -83,6 +85,8 @@ should be enough for the next successful build).`, root := doc.SelectElement("rdf:RDF") for _, attachmentNode := range root.FindElements("[name()='link:type']") { var zoteroItem ZoteroItem + var bookOpf calibre.BookOpfW + if attachmentNode.Text() != "application/pdf" { continue } @@ -91,17 +95,54 @@ should be enough for the next successful build).`, filePathElement := zoteroUnion.CreateElement("filePath") filePathQuery := attachmentNode.Parent().FindElement("[name()='rdf:resource']").SelectAttr("rdf:resource").Value filePathElement.CreateText(filePathQuery) + mimeTypeElement := zoteroUnion.CreateElement("mimeType") mimeType := attachmentNode.Text() mimeTypeElement.CreateText(mimeType) bibliographyNode := root.FindElement(fmt.Sprintf("[@rdf:resource='%s']", attachmentNode.Parent().SelectAttr("rdf:about").Value)).Parent().Copy() + // newDoc.AddChild(bibliographyNode) + // newDoc.WriteTo(os.Stdout) + + dateQuery := bibliographyNode.FindElement("[name()='dc:date']") + if dateQuery != nil { + dateElement := zoteroUnion.CreateElement("date") + date, err := dateparse.ParseAny(dateQuery.Text()) + if err == nil { + formattedDate := date.Format("2006-01-02") + dateElement.CreateText(formattedDate) + + bookOpf.Metadata.Published = formattedDate + } else { + newDateQuery := fmt.Sprintf("1 %s", dateQuery.Text()) + newDate, err := dateparse.ParseAny(newDateQuery) + if err == nil { + newFormattedDate := newDate.Format("2006-01-02") + dateElement.CreateText(newFormattedDate) + bookOpf.Metadata.Published = newFormattedDate + } else { + lastChanceDate := dateQuery.Text()[len(dateQuery.Text())-4:] + year, err := strconv.Atoi(lastChanceDate) + if err == nil { + justYear := fmt.Sprintf("%d-01-01", year) + dateElement.CreateText(justYear) + bookOpf.Metadata.Published = justYear + } else { + fmt.Println("ERROR parsing date...", err) + } + } + } + + } + publisherQuery := bibliographyNode.FindElement("[name()='foaf:name']") if publisherQuery != nil { publisherElement := zoteroUnion.CreateElement("publisher") publisher := publisherQuery.Text() publisherElement.CreateText(publisher) + + bookOpf.Metadata.Publisher = publisher } authorsQuery := bibliographyNode.FindElements("[name()='foaf:Person']") @@ -118,7 +159,13 @@ should be enough for the next successful build).`, if surNameNode != nil { surName = surNameNode.Text() } - author.CreateText(fmt.Sprintf("%s %s", firstName, surName)) + fullName := fmt.Sprintf("%s %s", firstName, surName) + author.CreateText(fullName) + + bookOpf.Metadata.Creators = append(bookOpf.Metadata.Creators, calibre.Creator{ + Role: "aut", + Name: fullName, + }) } titleQuery := bibliographyNode.FindElement("[name()='dc:title']") @@ -126,6 +173,8 @@ should be enough for the next successful build).`, titleNode := zoteroUnion.CreateElement("title") title := titleQuery.Text() titleNode.CreateText(title) + + bookOpf.Metadata.Title = title } descriptionQuery := bibliographyNode.FindElement("[name()='dcterms:abstract']") @@ -133,8 +182,13 @@ should be enough for the next successful build).`, descriptionNode := zoteroUnion.CreateElement("description") description := descriptionQuery.Text() descriptionNode.CreateText(description) + + bookOpf.Metadata.Description = description } + // newDoc.WriteTo(os.Stdout) + // fmt.Println("") + b, err := newDoc.WriteToBytes() if err != nil { log.Fatal(err) @@ -142,8 +196,27 @@ should be enough for the next successful build).`, if err := xml.Unmarshal(b, &zoteroItem); err != nil { log.Fatalln(err) } - fmt.Printf("\nZoteroItem: %#v\n", zoteroItem) + // fmt.Printf("\nZoteroItem: %#v\n", zoteroItem) + bookOpf.Version = "2.0" + bookOpf.Xmlns = "http://www.idpf.org/2007/opf" + bookOpf.UniqueIdentifier = "uuid_id" + bookOpf.Metadata.DC = "http://purl.org/dc/elements/1.1/" + bookOpf.Metadata.OPF = "http://www.idpf.org/2007/opf" + + bookOpf.Metadata.Identifiers = append(bookOpf.Metadata.Identifiers, calibre.Identifier{ + Scheme: "calibre", + Id: "calibre_id", + Value: "-1", + }) + + bookOpfOutput, err := xml.MarshalIndent(bookOpf, " ", " ") + if err != nil { + log.Fatalln(err) + } + // _ = bookOpfOutput + os.Stdout.Write(bookOpfOutput) + fmt.Println("") } calibre.RenderStandaloneApp(calibrePath, librarianName, libraryUUID, librarySecret, jsonPath) }, diff --git a/go.mod b/go.mod index 04609d9..8b9de67 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module accorder go 1.16 require ( + github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect github.com/beevik/etree v1.1.0 github.com/karrick/godirwalk v1.16.1 github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f diff --git a/go.sum b/go.sum index 67bcdac..924c98b 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= +github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -343,6 +345,7 @@ github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -446,6 +449,7 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= @@ -460,6 +464,7 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc= github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs= diff --git a/pkg/calibre/calibre.go b/pkg/calibre/calibre.go index 5c822b6..b646758 100644 --- a/pkg/calibre/calibre.go +++ b/pkg/calibre/calibre.go @@ -80,44 +80,83 @@ type BookJSON struct { CoverUrl string `json:"cover_url"` } +type Creator struct { + Role string `xml:"opf:role,attr"` + Name string `xml:",chardata"` +} + +type Identifier struct { + Scheme string `xml:"opf:scheme,attr"` + Id string `xml:"id,attr"` // Calibre has two internal Ids: calibre_id and uuid_id + Value string `xml:",chardata"` +} + +type BookOpfW struct { + XMLName xml.Name `xml:"package"` + Version string `xml:"version,attr"` + Xmlns string `xml:"xmlns,attr"` + UniqueIdentifier string `xml:"unique-identifier,attr"` + Metadata struct { + DC string `xml:"xmlns:dc,attr"` + OPF string `xml:"xmlns:opf,attr"` + Identifiers []struct { + Scheme string `xml:"opf:scheme,attr"` + Id string `xml:"id,attr"` // Calibre has two internal Ids: calibre_id and uuid_id + Value string `xml:",chardata"` + } `xml:"dc:identifier"` + Title string `xml:"dc:title"` + Creators []struct { + Role string `xml:"opf:role,attr"` + Name string `xml:",chardata"` + } `xml:"dc:creator"` + Published string `xml:"dc:date"` + Description string `xml:"dc:description"` + Publisher string `xml:"dc:publisher"` + Languages []struct { + Language string `xml:",chardata"` + } `xml:"dc:language"` + Tags []struct { + Tag string `xml:",chardata"` + } `xml:"dc:subject"` + Meta []struct { + Content string `xml:"content,attr"` + Name string `xml:"name,attr"` + } `xml:"meta"` + } `xml:"metadata"` +} + type BookOpf struct { XMLName xml.Name `xml:"package"` Version string `xml:"version,attr"` Xmlns string `xml:"xmlns,attr"` UniqueIdentifier string `xml:"unique-identifier,attr"` Metadata struct { - XMLName xml.Name `xml:"metadata"` - DC string `xml:"dc,attr"` - OPF string `xml:"opf,attr"` + DC string `xml:"dc,attr"` + OPF string `xml:"opf,attr"` Identifiers []struct { - XMLName xml.Name `xml:"identifier"` - Scheme string `xml:"scheme,attr"` - Id string `xml:"id,attr"` // Calibre has two internal Ids: calibre_id and uuid_id - Value string `xml:",chardata"` + Scheme string `xml:"scheme,attr"` + Id string `xml:"id,attr"` // Calibre has two internal Ids: calibre_id and uuid_id + Value string `xml:",chardata"` } `xml:"identifier"` Title string `xml:"title"` Creators []struct { - XMLName xml.Name `xml:"creator"` - Role string `xml:"role,attr"` - Name string `xml:",chardata"` + Role string `xml:"role,attr"` + Name string `xml:",chardata"` } `xml:"creator"` Published string `xml:"date"` Description string `xml:"description"` Publisher string `xml:"publisher"` Languages []struct { - XMLName xml.Name `xml:"language"` - Language string `xml:",chardata"` + Language string `xml:",chardata"` } `xml:"language"` Tags []struct { - XMLName xml.Name `xml:"subject"` - Tag string `xml:",chardata"` + Tag string `xml:",chardata"` } `xml:"subject"` Meta []struct { - XMLName xml.Name `xml:"meta"` - Content string `xml:"content,attr"` - Name string `xml:"name,attr"` + Content string `xml:"content,attr"` + Name string `xml:"name,attr"` } `xml:"meta"` - } + } `xml:"metadata"` } // TitleSort returns if Calibre processed Title for sorting order