Support for Local Novels. Adjusted Metadata File Finding to not rely on File order.
This commit is contained in:
parent
741eed00ac
commit
589f5dc141
2 changed files with 308 additions and 162 deletions
17
jnc/jnc.go
17
jnc/jnc.go
|
@ -125,6 +125,14 @@ func (creators Creators) Get(role string) *Creator {
|
||||||
return &(creators)[idx]
|
return &(creators)[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (creators Creators) TryGetName(role string) string {
|
||||||
|
if creators.Contains(role) {
|
||||||
|
return creators.Get(role).Name
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type VolumeAugmented struct {
|
type VolumeAugmented struct {
|
||||||
Info Volume
|
Info Volume
|
||||||
Downloads []Download
|
Downloads []Download
|
||||||
|
@ -199,6 +207,15 @@ type Pagination struct {
|
||||||
LastPage bool `json:"lastPage"`
|
LastPage bool `json:"lastPage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProviderSettings struct {
|
||||||
|
Domain string
|
||||||
|
Series string
|
||||||
|
Directory string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Language string
|
||||||
|
}
|
||||||
|
|
||||||
func NewJNC(domain string) Api {
|
func NewJNC(domain string) Api {
|
||||||
baseUrl := fmt.Sprintf("https://%s/", domain)
|
baseUrl := fmt.Sprintf("https://%s/", domain)
|
||||||
jncApi := Api{
|
jncApi := Api{
|
||||||
|
|
453
main.go
453
main.go
|
@ -13,6 +13,7 @@ import (
|
||||||
"kavita-helper-go/jnc"
|
"kavita-helper-go/jnc"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -30,19 +31,6 @@ type Chapter struct {
|
||||||
pages []string
|
pages []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileWrapper struct {
|
|
||||||
fileName string
|
|
||||||
file zip.File
|
|
||||||
fileSet bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFileWrapper() FileWrapper {
|
|
||||||
newFileWrapper := FileWrapper{}
|
|
||||||
newFileWrapper.fileSet = false
|
|
||||||
newFileWrapper.fileName = ""
|
|
||||||
return newFileWrapper
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO's
|
// TODO's
|
||||||
// [ ] Create Chapter .cbz's based on given Information (no ComicInfo.xml yet)
|
// [ ] Create Chapter .cbz's based on given Information (no ComicInfo.xml yet)
|
||||||
// [ ] Sort Into Volume Folders (requires following step)
|
// [ ] Sort Into Volume Folders (requires following step)
|
||||||
|
@ -76,57 +64,150 @@ func GetArg(argKey string) (string, error) {
|
||||||
return "", errors.New("arg " + argKey + " not found")
|
return "", errors.New("arg " + argKey + " not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Arguments:
|
||||||
|
// -I: Interactive Mode, prompts user for required info when needed
|
||||||
|
// -D: Domain. Only required if -P jnc is set
|
||||||
|
// -L: Language Suffix for Series Title
|
||||||
|
// -S: Series Name. optional when using -I
|
||||||
|
// -P: Provider
|
||||||
|
// -d: download Directory or File Directory. Depends on set -P
|
||||||
|
// -u: username. Only required if -P jnc is set
|
||||||
|
// -p: password. Only required if -P jnc is set
|
||||||
|
// -f: file. Only required when no -P is set. Specifies the file to be used
|
||||||
|
// -n: number. Only required when no -P is set. Specifies the volume number
|
||||||
func main() {
|
func main() {
|
||||||
interactive := false
|
interactive := false
|
||||||
if slices.Contains(os.Args, "-I") {
|
if slices.Contains(os.Args, "-I") {
|
||||||
interactive = true
|
interactive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !interactive {
|
provider, _ := GetArg("-P")
|
||||||
panic("automatic mode is not implemented yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
domain, err := GetArg("-D")
|
serLan, _ := GetArg("-L")
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
serLan, err := GetArg("-L")
|
|
||||||
if serLan != "" {
|
if serLan != "" {
|
||||||
serLan = " " + serLan
|
serLan = " " + serLan
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadDir, err := GetArg("-d")
|
switch provider {
|
||||||
if err != nil {
|
case "":
|
||||||
panic(err)
|
{
|
||||||
|
// figure out settings needed for series input
|
||||||
|
series, err := GetArg("-S")
|
||||||
|
if err != nil {
|
||||||
|
series = GetUserInput("Enter Series Name: ")
|
||||||
|
}
|
||||||
|
|
||||||
|
epubData := EpubInfo{
|
||||||
|
series: series,
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := GetArg("-d")
|
||||||
|
if err != nil {
|
||||||
|
epubData.path, err = GetArg("-f")
|
||||||
|
if err != nil {
|
||||||
|
epubData.path = GetUserInput("Enter File Path: ")
|
||||||
|
epubData.title = filepath.Base(epubData.path)[0 : len(filepath.Base(epubData.path))-len(filepath.Ext(epubData.path))]
|
||||||
|
|
||||||
|
number, err := GetArg("-n")
|
||||||
|
if err != nil {
|
||||||
|
number = GetUserInput("Enter Volume Number: ")
|
||||||
|
}
|
||||||
|
epubData.number = number
|
||||||
|
|
||||||
|
epub, err := zip.OpenReader(epubData.path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
epubData.format = DetermineEpubFormat(epub)
|
||||||
|
if epubData.format.fType == "manga" {
|
||||||
|
panic("Manga without provider not yet handled")
|
||||||
|
}
|
||||||
|
ProcessEpub(epub, epubData)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if strings.LastIndex(dir, "/") != len(dir)-1 {
|
||||||
|
dir = dir + "/"
|
||||||
|
}
|
||||||
|
files, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for fIdx := range files {
|
||||||
|
epubData.path = dir + files[fIdx].Name()
|
||||||
|
epubData.title = files[fIdx].Name()
|
||||||
|
|
||||||
|
epubData.number = GetUserInput(fmt.Sprintf("Enter Volume Number for '%s':", files[fIdx].Name()))
|
||||||
|
|
||||||
|
epub, err := zip.OpenReader(epubData.path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
epubData.format = DetermineEpubFormat(epub)
|
||||||
|
if epubData.format.fType == "manga" {
|
||||||
|
panic("Manga without provider not yet handled")
|
||||||
|
}
|
||||||
|
ProcessEpub(epub, epubData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "jnc":
|
||||||
|
{
|
||||||
|
if !interactive {
|
||||||
|
panic("automatic mode for JNC is not implemented yet")
|
||||||
|
}
|
||||||
|
settings := jnc.ProviderSettings{
|
||||||
|
Language: serLan,
|
||||||
|
}
|
||||||
|
|
||||||
|
domain, err := GetArg("-D")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
settings.Domain = domain
|
||||||
|
|
||||||
|
downloadDir, err := GetArg("-d")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if strings.LastIndex(downloadDir, "/") != len(downloadDir)-1 {
|
||||||
|
downloadDir = downloadDir + "/"
|
||||||
|
}
|
||||||
|
settings.Directory = downloadDir
|
||||||
|
|
||||||
|
series, err := GetArg("-S")
|
||||||
|
if err == nil {
|
||||||
|
settings.Series = series
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(os.Args, "-u") {
|
||||||
|
idx := slices.Index(os.Args, "-u")
|
||||||
|
settings.Username = os.Args[idx+1]
|
||||||
|
} else {
|
||||||
|
settings.Username = GetUserInput("Enter Username: ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(os.Args, "-p") {
|
||||||
|
idx := slices.Index(os.Args, "-p")
|
||||||
|
settings.Password = os.Args[idx+1]
|
||||||
|
} else {
|
||||||
|
settings.Password = GetUserInput("Enter Password: ")
|
||||||
|
}
|
||||||
|
|
||||||
|
RunJNCProvider(settings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.LastIndex(downloadDir, "/") != len(downloadDir)-1 {
|
}
|
||||||
downloadDir = downloadDir + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func RunJNCProvider(settings jnc.ProviderSettings) {
|
||||||
// initialize the J-Novel Club Instance
|
// initialize the J-Novel Club Instance
|
||||||
jnovel := jnc.NewJNC(domain)
|
jnovel := jnc.NewJNC(settings.Domain)
|
||||||
|
jnovel.SetUsername(settings.Username)
|
||||||
|
jnovel.SetPassword(settings.Password)
|
||||||
|
|
||||||
var username string
|
err := jnovel.Login()
|
||||||
if slices.Contains(os.Args, "-u") {
|
|
||||||
idx := slices.Index(os.Args, "-u")
|
|
||||||
username = os.Args[idx+1]
|
|
||||||
} else {
|
|
||||||
username = GetUserInput("Enter Username: ")
|
|
||||||
}
|
|
||||||
jnovel.SetUsername(username)
|
|
||||||
|
|
||||||
var password string
|
|
||||||
if slices.Contains(os.Args, "-p") {
|
|
||||||
idx := slices.Index(os.Args, "-p")
|
|
||||||
password = os.Args[idx+1]
|
|
||||||
} else {
|
|
||||||
password = GetUserInput("Enter Password: ")
|
|
||||||
}
|
|
||||||
jnovel.SetPassword(password)
|
|
||||||
|
|
||||||
err = jnovel.Login()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -170,12 +251,12 @@ func main() {
|
||||||
serie := seriesList[s]
|
serie := seriesList[s]
|
||||||
if mode == "3" || mode == "4" {
|
if mode == "3" || mode == "4" {
|
||||||
if serie.Info.Type == "MANGA" {
|
if serie.Info.Type == "MANGA" {
|
||||||
HandleSeries(jnovel, serie, downloadDir, updatedOnly == "Y" || updatedOnly == "y", serLan)
|
HandleSeries(jnovel, serie, settings.Directory, updatedOnly == "Y" || updatedOnly == "y", settings.Language)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mode == "3" || mode == "5" {
|
if mode == "3" || mode == "5" {
|
||||||
if serie.Info.Type == "NOVEL" {
|
if serie.Info.Type == "NOVEL" {
|
||||||
HandleSeries(jnovel, serie, downloadDir, updatedOnly == "Y" || updatedOnly == "y", serLan)
|
HandleSeries(jnovel, serie, settings.Directory, updatedOnly == "Y" || updatedOnly == "y", settings.Language)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,7 +283,7 @@ func main() {
|
||||||
serie := seriesList[seriesNumber-1]
|
serie := seriesList[seriesNumber-1]
|
||||||
|
|
||||||
if mode == "2" {
|
if mode == "2" {
|
||||||
HandleSeries(jnovel, serie, downloadDir, false, serLan)
|
HandleSeries(jnovel, serie, settings.Directory, false, settings.Language)
|
||||||
} else {
|
} else {
|
||||||
ClearScreen()
|
ClearScreen()
|
||||||
fmt.Println("\n###[Volume Selection]###")
|
fmt.Println("\n###[Volume Selection]###")
|
||||||
|
@ -223,7 +304,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
volume := serie.Volumes[volumeNumber-1]
|
volume := serie.Volumes[volumeNumber-1]
|
||||||
HandleVolume(jnovel, serie, volume, downloadDir, serLan)
|
HandleVolume(jnovel, serie, volume, settings.Directory, settings.Language)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,89 +368,159 @@ func HandleVolume(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc.VolumeAug
|
||||||
downloadLink = volume.Downloads[0].Link
|
downloadLink = volume.Downloads[0].Link
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadAndProcessEpub(jnovel, serie, volume, downloadLink, downloadDir, titleSuffix)
|
file, err := jnovel.Download(downloadLink, downloadDir)
|
||||||
}
|
|
||||||
|
|
||||||
func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc.VolumeAugmented, downloadLink string, downloadDirectory string, titleSuffix string) {
|
|
||||||
FormatNavigationFile := map[string]string{
|
|
||||||
"manga": "item/nav.ncx",
|
|
||||||
"novel": "OEBPS/toc.ncx",
|
|
||||||
}
|
|
||||||
|
|
||||||
FormatMetadataName := map[string]string{
|
|
||||||
"item/comic.opf": "manga",
|
|
||||||
"OEBPS/content.opf": "novel",
|
|
||||||
}
|
|
||||||
|
|
||||||
MetaInf := "META-INF/container.xml"
|
|
||||||
file, err := jnovel.Download(downloadLink, downloadDirectory)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
volume.Info, err = jnovel.FetchVolumeInfo(volume.Info)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error fetching volume info: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var title string
|
||||||
|
var number string
|
||||||
|
if serie.Info.Title == "Ascendance of a Bookworm" {
|
||||||
|
splits := strings.Split(volume.Info.ShortTitle, " ")
|
||||||
|
title = serie.Info.Title + " " + splits[0] + " " + splits[1] + titleSuffix
|
||||||
|
number = splits[3]
|
||||||
|
} else {
|
||||||
|
title = serie.Info.Title + titleSuffix
|
||||||
|
number = strconv.Itoa(volume.Info.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
epubData := EpubInfo{
|
||||||
|
path: file,
|
||||||
|
title: volume.Info.Title,
|
||||||
|
series: title,
|
||||||
|
number: number,
|
||||||
|
info: ComicInfo{
|
||||||
|
Publisher: "J-Novel Club",
|
||||||
|
Tags: strings.Join(serie.Info.Tags, ","),
|
||||||
|
Series: serie.Info.Title,
|
||||||
|
Volume: volume.Info.Number,
|
||||||
|
Summary: volume.Info.Description,
|
||||||
|
Writer: volume.Info.Creators.TryGetName("AUTHOR"),
|
||||||
|
Translator: volume.Info.Creators.TryGetName("TRANSLATOR"),
|
||||||
|
Editor: volume.Info.Creators.TryGetName("EDITOR"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if volume.Info.Creators.Contains("ARTIST") {
|
||||||
|
epubData.info.CoverArtist = volume.Info.Creators.Get("ARTIST").Name
|
||||||
|
} else if volume.Info.Creators.Contains("ILLUSTRATOR") {
|
||||||
|
epubData.info.CoverArtist = volume.Info.Creators.Get("ILLUSTRATOR").Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if volume.Info.Creators.Contains("LETTERER") {
|
||||||
|
epubData.info.Letterer = volume.Info.Creators.Get("LETTERER").Name
|
||||||
|
} else if volume.Info.Creators.Contains("ILLUSTRATOR") {
|
||||||
|
epubData.info.Letterer = volume.Info.Creators.Get("ILLUSTRATOR").Name
|
||||||
|
}
|
||||||
|
|
||||||
|
publishingTime, err := time.Parse(time.RFC3339, volume.Info.Publishing)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
epubData.info.Year = publishingTime.Year()
|
||||||
|
epubData.info.Month = int(publishingTime.Month())
|
||||||
|
epubData.info.Day = publishingTime.Day()
|
||||||
|
|
||||||
epub, err := zip.OpenReader(file)
|
epub, err := zip.OpenReader(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := NewFileWrapper()
|
epubData.format = DetermineEpubFormat(epub)
|
||||||
navigation := NewFileWrapper()
|
ProcessEpub(epub, epubData)
|
||||||
|
}
|
||||||
|
|
||||||
for i := range epub.Reader.File {
|
type EpubInfo struct {
|
||||||
file := epub.Reader.File[i]
|
path string
|
||||||
|
title string
|
||||||
|
series string
|
||||||
|
number string
|
||||||
|
info ComicInfo
|
||||||
|
format FormatInfo
|
||||||
|
}
|
||||||
|
|
||||||
if file.Name == MetaInf {
|
type FormatInfo struct {
|
||||||
doc := etree.NewDocument()
|
fType string
|
||||||
reader, err := file.Open()
|
metadata zip.File
|
||||||
if err != nil {
|
navigation zip.File
|
||||||
fmt.Println(err)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := doc.ReadFrom(reader); err != nil {
|
func DetermineEpubFormat(epub *zip.ReadCloser) FormatInfo {
|
||||||
_ = reader.Close()
|
FormatNavigationFile := map[string]string{
|
||||||
panic(err)
|
"manga": "item/nav.ncx",
|
||||||
}
|
"novel": "OEBPS/toc.ncx",
|
||||||
|
}
|
||||||
|
FormatMetadataName := map[string]string{
|
||||||
|
"item/comic.opf": "manga",
|
||||||
|
"OEBPS/content.opf": "novel",
|
||||||
|
}
|
||||||
|
MetaInf := "META-INF/container.xml"
|
||||||
|
|
||||||
metadata.fileName = doc.FindElement("//container/rootfiles/rootfile").SelectAttr("full-path").Value
|
metaInfFileIdx := slices.IndexFunc(epub.Reader.File, func(f *zip.File) bool {
|
||||||
navigation.fileName = FormatNavigationFile[FormatMetadataName[metadata.fileName]]
|
if f.Name == MetaInf {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
if len(metadata.fileName) != 0 && file.Name == metadata.fileName {
|
metaInfFile := epub.Reader.File[metaInfFileIdx]
|
||||||
metadata.file = *file
|
doc := etree.NewDocument()
|
||||||
metadata.fileSet = true
|
reader, err := metaInfFile.Open()
|
||||||
}
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
if len(navigation.fileName) != 0 && file.Name == navigation.fileName {
|
|
||||||
navigation.file = *file
|
|
||||||
navigation.fileSet = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if metadata.fileSet && navigation.fileSet {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch based on metadata file
|
if _, err := doc.ReadFrom(reader); err != nil {
|
||||||
|
_ = reader.Close()
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
switch FormatMetadataName[metadata.fileName] {
|
metadataFileName := doc.FindElement("//container/rootfiles/rootfile").SelectAttr("full-path").Value
|
||||||
|
metaFileIdx := slices.IndexFunc(epub.Reader.File, func(f *zip.File) bool {
|
||||||
|
if f.Name == metadataFileName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
metadata := *epub.Reader.File[metaFileIdx]
|
||||||
|
|
||||||
|
navigationFileName := FormatNavigationFile[FormatMetadataName[metadataFileName]]
|
||||||
|
navigationFileIdx := slices.IndexFunc(epub.Reader.File, func(f *zip.File) bool {
|
||||||
|
if f.Name == navigationFileName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
navigation := *epub.Reader.File[navigationFileIdx]
|
||||||
|
|
||||||
|
return FormatInfo{
|
||||||
|
fType: FormatMetadataName[metadataFileName],
|
||||||
|
metadata: metadata,
|
||||||
|
navigation: navigation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessEpub(epub *zip.ReadCloser, epubData EpubInfo) {
|
||||||
|
// Switch based on metadata file
|
||||||
|
switch epubData.format.fType {
|
||||||
case "manga":
|
case "manga":
|
||||||
{
|
{
|
||||||
comicInfoName := "ComicInfo.xml"
|
comicInfoName := "ComicInfo.xml"
|
||||||
chapterList := GenerateMangaChapterList(navigation)
|
chapterList := GenerateMangaChapterList(epubData.format.navigation)
|
||||||
|
|
||||||
if chapterList.Len() == 0 {
|
if chapterList.Len() == 0 {
|
||||||
fmt.Println("No chapters found, chapter name likely not supported")
|
fmt.Println("No chapters found, chapter name likely not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
basePath := downloadDirectory + volume.Info.Title + titleSuffix + "/"
|
basePath := filepath.Dir(epubData.path) + "/" + epubData.title
|
||||||
PrepareVolumeDirectory(basePath)
|
PrepareVolumeDirectory(basePath)
|
||||||
volume.Info, err = jnovel.FetchVolumeInfo(volume.Info)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
doc := etree.NewDocument()
|
doc := etree.NewDocument()
|
||||||
reader, err := metadata.file.Open()
|
reader, err := epubData.format.metadata.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
@ -384,7 +535,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
for ch := chapterList.Front(); ch != nil; ch = ch.Next() {
|
for ch := chapterList.Front(); ch != nil; ch = ch.Next() {
|
||||||
chap := ch.Value.(Chapter)
|
chap := ch.Value.(Chapter)
|
||||||
|
|
||||||
zipPath := basePath + volume.Info.Title + " Chapter " + chap.chDisplay + ".cbz"
|
zipPath := basePath + "/" + epubData.title + " Chapter " + chap.chDisplay + ".cbz"
|
||||||
|
|
||||||
if _, err = os.Stat(zipPath); err == nil {
|
if _, err = os.Stat(zipPath); err == nil {
|
||||||
err := os.Remove(zipPath)
|
err := os.Remove(zipPath)
|
||||||
|
@ -444,7 +595,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
comicInfo, err := GenerateChapterMetadata(volume, serie, len(chap.pages), language, chap.chDisplay, titleSuffix)
|
comicInfo, err := GenerateChapterMetadata(epubData, len(chap.pages), language, chap.chDisplay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
@ -459,7 +610,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
}
|
}
|
||||||
|
|
||||||
epub.Close()
|
epub.Close()
|
||||||
os.Remove(file)
|
os.Remove(epubData.path)
|
||||||
}
|
}
|
||||||
case "novel":
|
case "novel":
|
||||||
{
|
{
|
||||||
|
@ -467,7 +618,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
CalibreSeriesIndexAttr := "calibre:series_index"
|
CalibreSeriesIndexAttr := "calibre:series_index"
|
||||||
EpubTitleTag := "//package/metadata"
|
EpubTitleTag := "//package/metadata"
|
||||||
|
|
||||||
metafile, err := metadata.file.Open()
|
metafile, err := epubData.format.metadata.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
@ -481,29 +632,18 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
|
|
||||||
seriesTitle := etree.NewElement("meta")
|
seriesTitle := etree.NewElement("meta")
|
||||||
seriesTitle.CreateAttr("name", CalibreSeriesAttr)
|
seriesTitle.CreateAttr("name", CalibreSeriesAttr)
|
||||||
var title string
|
seriesTitle.CreateAttr("content", epubData.series)
|
||||||
var number string
|
|
||||||
if serie.Info.Title == "Ascendance of a Bookworm" {
|
|
||||||
splits := strings.Split(volume.Info.ShortTitle, " ")
|
|
||||||
title = serie.Info.Title + " " + splits[0] + " " + splits[1] + titleSuffix
|
|
||||||
number = splits[3]
|
|
||||||
} else {
|
|
||||||
title = serie.Info.Title + titleSuffix
|
|
||||||
number = strconv.Itoa(volume.Info.Number)
|
|
||||||
}
|
|
||||||
|
|
||||||
seriesTitle.CreateAttr("content", title)
|
|
||||||
|
|
||||||
seriesNumber := etree.NewElement("meta")
|
seriesNumber := etree.NewElement("meta")
|
||||||
seriesNumber.CreateAttr("name", CalibreSeriesIndexAttr)
|
seriesNumber.CreateAttr("name", CalibreSeriesIndexAttr)
|
||||||
seriesNumber.CreateAttr("content", number)
|
seriesNumber.CreateAttr("content", epubData.number)
|
||||||
|
|
||||||
doc.FindElement(EpubTitleTag).InsertChildAt(idx+1, seriesTitle)
|
doc.FindElement(EpubTitleTag).InsertChildAt(idx+1, seriesTitle)
|
||||||
doc.FindElement(EpubTitleTag).InsertChildAt(idx+2, seriesNumber)
|
doc.FindElement(EpubTitleTag).InsertChildAt(idx+2, seriesNumber)
|
||||||
doc.Indent(2)
|
doc.Indent(2)
|
||||||
|
|
||||||
// Open the zip file for writing
|
// Open the zip file for writing
|
||||||
zipfile, err := os.OpenFile(file+".new", os.O_RDWR|os.O_CREATE, 0644)
|
zipfile, err := os.OpenFile(epubData.path+".new", os.O_RDWR|os.O_CREATE, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -521,7 +661,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
metawriter, err := zipWriter.Create(metadata.fileName)
|
metawriter, err := zipWriter.Create(epubData.format.metadata.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -532,7 +672,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range epub.File {
|
for _, f := range epub.File {
|
||||||
if f.Name != metadata.fileName {
|
if f.Name != epubData.format.metadata.Name {
|
||||||
writer, err := zipWriter.Create(f.Name)
|
writer, err := zipWriter.Create(f.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -552,68 +692,57 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
||||||
|
|
||||||
epub.Close()
|
epub.Close()
|
||||||
zipWriter.Close()
|
zipWriter.Close()
|
||||||
source, _ := os.Open(file + ".new")
|
source, _ := os.Open(epubData.path + ".new")
|
||||||
dest, _ := os.Create(file)
|
dest, _ := os.Create(epubData.path)
|
||||||
io.Copy(dest, source)
|
io.Copy(dest, source)
|
||||||
os.Remove(file + ".new")
|
os.Remove(epubData.path + ".new")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateChapterMetadata(volume jnc.VolumeAugmented, serie jnc.SerieAugmented, pageCount int, language string, chapterNumber string, titleSuffix string) ([]byte, error) {
|
func GenerateChapterMetadata(epubData EpubInfo, pageCount int, language string, chapterNumber string) ([]byte, error) {
|
||||||
comicInfo := ComicInfo{
|
comicInfo := ComicInfo{
|
||||||
XMLName: "ComicInfo",
|
XMLName: "ComicInfo",
|
||||||
XMLNS: "http://www.w3.org/2001/XMLSchema-instance",
|
XMLNS: "http://www.w3.org/2001/XMLSchema-instance",
|
||||||
XSI: "ComicInfo.xsd",
|
XSI: "ComicInfo.xsd",
|
||||||
}
|
}
|
||||||
|
|
||||||
vInfo := volume.Info
|
comicInfo.Series = epubData.info.Series
|
||||||
sInfo := serie.Info
|
comicInfo.Title = epubData.title
|
||||||
|
|
||||||
comicInfo.Series = sInfo.Title
|
|
||||||
comicInfo.Title = vInfo.Title + titleSuffix
|
|
||||||
comicInfo.Number = chapterNumber
|
comicInfo.Number = chapterNumber
|
||||||
comicInfo.Volume = vInfo.Number
|
comicInfo.Volume = epubData.info.Volume
|
||||||
|
|
||||||
comicInfo.Count = -1 // TODO somehow fetch actual completion status
|
comicInfo.Count = -1 // TODO somehow fetch actual completion status
|
||||||
|
|
||||||
comicInfo.Summary = vInfo.Description
|
comicInfo.Summary = epubData.info.Summary
|
||||||
|
|
||||||
publishingTime, err := time.Parse(time.RFC3339, vInfo.Publishing)
|
comicInfo.Year = epubData.info.Year
|
||||||
if err != nil {
|
comicInfo.Month = epubData.info.Month
|
||||||
return nil, err
|
comicInfo.Day = epubData.info.Day
|
||||||
}
|
|
||||||
comicInfo.Year = publishingTime.Year()
|
|
||||||
comicInfo.Month = int(publishingTime.Month())
|
|
||||||
comicInfo.Day = publishingTime.Day()
|
|
||||||
|
|
||||||
if vInfo.Creators.Contains("AUTHOR") {
|
if epubData.info.Writer != "" {
|
||||||
comicInfo.Writer = vInfo.Creators.Get("AUTHOR").Name
|
comicInfo.Writer = epubData.info.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
if vInfo.Creators.Contains("LETTERER") {
|
if epubData.info.Letterer != "" {
|
||||||
comicInfo.Letterer = vInfo.Creators.Get("LETTERER").Name
|
comicInfo.Letterer = epubData.info.Letterer
|
||||||
} else if vInfo.Creators.Contains("ILLUSTRATOR") {
|
|
||||||
comicInfo.Letterer = vInfo.Creators.Get("ILLUSTRATOR").Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if vInfo.Creators.Contains("ARTIST") {
|
if epubData.info.CoverArtist != "" {
|
||||||
comicInfo.CoverArtist = vInfo.Creators.Get("ARTIST").Name
|
comicInfo.CoverArtist = epubData.info.CoverArtist
|
||||||
} else if vInfo.Creators.Contains("ILLUSTRATOR") {
|
|
||||||
comicInfo.CoverArtist = vInfo.Creators.Get("ILLUSTRATOR").Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if vInfo.Creators.Contains("TRANSLATOR") {
|
if epubData.info.Translator != "" {
|
||||||
comicInfo.Translator = vInfo.Creators.Get("TRANSLATOR").Name
|
comicInfo.Translator = epubData.info.Translator
|
||||||
}
|
}
|
||||||
|
|
||||||
if vInfo.Creators.Contains("EDITOR") {
|
if epubData.info.Editor != "" {
|
||||||
comicInfo.Editor = vInfo.Creators.Get("EDITOR").Name
|
comicInfo.Editor = epubData.info.Editor
|
||||||
}
|
}
|
||||||
|
|
||||||
comicInfo.Publisher = "J-Novel Club"
|
comicInfo.Publisher = epubData.info.Publisher
|
||||||
|
|
||||||
comicInfo.Tags = strings.Join(sInfo.Tags, ",")
|
comicInfo.Tags = epubData.info.Tags
|
||||||
comicInfo.PageCount = pageCount
|
comicInfo.PageCount = pageCount
|
||||||
comicInfo.Language = language
|
comicInfo.Language = language
|
||||||
comicInfo.Format = "Comic" // JNC reports this as type in the epub file
|
comicInfo.Format = "Comic" // JNC reports this as type in the epub file
|
||||||
|
@ -820,8 +949,8 @@ func GetLastValidChapterNumber(currentChapter *list.Element) Chapter {
|
||||||
return currentChapter.Value.(Chapter)
|
return currentChapter.Value.(Chapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateMangaChapterList(navigation FileWrapper) *list.List {
|
func GenerateMangaChapterList(navigation zip.File) *list.List {
|
||||||
reader, err := navigation.file.Open()
|
reader, err := navigation.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue