From 75745b9431a7dfe6b62433f1473b9f91998eed5e Mon Sep 17 00:00:00 2001 From: nite Date: Thu, 17 Jul 2025 14:03:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20kavita=20=E6=97=A0=E6=B3=95=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E5=8A=A0=E8=BD=BD=E5=AD=97=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- downloader/bilinovel/bilinovel.go | 85 +++---------------- downloader/bilinovel/epub.go | 2 +- downloader/bilinovel/style.css.go | 8 +- model/container_opf.go | 1 - model/toc_ncx.go | 47 ---------- template/container.xml.templ | 2 +- template/container.xml_templ.go | 2 +- template/content.xhtml.templ | 2 +- template/content.xhtml_templ.go | 2 +- template/cover.xhtml.templ | 37 ++++++++ ...{toc.ncx_templ.go => cover.xhtml_templ.go} | 45 ++-------- template/toc.ncx.templ | 25 ------ 13 files changed, 70 insertions(+), 193 deletions(-) delete mode 100644 model/toc_ncx.go create mode 100644 template/cover.xhtml.templ rename template/{toc.ncx_templ.go => cover.xhtml_templ.go} (57%) delete mode 100644 template/toc.ncx.templ diff --git a/README.md b/README.md index bb6bfb8..537f7fd 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,7 @@ ``` 3. 对自动生成的 epub 格式不满意可以自行修改后使用命令打包 + ```bash bilinovel-downloader pack -d <目录路径> ``` - -## 注意事项 - -如果使用 [Kavita](https://github.com/Kareadita/Kavita) 阅读可能出现部分文字乱码问题,这是 Kavita 对 EPUB 格式支持不足导致的,目前在等待修复。 diff --git a/downloader/bilinovel/bilinovel.go b/downloader/bilinovel/bilinovel.go index 7110829..ac0f777 100644 --- a/downloader/bilinovel/bilinovel.go +++ b/downloader/bilinovel/bilinovel.go @@ -260,7 +260,7 @@ func downloadVolume(volume *model.Volume, outputPath string) error { return fmt.Errorf("failed to write volume: %v", err) } - coverPath := filepath.Join(outputPath, "OEBPS/Images/cover.jpg") + coverPath := filepath.Join(outputPath, "cover.jpeg") err = os.MkdirAll(path.Dir(coverPath), 0755) if err != nil { return fmt.Errorf("failed to create cover directory: %v", err) @@ -279,10 +279,7 @@ func downloadVolume(volume *model.Volume, outputPath string) error { if err != nil { return fmt.Errorf("failed to create cover file: %v", err) } - err = template.ContentXHTML(&model.Chapter{ - Title: "封面", - Content: fmt.Sprintf(``, path.Ext(volume.Cover)), - }).Render(context.Background(), file) + err = template.CoverXHTML(fmt.Sprintf(`../../cover%s`, strings.ReplaceAll(path.Ext(volume.Cover), "jpg", "jpeg"))).Render(context.Background(), file) if err != nil { return fmt.Errorf("failed to render cover: %v", err) } @@ -332,11 +329,6 @@ func downloadVolume(volume *model.Volume, outputPath string) error { return fmt.Errorf("failed to create content opf: %v", err) } - err = CreateTocNCX(outputPath, u.String(), volume) - if err != nil { - return fmt.Errorf("failed to create toc ncx: %v", err) - } - err = CreateEpub(outputPath) if err != nil { return fmt.Errorf("failed to create epub: %v", err) @@ -530,7 +522,7 @@ func CreateContentOPF(dirPath string, uuid string, volume *model.Volume) error { Metas: []model.DublinCoreMeta{ { Name: "cover", - Content: "images-cover" + path.Ext(volume.Cover), + Content: "cover", }, { Property: "dcterms:modified", @@ -549,42 +541,38 @@ func CreateContentOPF(dirPath string, uuid string, volume *model.Volume) error { manifest := &model.Manifest{ Items: make([]model.ManifestItem, 0), } - manifest.Items = append(manifest.Items, model.ManifestItem{ - ID: "ncx", - Link: "toc.ncx", - Media: "application/x-dtbncx+xml", - }) manifest.Items = append(manifest.Items, model.ManifestItem{ ID: "cover.xhtml", - Link: "Text/cover.xhtml", + Link: "OEBPS/Text/cover.xhtml", Media: "application/xhtml+xml", }) manifest.Items = append(manifest.Items, model.ManifestItem{ ID: "contents.xhtml", - Link: "Text/contents.xhtml", + Link: "OEBPS/Text/contents.xhtml", Media: "application/xhtml+xml", Properties: "nav", }) manifest.Items = append(manifest.Items, model.ManifestItem{ - ID: "images-cover" + path.Ext(volume.Cover), - Link: fmt.Sprintf("Images/cover%s", path.Ext(volume.Cover)), - Media: fmt.Sprintf("image/%s", strings.ReplaceAll(strings.TrimPrefix(path.Ext(volume.Cover), "."), "jpg", "jpeg")), + ID: "cover", + Link: fmt.Sprintf("cover%s", strings.ReplaceAll(path.Ext(volume.Cover), "jpg", "jpeg")), + Media: fmt.Sprintf("image/%s", strings.ReplaceAll(strings.TrimPrefix(path.Ext(volume.Cover), "."), "jpg", "jpeg")), + Properties: "cover-image", }) manifest.Items = append(manifest.Items, model.ManifestItem{ ID: "read.ttf", - Link: "Fonts/read.ttf", + Link: "OEBPS/Fonts/read.ttf", Media: "application/vnd.ms-opentype", }) for _, chapter := range volume.Chapters { manifest.Items = append(manifest.Items, model.ManifestItem{ ID: path.Base(chapter.TextOEBPSPath), - Link: chapter.TextOEBPSPath, + Link: "OEBPS/" + chapter.TextOEBPSPath, Media: "application/xhtml+xml", }) for _, image := range chapter.ImageOEBPSPaths { item := model.ManifestItem{ ID: strings.Join(strings.Split(strings.ToLower(image), string(filepath.Separator)), "-"), - Link: image, + Link: "OEBPS/" + image, } item.Media = fmt.Sprintf("image/%s", strings.ReplaceAll(strings.TrimPrefix(path.Ext(volume.Cover), "."), "jpg", "jpeg")) manifest.Items = append(manifest.Items, item) @@ -592,7 +580,7 @@ func CreateContentOPF(dirPath string, uuid string, volume *model.Volume) error { } manifest.Items = append(manifest.Items, model.ManifestItem{ ID: "style", - Link: "Styles/style.css", + Link: "style.css", Media: "text/css", }) @@ -606,7 +594,7 @@ func CreateContentOPF(dirPath string, uuid string, volume *model.Volume) error { }) } } - contentOPFPath := filepath.Join(dirPath, "OEBPS/content.opf") + contentOPFPath := filepath.Join(dirPath, "content.opf") err := os.MkdirAll(path.Dir(contentOPFPath), 0755) if err != nil { return fmt.Errorf("failed to create content directory: %v", err) @@ -622,51 +610,6 @@ func CreateContentOPF(dirPath string, uuid string, volume *model.Volume) error { return nil } -func CreateTocNCX(dirPath string, uuid string, volume *model.Volume) error { - navMap := &model.NavMap{Points: make([]*model.NavPoint, 0)} - navMap.Points = append(navMap.Points, &model.NavPoint{ - Id: "cover", - PlayOrder: 1, - Label: "封面", - Content: model.NavPointContent{Src: "Text/cover.xhtml"}, - }) - navMap.Points = append(navMap.Points, &model.NavPoint{ - Id: "contents", - PlayOrder: 2, - Label: "目录", - Content: model.NavPointContent{Src: "Text/contents.xhtml"}, - }) - for idx, chapter := range volume.Chapters { - navMap.Points = append(navMap.Points, &model.NavPoint{ - Id: fmt.Sprintf("chapter-%03v", idx+1), - PlayOrder: len(navMap.Points) + 1, - Label: chapter.Title, - Content: model.NavPointContent{Src: chapter.TextOEBPSPath}, - }) - } - - head := &model.TocNCXHead{ - Meta: []model.TocNCXHeadMeta{ - {Name: "dtb:uid", Content: fmt.Sprintf("urn:uuid:%s", uuid)}, - }, - } - - ncxPath := filepath.Join(dirPath, "OEBPS/toc.ncx") - err := os.MkdirAll(path.Dir(ncxPath), 0755) - if err != nil { - return fmt.Errorf("failed to create toc directory: %v", err) - } - file, err := os.Create(ncxPath) - if err != nil { - return fmt.Errorf("failed to create toc file: %v", err) - } - err = template.TocNCX(volume.Title, head, navMap).Render(context.Background(), file) - if err != nil { - return fmt.Errorf("failed to render toc: %v", err) - } - return nil -} - //go:embed read.ttf var readTTF []byte diff --git a/downloader/bilinovel/epub.go b/downloader/bilinovel/epub.go index 318c841..81846a8 100644 --- a/downloader/bilinovel/epub.go +++ b/downloader/bilinovel/epub.go @@ -31,7 +31,7 @@ func CreateEpub(path string) error { return err } - err = addStringToZip(zipWriter, "OEBPS/Styles/style.css", StyleCSS, zip.Deflate) + err = addStringToZip(zipWriter, "style.css", StyleCSS, zip.Deflate) if err != nil { return err } diff --git a/downloader/bilinovel/style.css.go b/downloader/bilinovel/style.css.go index 08c5ae9..3efa107 100644 --- a/downloader/bilinovel/style.css.go +++ b/downloader/bilinovel/style.css.go @@ -3,11 +3,15 @@ package bilinovel const StyleCSS = ` @font-face { font-family: "MI LANTING"; - src: url(../Fonts/read.ttf); + src: url(OEBPS/Fonts/read.ttf); } .read-font { - font-family: "MI LANTING", serif !important; + display: block; + font-family: "MI LANTING", serif; + font-size: 1.33333em; + text-indent: 2em; + margin: 0.8em 0; } body > div { diff --git a/model/container_opf.go b/model/container_opf.go index c869ddb..965461d 100644 --- a/model/container_opf.go +++ b/model/container_opf.go @@ -158,7 +158,6 @@ type Spine struct { } func (s *Spine) Marshal() (string, error) { - s.Toc = "ncx" xmlBytes, err := xml.Marshal(s) if err != nil { return "", err diff --git a/model/toc_ncx.go b/model/toc_ncx.go deleted file mode 100644 index afa5512..0000000 --- a/model/toc_ncx.go +++ /dev/null @@ -1,47 +0,0 @@ -package model - -import "encoding/xml" - -type TocNCXHead struct { - XMLName xml.Name `xml:"head"` - Meta []TocNCXHeadMeta `xml:"meta"` -} - -type TocNCXHeadMeta struct { - XMLName xml.Name `xml:"meta"` - Content string `xml:"content,attr"` - Name string `xml:"name,attr"` -} - -func (h *TocNCXHead) Marshal() (string, error) { - xmlBytes, err := xml.Marshal(h) - if err != nil { - return "", err - } - return string(xmlBytes), nil -} - -type NavPoint struct { - Id string `xml:"id,attr"` - PlayOrder int `xml:"playOrder,attr"` - Label string `xml:"navLabel>text"` - Content NavPointContent `xml:"content"` - NavPoints []*NavPoint `xml:"navPoint"` -} - -type NavPointContent struct { - Src string `xml:"src,attr"` -} - -type NavMap struct { - XMLName xml.Name `xml:"navMap"` - Points []*NavPoint `xml:"navPoint"` -} - -func (n *NavMap) Marshal() (string, error) { - xmlBytes, err := xml.Marshal(n) - if err != nil { - return "", err - } - return string(xmlBytes), nil -} diff --git a/template/container.xml.templ b/template/container.xml.templ index 9796422..65ce672 100644 --- a/template/container.xml.templ +++ b/template/container.xml.templ @@ -4,7 +4,7 @@ templ ContainerXML() { @templ.Raw(``) - + } diff --git a/template/container.xml_templ.go b/template/container.xml_templ.go index 8a4a31f..35a864a 100644 --- a/template/container.xml_templ.go +++ b/template/container.xml_templ.go @@ -33,7 +33,7 @@ func ContainerXML() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/template/content.xhtml.templ b/template/content.xhtml.templ index 7e59345..18b9633 100644 --- a/template/content.xhtml.templ +++ b/template/content.xhtml.templ @@ -8,7 +8,7 @@ templ ContentXHTML(content *model.Chapter) { { content.Title } - @templ.Raw(``) + @templ.Raw(``)
diff --git a/template/content.xhtml_templ.go b/template/content.xhtml_templ.go index 69ea939..fcac00a 100644 --- a/template/content.xhtml_templ.go +++ b/template/content.xhtml_templ.go @@ -52,7 +52,7 @@ func ContentXHTML(content *model.Chapter) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templ.Raw(``).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = templ.Raw(``).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/template/cover.xhtml.templ b/template/cover.xhtml.templ new file mode 100644 index 0000000..361f9af --- /dev/null +++ b/template/cover.xhtml.templ @@ -0,0 +1,37 @@ +package template + +templ CoverXHTML(coverPath string) { + @templ.Raw(` +`) + + + Cover + + + +
+ + + +
+ + +} diff --git a/template/toc.ncx_templ.go b/template/cover.xhtml_templ.go similarity index 57% rename from template/toc.ncx_templ.go rename to template/cover.xhtml_templ.go index 9eb7a11..3a8f27c 100644 --- a/template/toc.ncx_templ.go +++ b/template/cover.xhtml_templ.go @@ -8,9 +8,7 @@ package template import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" -import "bilinovel-downloader/model" - -func TocNCX(title string, head *model.TocNCXHead, navMap *model.NavMap) templ.Component { +func CoverXHTML(coverPath string) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -31,54 +29,25 @@ func TocNCX(title string, head *model.TocNCXHead, navMap *model.NavMap) templ.Co templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templ.Raw(``).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = templ.Raw(` +`).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templ.Raw(``).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if head != nil { - head, err := head.Marshal() - if err == nil { - templ_7745c5c3_Err = templ.Raw(head).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Cover
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if navMap != nil { - navMap, err := navMap.Marshal() - if err == nil { - templ_7745c5c3_Err = templ.Raw(navMap).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/template/toc.ncx.templ b/template/toc.ncx.templ deleted file mode 100644 index 86e9ab3..0000000 --- a/template/toc.ncx.templ +++ /dev/null @@ -1,25 +0,0 @@ -package template - -import "bilinovel-downloader/model" - -templ TocNCX(title string, head *model.TocNCXHead, navMap *model.NavMap) { - @templ.Raw(``) - @templ.Raw(``) - - if head != nil { - {{ head, err := head.Marshal() }} - if err == nil { - @templ.Raw(head) - } - } - - { title } - - if navMap != nil { - {{ navMap, err := navMap.Marshal() }} - if err == nil { - @templ.Raw(navMap) - } - } - -}