diff --git a/go/main/controller/download.go b/go/main/controller/download.go index 39c9bf8..874da1d 100644 --- a/go/main/controller/download.go +++ b/go/main/controller/download.go @@ -1,12 +1,15 @@ package controller import ( + "bytes" "fmt" + "image" "io/ioutil" "os" + "path" "path/filepath" "pikapi/main/database/comic_center" - utils2 "pikapi/main/utils" + "pikapi/main/utils" "time" ) @@ -225,6 +228,7 @@ func downloadSummaryDownload() { } if over { // 如果所有章节下载完成则下载成功 + downloadAndExportLogo(downloadingComic) err = comic_center.DownloadSuccess(downloadingComic.ID) if err != nil { panic(err) @@ -297,7 +301,7 @@ func downloadInitPicture() { // 下载指定图片 func downloadThePicture(picturePoint *comic_center.ComicDownloadPicture) error { // 为了不和页面前端浏览的数据冲突, 使用url做hash锁 - lock := utils2.HashLock(fmt.Sprintf("%s$%s", picturePoint.FileServer, picturePoint.Path)) + lock := utils.HashLock(fmt.Sprintf("%s$%s", picturePoint.FileServer, picturePoint.Path)) lock.Lock() defer lock.Unlock() // 图片保存位置使用相对路径储存, 使用绝对路径操作 @@ -315,12 +319,14 @@ func downloadThePicture(picturePoint *comic_center.ComicDownloadPicture) error { // 将图片保存到文件 dir := filepath.Dir(realPath) if _, err := os.Stat(dir); os.IsNotExist(err) { - os.Mkdir(dir, utils2.CreateDirMode) + os.Mkdir(dir, utils.CreateDirMode) } - err = ioutil.WriteFile(downloadPath(picturePath), buff, utils2.CreateFileMode) + err = ioutil.WriteFile(downloadPath(picturePath), buff, utils.CreateFileMode) if err != nil { return err } + // 下载时同时导出 + downloadAndExport(downloadingComic, downloadingEp, picturePoint, buff, format) // 存入数据库 return comic_center.PictureSuccess( picturePoint.ComicId, @@ -364,3 +370,92 @@ func downloadSummaryEp() { // 去加载下一个EP go downloadLoadEp() } + +var downloadAndExportPath = "" + +func downloadAndExport( + downloadingComic *comic_center.ComicDownload, + downloadingEp *comic_center.ComicDownloadEp, + downloadingPicture *comic_center.ComicDownloadPicture, + buff []byte, + format string, +) { + if downloadAndExportPath == "" { + return + } + if i, e := os.Stat(downloadAndExportPath); e == nil { + if i.IsDir() { + // 进入漫画目录 + comicDir := path.Join(downloadAndExportPath, utils.ReasonableFileName(downloadingComic.Title)) + i, e = os.Stat(comicDir) + if e != nil { + if os.IsNotExist(e) { + e = os.Mkdir(comicDir, utils.CreateDirMode) + } else { + return + } + } + if e != nil { + return + } + // 进入章节目录 + epDir := path.Join(comicDir, utils.ReasonableFileName(fmt.Sprintf("%02d - ", downloadingEp.EpOrder)+downloadingEp.Title)) + i, e = os.Stat(epDir) + if e != nil { + if os.IsNotExist(e) { + e = os.Mkdir(epDir, utils.CreateDirMode) + } else { + return + } + } + if e != nil { + return + } + // 写入文件 + filePath := path.Join(epDir, fmt.Sprintf("%03d.%s", downloadingPicture.RankInEp, jFormat(format))) + ioutil.WriteFile(filePath, buff, utils.CreateFileMode) + } + } +} + +func downloadAndExportLogo( + downloadingComic *comic_center.ComicDownload, +) { + if downloadAndExportPath == "" { + return + } + comicLogoPath := downloadPath(path.Join(downloadingComic.ID, "logo")) + if _, e := os.Stat(comicLogoPath); e == nil { + buff, e := ioutil.ReadFile(comicLogoPath) + if e == nil { + _, f, e := image.Decode(bytes.NewBuffer(buff)) + if e == nil { + if i, e := os.Stat(downloadAndExportPath); e == nil { + if i.IsDir() { + // 进入漫画目录 + comicDir := path.Join(downloadAndExportPath, utils.ReasonableFileName(downloadingComic.Title)) + i, e = os.Stat(comicDir) + if e != nil { + if os.IsNotExist(e) { + e = os.Mkdir(comicDir, utils.CreateDirMode) + } + } + if e != nil { + return + } + // 写入文件 + filePath := path.Join(comicDir, fmt.Sprintf("%s.%s", "logo", jFormat(f))) + ioutil.WriteFile(filePath, buff, utils.CreateFileMode) + } + } + } + } + } +} + +func jFormat(format string) string { + if format == "jpeg" { + return "jpg" + } + return format +} diff --git a/go/main/controller/export.go b/go/main/controller/export.go index 6fe70f2..beb6bef 100644 --- a/go/main/controller/export.go +++ b/go/main/controller/export.go @@ -14,7 +14,6 @@ import ( "path" "pikapi/main/database/comic_center" "pikapi/main/utils" - "strings" "time" ) @@ -91,7 +90,7 @@ func exportComicDownload(params string) (filePath string, err error) { err = errors.New("not download finish") return } - filePath = path.Join(dir, fmt.Sprintf("%s-%s.zip", reasonablePath(comic.Title), time.Now().Format("2006_01_02_15_04_05.999"))) + filePath = path.Join(dir, fmt.Sprintf("%s-%s.zip", utils.ReasonableFileName(comic.Title), time.Now().Format("2006_01_02_15_04_05.999"))) println(fmt.Sprintf("ZIP : %s", filePath)) fileStream, err := os.Create(filePath) if err != nil { @@ -109,17 +108,6 @@ func exportComicDownload(params string) (filePath string, err error) { return } -func reasonablePath(title string) string { - title = strings.ReplaceAll(title, "\\", "_") - title = strings.ReplaceAll(title, "/", "_") - title = strings.ReplaceAll(title, "*", "_") - title = strings.ReplaceAll(title, "?", "_") - title = strings.ReplaceAll(title, "<", "_") - title = strings.ReplaceAll(title, ">", "_") - title = strings.ReplaceAll(title, "|", "_") - return title -} - func exportComicDownloadFetch(comicId string, onWriteFile func(path string, size int64) (io.Writer, error)) error { comic, err := comic_center.FindComicDownloadById(comicId) if err != nil { @@ -384,7 +372,7 @@ func exportComicDownloadToJPG(params string) error { if !comic.DownloadFinished { return errors.New("not download finish") } - dirPath := path.Join(dir, fmt.Sprintf("%s-%s", reasonablePath(comic.Title), time.Now().Format("2006_01_02_15_04_05.999"))) + dirPath := path.Join(dir, fmt.Sprintf("%s-%s", utils.ReasonableFileName(comic.Title), time.Now().Format("2006_01_02_15_04_05.999"))) println(fmt.Sprintf("DIR : %s", dirPath)) err = os.Mkdir(dirPath, utils.CreateDirMode) if err != nil { diff --git a/go/main/controller/pikapi.go b/go/main/controller/pikapi.go index ddf63fa..cd9d5fa 100644 --- a/go/main/controller/pikapi.go +++ b/go/main/controller/pikapi.go @@ -29,6 +29,7 @@ func InitPlugin(_remoteDir string, _downloadDir string, _tmpDir string) { downloadDir = _downloadDir tmpDir = _tmpDir comic_center.ResetAll() + downloadAndExportPath = loadDownloadAndExportPath() go downloadBackground() downloadRunning = true } @@ -59,6 +60,19 @@ func loadProperty(params string) (string, error) { return properties.LoadProperty(paramsStruct.Name, paramsStruct.DefaultValue) } +func saveDownloadAndExportPath(value string) error { + err := properties.SaveProperty("downloadAndExportPath", value) + if err == nil { + downloadAndExportPath = value + } + return err +} + +func loadDownloadAndExportPath() string { + p, _ := properties.LoadProperty("downloadAndExportPath", "") + return p +} + func setSwitchAddress(nSwitchAddress string) error { err := properties.SaveSwitchAddress(nSwitchAddress) if err != nil { @@ -601,6 +615,10 @@ func FlatInvoke(method string, params string) (string, error) { return "", convertImageToJPEG100(params) case "specialDownloadTitle": return specialDownloadTitle(params) + case "loadDownloadAndExportPath": + return loadDownloadAndExportPath(), nil + case "saveDownloadAndExportPath": + return "", saveDownloadAndExportPath(params) } return "", errors.New("method not found : " + method) } diff --git a/go/main/utils/file.go b/go/main/utils/file.go index eed4ad8..cb6a4e3 100644 --- a/go/main/utils/file.go +++ b/go/main/utils/file.go @@ -2,6 +2,7 @@ package utils import ( "os" + "strings" ) func Mkdir(dir string) { @@ -16,3 +17,14 @@ func Mkdir(dir string) { } } } + +func ReasonableFileName(title string) string { + title = strings.ReplaceAll(title, "\\", "_") + title = strings.ReplaceAll(title, "/", "_") + title = strings.ReplaceAll(title, "*", "_") + title = strings.ReplaceAll(title, "?", "_") + title = strings.ReplaceAll(title, "<", "_") + title = strings.ReplaceAll(title, ">", "_") + title = strings.ReplaceAll(title, "|", "_") + return title +} diff --git a/lib/basic/Method.dart b/lib/basic/Method.dart index efca422..8e8acf0 100644 --- a/lib/basic/Method.dart +++ b/lib/basic/Method.dart @@ -572,4 +572,13 @@ class Method { return _channel.invokeMethod("migrate", {"path": path}); } + /// 下载的同时导出-配置获取 + Future loadDownloadAndExportPath() { + return _flatInvoke("loadDownloadAndExportPath", ""); + } + + /// 下载的同时导出-设置 + Future saveDownloadAndExportPath(String folder) { + return _flatInvoke("saveDownloadAndExportPath", folder); + } } diff --git a/lib/basic/config/DownloadAndExportPath.dart b/lib/basic/config/DownloadAndExportPath.dart new file mode 100644 index 0000000..01346f0 --- /dev/null +++ b/lib/basic/config/DownloadAndExportPath.dart @@ -0,0 +1,60 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:pikapi/basic/Common.dart'; +import 'package:pikapi/basic/Cross.dart'; + +import '../Method.dart'; + +late String _downloadAndExportPath; + +Future initDownloadAndExportPath() async { + _downloadAndExportPath = await method.loadDownloadAndExportPath(); +} + +Widget downloadAndExportPathSetting() { + if (Platform.isWindows || + Platform.isMacOS || + Platform.isAndroid || + Platform.isLinux) { + return StatefulBuilder( + builder: (BuildContext context, void Function(void Function()) setState) { + return ListTile( + title: Text("下载的同时导出到文件系统"), + subtitle: Text(_downloadAndExportPath), + onTap: () async { + if (_downloadAndExportPath == "") { + bool b = await confirmDialog( + context, + "下载的同时导出到文件系统", + "您即将选择一个目录, 如果文件系统可写, 下载的同时会为您自动导出一份", + ); + if (b) { + String? folder = await chooseFolder(context); + if (folder != null) { + await method.saveDownloadAndExportPath(folder); + _downloadAndExportPath = folder; + setState(() {}); + } + } + } else { + bool b = await confirmDialog( + context, + "下载的同时导出到文件系统", + "您确定取消下载并导出的功能吗? 取消之后您可以再次点击设置", + ); + if (b) { + var folder = ""; + await method.saveDownloadAndExportPath(folder); + _downloadAndExportPath = folder; + setState(() {}); + } + } + }, + ); + }, + ); + } + return Container(); +} diff --git a/lib/screens/InitScreen.dart b/lib/screens/InitScreen.dart index 7f4d252..94c2880 100644 --- a/lib/screens/InitScreen.dart +++ b/lib/screens/InitScreen.dart @@ -5,6 +5,7 @@ import 'package:pikapi/basic/config/AutoClean.dart'; import 'package:pikapi/basic/config/AutoFullScreen.dart'; import 'package:pikapi/basic/config/ChooserRoot.dart'; import 'package:pikapi/basic/config/ContentFailedReloadAction.dart'; +import 'package:pikapi/basic/config/DownloadAndExportPath.dart'; import 'package:pikapi/basic/config/FullScreenAction.dart'; import 'package:pikapi/basic/config/FullScreenUI.dart'; import 'package:pikapi/basic/config/KeyboardController.dart'; @@ -61,6 +62,7 @@ class _InitScreenState extends State { await initAndroidDisplayMode(); await initChooserRoot(); await initTimeZone(); + await initDownloadAndExportPath(); // 登录, 如果token失效重新登录, 网络不好的时候可能需要1分钟 if (await method.preLogin()) { // 如果token或username+password有效则直接进入登录好的界面 diff --git a/lib/screens/SettingsScreen.dart b/lib/screens/SettingsScreen.dart index 3bf3269..94d8f36 100644 --- a/lib/screens/SettingsScreen.dart +++ b/lib/screens/SettingsScreen.dart @@ -8,6 +8,7 @@ import 'package:pikapi/basic/config/AutoClean.dart'; import 'package:pikapi/basic/config/AutoFullScreen.dart'; import 'package:pikapi/basic/config/ChooserRoot.dart'; import 'package:pikapi/basic/config/ContentFailedReloadAction.dart'; +import 'package:pikapi/basic/config/DownloadAndExportPath.dart'; import 'package:pikapi/basic/config/FullScreenAction.dart'; import 'package:pikapi/basic/config/FullScreenUI.dart'; import 'package:pikapi/basic/config/KeyboardController.dart'; @@ -129,6 +130,7 @@ class _SettingsScreenState extends State { setState(() {}); }, ), + downloadAndExportPathSetting(), fontSetting(), Divider(), migrate(),