Support JNC Nina via dynamic domain, add optional Suffix for Series (used for language separation)
This commit is contained in:
parent
d828532239
commit
741eed00ac
2 changed files with 64 additions and 46 deletions
22
jnc/jnc.go
22
jnc/jnc.go
|
@ -15,11 +15,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const BaseUrl = "https://labs.j-novel.club/"
|
||||
const ApiV1Url = BaseUrl + "app/v1/"
|
||||
const ApiV2Url = BaseUrl + "app/v2/"
|
||||
const FeedUrl = BaseUrl + "feed/"
|
||||
|
||||
type ApiFormat int
|
||||
|
||||
const (
|
||||
|
@ -56,6 +51,8 @@ type Api struct {
|
|||
_format ApiFormat
|
||||
_library Library
|
||||
_series map[string]SerieAugmented
|
||||
_apiUrl string
|
||||
_feedUrl string
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
|
@ -202,7 +199,8 @@ type Pagination struct {
|
|||
LastPage bool `json:"lastPage"`
|
||||
}
|
||||
|
||||
func NewJNC() Api {
|
||||
func NewJNC(domain string) Api {
|
||||
baseUrl := fmt.Sprintf("https://%s/", domain)
|
||||
jncApi := Api{
|
||||
_auth: Auth{
|
||||
_username: "",
|
||||
|
@ -211,6 +209,8 @@ func NewJNC() Api {
|
|||
},
|
||||
_library: Library{},
|
||||
_series: map[string]SerieAugmented{},
|
||||
_apiUrl: baseUrl + "app/v2/",
|
||||
_feedUrl: baseUrl + "feed/",
|
||||
}
|
||||
return jncApi
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ func (jncApi *Api) Login() error {
|
|||
return err
|
||||
}
|
||||
|
||||
res, err := http.Post(ApiV2Url+"auth/login?"+jncApi.ReturnFormat(), "application/json", bytes.NewBuffer(jsonAuthBody))
|
||||
res, err := http.Post(jncApi._apiUrl+"auth/login?"+jncApi.ReturnFormat(), "application/json", bytes.NewBuffer(jsonAuthBody))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ func (jncApi *Api) AuthParam() string {
|
|||
// Logout invalidates the auth token and resets the jnc instance to a blank slate. No information remains after calling.
|
||||
func (jncApi *Api) Logout() error {
|
||||
fmt.Println("Logging out...")
|
||||
res, err := http.Post(ApiV2Url+"auth/logout?"+jncApi.ReturnFormat()+"&"+jncApi.AuthParam(), "application/json", nil)
|
||||
res, err := http.Post(jncApi._apiUrl+"auth/logout?"+jncApi.ReturnFormat()+"&"+jncApi.AuthParam(), "application/json", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ func (jncApi *Api) SetUsername(username string) {
|
|||
// FetchLibrary retrieves the list of Book's in the logged-in User's J-Novel Club library
|
||||
func (jncApi *Api) FetchLibrary() error {
|
||||
fmt.Println("Fetching library contents...")
|
||||
res, err := http.Get(ApiV2Url + "me/library?" + jncApi.ReturnFormat() + "&" + jncApi.AuthParam())
|
||||
res, err := http.Get(jncApi._apiUrl + "me/library?" + jncApi.ReturnFormat() + "&" + jncApi.AuthParam())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ func (jncApi *Api) FetchLibrarySeries() error {
|
|||
fmt.Printf("\rFetching Series Info: %s/%s - %s%%", strconv.Itoa(progress), strconv.Itoa(len(jncApi._series)), strconv.FormatFloat(float64(progress)/float64(len(jncApi._series))*float64(100), 'f', 2, 32))
|
||||
serie := jncApi._series[i]
|
||||
|
||||
res, err := http.Get(ApiV2Url + "series/" + serie.Info.Id + "?" + jncApi.ReturnFormat() + "&" + jncApi.AuthParam())
|
||||
res, err := http.Get(jncApi._apiUrl + "series/" + serie.Info.Id + "?" + jncApi.ReturnFormat() + "&" + jncApi.AuthParam())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -394,7 +394,7 @@ func (jncApi *Api) FetchLibrarySeries() error {
|
|||
// FetchVolumeInfo retrieves additional Volume Info that was not returned when retrieving the entire Library
|
||||
func (jncApi *Api) FetchVolumeInfo(volume Volume) (Volume, error) {
|
||||
fmt.Println("Fetching Volume details...")
|
||||
res, err := http.Get(ApiV2Url + "volumes/" + volume.Id + "?" + jncApi.ReturnFormat() + "&" + jncApi.AuthParam())
|
||||
res, err := http.Get(jncApi._apiUrl + "volumes/" + volume.Id + "?" + jncApi.ReturnFormat() + "&" + jncApi.AuthParam())
|
||||
if err != nil {
|
||||
return volume, err
|
||||
}
|
||||
|
|
88
main.go
88
main.go
|
@ -6,6 +6,7 @@ import (
|
|||
"bytes"
|
||||
"container/list"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/beevik/etree"
|
||||
"io"
|
||||
|
@ -67,6 +68,14 @@ func ClearScreen() {
|
|||
}
|
||||
}
|
||||
|
||||
func GetArg(argKey string) (string, error) {
|
||||
if slices.Contains(os.Args, argKey) {
|
||||
idx := slices.Index(os.Args, argKey)
|
||||
return os.Args[idx+1], nil
|
||||
}
|
||||
return "", errors.New("arg " + argKey + " not found")
|
||||
}
|
||||
|
||||
func main() {
|
||||
interactive := false
|
||||
if slices.Contains(os.Args, "-I") {
|
||||
|
@ -77,19 +86,27 @@ func main() {
|
|||
panic("automatic mode is not implemented yet")
|
||||
}
|
||||
|
||||
var downloadDir string
|
||||
if slices.Contains(os.Args, "-d") {
|
||||
idx := slices.Index(os.Args, "-d")
|
||||
downloadDir = os.Args[idx+1]
|
||||
if strings.LastIndex(downloadDir, "/") != len(downloadDir)-1 {
|
||||
downloadDir = downloadDir + "/"
|
||||
}
|
||||
} else {
|
||||
panic("working directory not specified")
|
||||
domain, err := GetArg("-D")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
serLan, err := GetArg("-L")
|
||||
if serLan != "" {
|
||||
serLan = " " + serLan
|
||||
}
|
||||
|
||||
downloadDir, err := GetArg("-d")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if strings.LastIndex(downloadDir, "/") != len(downloadDir)-1 {
|
||||
downloadDir = downloadDir + "/"
|
||||
}
|
||||
|
||||
// initialize the J-Novel Club Instance
|
||||
jnovel := jnc.NewJNC()
|
||||
jnovel := jnc.NewJNC(domain)
|
||||
|
||||
var username string
|
||||
if slices.Contains(os.Args, "-u") {
|
||||
|
@ -109,7 +126,7 @@ func main() {
|
|||
}
|
||||
jnovel.SetPassword(password)
|
||||
|
||||
err := jnovel.Login()
|
||||
err = jnovel.Login()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -153,12 +170,12 @@ func main() {
|
|||
serie := seriesList[s]
|
||||
if mode == "3" || mode == "4" {
|
||||
if serie.Info.Type == "MANGA" {
|
||||
HandleSeries(jnovel, serie, downloadDir, updatedOnly == "Y" || updatedOnly == "y")
|
||||
HandleSeries(jnovel, serie, downloadDir, updatedOnly == "Y" || updatedOnly == "y", serLan)
|
||||
}
|
||||
}
|
||||
if mode == "3" || mode == "5" {
|
||||
if serie.Info.Type == "NOVEL" {
|
||||
HandleSeries(jnovel, serie, downloadDir, updatedOnly == "Y" || updatedOnly == "y")
|
||||
HandleSeries(jnovel, serie, downloadDir, updatedOnly == "Y" || updatedOnly == "y", serLan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +202,7 @@ func main() {
|
|||
serie := seriesList[seriesNumber-1]
|
||||
|
||||
if mode == "2" {
|
||||
HandleSeries(jnovel, serie, downloadDir, false)
|
||||
HandleSeries(jnovel, serie, downloadDir, false, serLan)
|
||||
} else {
|
||||
ClearScreen()
|
||||
fmt.Println("\n###[Volume Selection]###")
|
||||
|
@ -206,44 +223,44 @@ func main() {
|
|||
}
|
||||
|
||||
volume := serie.Volumes[volumeNumber-1]
|
||||
HandleVolume(jnovel, serie, volume, downloadDir)
|
||||
HandleVolume(jnovel, serie, volume, downloadDir, serLan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSeries(jnovel jnc.Api, serie jnc.SerieAugmented, downloadDir string, updatedOnly bool) {
|
||||
func HandleSeries(jnovel jnc.Api, serie jnc.SerieAugmented, downloadDir string, updatedOnly bool, titleSuffix string) {
|
||||
for v := range serie.Volumes {
|
||||
volume := serie.Volumes[v]
|
||||
if updatedOnly {
|
||||
if len(volume.Downloads) != 0 && volume.UpdateAvailable() {
|
||||
downloadDir = PrepareSerieDirectory(serie, volume, downloadDir)
|
||||
HandleVolume(jnovel, serie, volume, downloadDir)
|
||||
downloadDir = PrepareSerieDirectory(serie, volume, downloadDir, titleSuffix)
|
||||
HandleVolume(jnovel, serie, volume, downloadDir, titleSuffix)
|
||||
}
|
||||
} else {
|
||||
downloadDir = PrepareSerieDirectory(serie, volume, downloadDir)
|
||||
HandleVolume(jnovel, serie, volume, downloadDir)
|
||||
downloadDir = PrepareSerieDirectory(serie, volume, downloadDir, titleSuffix)
|
||||
HandleVolume(jnovel, serie, volume, downloadDir, titleSuffix)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func PrepareSerieDirectory(serie jnc.SerieAugmented, volume jnc.VolumeAugmented, downloadDir string) string {
|
||||
func PrepareSerieDirectory(serie jnc.SerieAugmented, volume jnc.VolumeAugmented, downloadDir string, titleSuffix string) string {
|
||||
splits := strings.Split(downloadDir, "/")
|
||||
// last element of this split is always empty due to the trailing slash
|
||||
if splits[len(splits)-2] != serie.Info.Title {
|
||||
if splits[len(splits)-2] != serie.Info.Title+titleSuffix {
|
||||
if serie.Info.Title == "Ascendance of a Bookworm" {
|
||||
partSplits := strings.Split(volume.Info.ShortTitle, " ")
|
||||
part := " " + partSplits[0] + " " + partSplits[1]
|
||||
|
||||
if strings.Split(splits[len(splits)-2], " ")[0] == "Ascendance" {
|
||||
splits[len(splits)-2] = serie.Info.Title + part
|
||||
splits[len(splits)-2] = serie.Info.Title + part + titleSuffix
|
||||
} else {
|
||||
splits[len(splits)-1] = serie.Info.Title + part
|
||||
splits[len(splits)-1] = serie.Info.Title + part + titleSuffix
|
||||
splits = append(splits, "")
|
||||
}
|
||||
downloadDir = strings.Join(splits, "/")
|
||||
} else {
|
||||
downloadDir += serie.Info.Title + "/"
|
||||
downloadDir += serie.Info.Title + titleSuffix + "/"
|
||||
}
|
||||
|
||||
_, err := os.Stat(downloadDir)
|
||||
|
@ -258,7 +275,7 @@ func PrepareSerieDirectory(serie jnc.SerieAugmented, volume jnc.VolumeAugmented,
|
|||
return downloadDir
|
||||
}
|
||||
|
||||
func HandleVolume(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc.VolumeAugmented, downloadDir string) {
|
||||
func HandleVolume(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc.VolumeAugmented, downloadDir string, titleSuffix string) {
|
||||
var downloadLink string
|
||||
if len(volume.Downloads) == 0 {
|
||||
fmt.Printf("Volume %s currently has no downloads available. Skipping \n", volume.Info.Title)
|
||||
|
@ -270,10 +287,10 @@ func HandleVolume(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc.VolumeAug
|
|||
downloadLink = volume.Downloads[0].Link
|
||||
}
|
||||
|
||||
DownloadAndProcessEpub(jnovel, serie, volume, downloadLink, downloadDir)
|
||||
DownloadAndProcessEpub(jnovel, serie, volume, downloadLink, downloadDir, titleSuffix)
|
||||
}
|
||||
|
||||
func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc.VolumeAugmented, downloadLink string, downloadDirectory string) {
|
||||
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",
|
||||
|
@ -344,7 +361,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
|||
fmt.Println("No chapters found, chapter name likely not supported")
|
||||
}
|
||||
|
||||
basePath := downloadDirectory + volume.Info.Title + "/"
|
||||
basePath := downloadDirectory + volume.Info.Title + titleSuffix + "/"
|
||||
PrepareVolumeDirectory(basePath)
|
||||
volume.Info, err = jnovel.FetchVolumeInfo(volume.Info)
|
||||
if err != nil {
|
||||
|
@ -427,7 +444,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
|||
}
|
||||
}
|
||||
|
||||
comicInfo, err := GenerateChapterMetadata(volume, serie, len(chap.pages), language, chap.chDisplay)
|
||||
comicInfo, err := GenerateChapterMetadata(volume, serie, len(chap.pages), language, chap.chDisplay, titleSuffix)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
@ -468,10 +485,10 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
|||
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]
|
||||
title = serie.Info.Title + " " + splits[0] + " " + splits[1] + titleSuffix
|
||||
number = splits[3]
|
||||
} else {
|
||||
title = serie.Info.Title
|
||||
title = serie.Info.Title + titleSuffix
|
||||
number = strconv.Itoa(volume.Info.Number)
|
||||
}
|
||||
|
||||
|
@ -543,7 +560,7 @@ func DownloadAndProcessEpub(jnovel jnc.Api, serie jnc.SerieAugmented, volume jnc
|
|||
}
|
||||
}
|
||||
|
||||
func GenerateChapterMetadata(volume jnc.VolumeAugmented, serie jnc.SerieAugmented, pageCount int, language string, chapterNumber string) ([]byte, error) {
|
||||
func GenerateChapterMetadata(volume jnc.VolumeAugmented, serie jnc.SerieAugmented, pageCount int, language string, chapterNumber string, titleSuffix string) ([]byte, error) {
|
||||
comicInfo := ComicInfo{
|
||||
XMLName: "ComicInfo",
|
||||
XMLNS: "http://www.w3.org/2001/XMLSchema-instance",
|
||||
|
@ -554,7 +571,7 @@ func GenerateChapterMetadata(volume jnc.VolumeAugmented, serie jnc.SerieAugmente
|
|||
sInfo := serie.Info
|
||||
|
||||
comicInfo.Series = sInfo.Title
|
||||
comicInfo.Title = vInfo.Title
|
||||
comicInfo.Title = vInfo.Title + titleSuffix
|
||||
comicInfo.Number = chapterNumber
|
||||
comicInfo.Volume = vInfo.Number
|
||||
|
||||
|
@ -796,8 +813,9 @@ func GetLastValidChapterNumber(currentChapter *list.Element) Chapter {
|
|||
if chapterData.numberMain != -1 && chapterData.numberSub == 0 {
|
||||
return chapterData
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
return currentChapter.Value.(Chapter)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue