diff --git a/go/pikapi/config/common.go b/go/pikapi/config/common.go index 2c8d1f1..7e46166 100644 --- a/go/pikapi/config/common.go +++ b/go/pikapi/config/common.go @@ -9,7 +9,7 @@ import ( "pgo/pikapi/utils" ) -// InitApplication 初始化文件保存的位置 +// InitApplication 由不同的平台直接调用, 根据提供的路径初始化数据库, 资料文件夹 func InitApplication(applicationDir string) { println("初始化 : " + applicationDir) var databasesDir, remoteDir, downloadDir, tmpDir string diff --git a/go/pikapi/controller/client.go b/go/pikapi/controller/client.go index 2ad40e1..326db00 100644 --- a/go/pikapi/controller/client.go +++ b/go/pikapi/controller/client.go @@ -1,7 +1,8 @@ +// 透传Client的功能并增加缓存 + package controller import ( - "context" "encoding/json" "fmt" source "github.com/niuhuan/pica-go" @@ -12,9 +13,13 @@ import ( "pgo/pikapi/database/network_cache" "pgo/pikapi/database/properties" "regexp" + "time" +) + +import ( + "context" "strconv" "strings" - "time" ) func InitClient() { @@ -405,7 +410,7 @@ func postComment(params string) (string, error) { func postChildComment(params string) (string, error) { var paramsStruct struct { - ComicId string `json:"comicId"` + ComicId string `json:"comicId"` CommentId string `json:"commentId"` Content string `json:"content"` } diff --git a/go/pikapi/controller/common.go b/go/pikapi/controller/common.go index 932c5a6..b3c7d1f 100644 --- a/go/pikapi/controller/common.go +++ b/go/pikapi/controller/common.go @@ -36,6 +36,7 @@ func notifyExport(str string) { onEvent("EXPORT", str) } +// 将interface序列化成字符串, 方便与flutter通信 func serialize(point interface{}, err error) (string, error) { if err != nil { return "", err diff --git a/go/pikapi/controller/download.go b/go/pikapi/controller/download.go index feb6e54..000b263 100644 --- a/go/pikapi/controller/download.go +++ b/go/pikapi/controller/download.go @@ -11,6 +11,10 @@ import ( "time" ) +// 使用协程进行后台下载 +// downloadRunning 如果为false则停止下载 +// downloadRestart 为true则取消从新启动下载功能 + var downloadRunning = false var downloadRestart = false @@ -18,16 +22,24 @@ var downloadingComic *comic_center.ComicDownload var downloadingEp *comic_center.ComicDownloadEp var downloadingPicture *comic_center.ComicDownloadPicture +var dlFlag = true + +// 程序启动后仅调用一次, 启动后台线程 func downloadBackground() { println("后台线程启动") - go downloadBegin() + if dlFlag { + dlFlag = false + go downloadBegin() + } } +// 下载启动/重新启动会暂停三秒 func downloadBegin() { time.Sleep(time.Second * 3) go downloadLoadComic() } +// 下载周期中, 每个下载单元会调用此方法, 如果返回true应该停止当前动作 func downloadHasStop() bool { if !downloadRunning { go downloadBegin() @@ -41,6 +53,7 @@ func downloadHasStop() bool { return false } +// 删除下载任务, 当用户要删除下载的时候, 他会被加入删除队列, 而不是直接被删除, 以减少出错 func downloadDelete() bool { c, e := comic_center.DeletingComic() if e != nil { @@ -57,39 +70,50 @@ func downloadDelete() bool { return false } +// 加载第一个需要下载的漫画 func downloadLoadComic() { + // 每次下载完一个漫画, 或者启动的时候, 首先进行删除任务 for downloadDelete() { } + // 检测是否需要停止 if downloadHasStop() { return } + // 找到第一个要下载的漫画, 查库有错误就停止, 因为这些错误很少出现, 一旦出现必然是严重的, 例如数据库文件突然被删除 var err error downloadingComic, err = comic_center.LoadFirstNeedDownload() - // 查库有错误就停止 if err != nil { panic(err) } + // 处理找到的下载任务 go downloadInitComic() } +// 初始化找到的下载任务 func downloadInitComic() { + // 检测是否需要停止 if downloadHasStop() { return } + // 若没有漫画要下载则重新启动 if downloadingComic == nil { println("没有找到要下载的漫画") go downloadBegin() return } + // 打印日志, 并向前端的eventChannel发送下载信息 println("正在下载漫画 " + downloadingComic.Title) downloadComicEventSend(downloadingComic) eps, err := comic_center.ListDownloadEpByComicId(downloadingComic.ID) if err != nil { panic(err) } + // 找到这个漫画需要下载的EP, 并搜索获取图片地址 for _, ep := range eps { + // FetchedPictures字段标志着这个章节的图片地址有没有获取过, 如果没有获取过就重新获取 if !ep.FetchedPictures { println("正在获取章节的图片 " + downloadingComic.Title + " " + ep.Title) + // 搜索图片地址, 如果五次没有请求成功, 就不在请求 for i := 0; i < 5; i++ { if client.Token == "" { continue @@ -102,6 +126,7 @@ func downloadInitComic() { ep.FetchedPictures = true break } + // 如果未能获取图片地址, 则直接置为失败 if !ep.FetchedPictures { println("章节的图片获取失败 " + downloadingComic.Title + " " + ep.Title) err = comic_center.EpFailed(ep.ID) @@ -115,11 +140,14 @@ func downloadInitComic() { } } } + // 获取图片地址结束, 去初始化下载的章节 go downloadLoadEp() } +// 获取图片地址 func downloadFetchPictures(downloadEp *comic_center.ComicDownloadEp) error { var list []comic_center.ComicDownloadPicture + // 官方的图片只能分页获取, 从第1页开始获取, 每页最多40张图片 page := 1 for true { rsp, err := client.ComicPicturePage(downloadingComic.ID, int(downloadEp.EpOrder), page) @@ -137,12 +165,14 @@ func downloadFetchPictures(downloadEp *comic_center.ComicDownloadEp) error { Path: doc.Media.Path, }) } + // 如果不是最后一页, 页码加1, 获取下一页 if rsp.Page.Page < rsp.Page.Pages { page++ continue } break } + // 保存获取到的图片 err := comic_center.FetchPictures(downloadEp.ComicId, downloadEp.ID, &list) if err != nil { panic(err) @@ -151,10 +181,13 @@ func downloadFetchPictures(downloadEp *comic_center.ComicDownloadEp) error { return err } +// 初始化下载 func downloadLoadEp() { + // 周期停止检测 if downloadHasStop() { return } + // 找到第一个需要下载的章节并去处理 (未下载失败的, 且未完成下载的) var err error downloadingEp, err = comic_center.LoadFirstNeedDownloadEp(downloadingComic.ID) if err != nil { @@ -163,29 +196,36 @@ func downloadLoadEp() { go downloadInitEp() } +// 处理需要下载的EP func downloadInitEp() { if downloadingEp == nil { // 所有Ep都下完了, 汇总Download下载情况 go downloadSummaryDownload() return } + // 没有下载完则去下载图片 println("正在下载章节 " + downloadingEp.Title) go downloadLoadPicture() } +// EP下载汇总 func downloadSummaryDownload() { + // 暂停检测 if downloadHasStop() { return } + // 加载这个漫画的所有EP list, err := comic_center.ListDownloadEpByComicId(downloadingComic.ID) if err != nil { panic(err) } + // 判断所有章节是否下载完成 over := true for _, downloadEp := range list { over = over && downloadEp.DownloadFinished } if over { + // 如果所有章节下载完成则下载成功 err = comic_center.DownloadSuccess(downloadingComic.ID) if err != nil { panic(err) @@ -193,17 +233,22 @@ func downloadSummaryDownload() { downloadingComic.DownloadFinished = true downloadingComic.DownloadFinishedTime = time.Now() } else { + // 否则下载失败 err = comic_center.DownloadFailed(downloadingComic.ID) if err != nil { panic(err) } downloadingComic.DownloadFailed = true } + // 向前端发送下载状态 downloadComicEventSend(downloadingComic) + // 去下载下一个漫画 go downloadLoadComic() } +// 加载需要下载的图片 func downloadLoadPicture() { + // 暂停检测 if downloadHasStop() { return } @@ -216,6 +261,7 @@ func downloadLoadPicture() { } func downloadInitPicture() { + // 暂停检测 if downloadHasStop() { return } @@ -224,42 +270,50 @@ func downloadInitPicture() { go downloadSummaryEp() return } + // 下载图片, 最多重试5次 println("正在下载图片 " + fmt.Sprintf("%d", downloadingPicture.RankInEp)) for i := 0; i < 5; i++ { err := downloadThePicture(downloadingPicture) if err != nil { continue } + // 对下载的漫画临时变量热更新并通知前端 downloadingPicture.DownloadFinished = true downloadingEp.DownloadPictureCount = downloadingEp.DownloadPictureCount + 1 downloadingComic.DownloadPictureCount = downloadingComic.DownloadPictureCount + 1 downloadComicEventSend(downloadingComic) break } + // 没能下载成功, 图片置为下载失败 if !downloadingPicture.DownloadFinished { err := comic_center.PictureFailed(downloadingPicture.ID) if err != nil { panic(err) } } + // 加载下一张需要下载的图片 go downloadLoadPicture() } +// 下载指定图片 func downloadThePicture(picturePoint *comic_center.ComicDownloadPicture) error { + // 为了不和页面前端浏览的数据冲突, 使用url做hash锁 lock := utils2.HashLock(fmt.Sprintf("%s$%s", picturePoint.FileServer, picturePoint.Path)) lock.Lock() defer lock.Unlock() + // 图片保存位置使用相对路径储存, 使用绝对路径操作 picturePath := fmt.Sprintf("%s/%d/%d", picturePoint.ComicId, picturePoint.EpOrder, picturePoint.RankInEp) realPath := downloadPath(picturePath) - // 从缓存 + // 从缓存获取图片 buff, img, format, err := decodeFromCache(picturePoint.FileServer, picturePoint.Path) if err != nil { - // 从网络 + // 若缓存不存在, 则从网络获取 buff, img, format, err = decodeFromUrl(picturePoint.FileServer, picturePoint.Path) } if err != nil { return err } + // 将图片保存到文件 dir := filepath.Dir(realPath) if _, err := os.Stat(dir); os.IsNotExist(err) { os.Mkdir(dir, const_value.CreateDirMode) @@ -268,6 +322,7 @@ func downloadThePicture(picturePoint *comic_center.ComicDownloadPicture) error { if err != nil { return err } + // 存入数据库 return comic_center.PictureSuccess( picturePoint.ComicId, picturePoint.EpId, @@ -280,14 +335,18 @@ func downloadThePicture(picturePoint *comic_center.ComicDownloadPicture) error { ) } +// EP 下载内容汇总 func downloadSummaryEp() { + // 暂停检测 if downloadHasStop() { return } + // 找到所有下载的图片 list, err := comic_center.ListDownloadPictureByEpId(downloadingEp.ID) if err != nil { panic(err) } + // 全部下载完成置为成功, 否则置为失败 over := true for _, downloadPicture := range list { over = over && downloadPicture.DownloadFinished @@ -303,5 +362,6 @@ func downloadSummaryEp() { panic(err) } } + // 去加载下一个EP go downloadLoadEp() } diff --git a/go/pikapi/controller/network.go b/go/pikapi/controller/network.go index 9be445e..dc18a50 100644 --- a/go/pikapi/controller/network.go +++ b/go/pikapi/controller/network.go @@ -5,6 +5,7 @@ import ( "strings" ) +// 获取IP的集合 func clientIpSet() (string, error) { address, err := net.InterfaceAddrs() if err != nil { diff --git a/go/pikapi/controller/special.go b/go/pikapi/controller/special.go index ed97d49..2ebbaca 100644 --- a/go/pikapi/controller/special.go +++ b/go/pikapi/controller/special.go @@ -2,6 +2,7 @@ package controller import "pgo/pikapi/database/comic_center" +// 根据comicId,获得标题,但是必须是下载的内容(暂未使用) func specialDownloadTitle(comicId string) (string, error) { info, err := comic_center.DownloadInfo(comicId) if err != nil { diff --git a/go/pikapi/database/comic_center/center.go b/go/pikapi/database/comic_center/center.go index 7690d0f..7fdea84 100644 --- a/go/pikapi/database/comic_center/center.go +++ b/go/pikapi/database/comic_center/center.go @@ -113,6 +113,8 @@ func ViewComicUpdateInfo(view *ComicView) error { } func ViewComic(comicId string) error { + mutex.Lock() + defer mutex.Unlock() return db.Model(&ComicView{}).Where( "id = ?", comicId, ).Update( diff --git a/go/pikapi/utils/time.go b/go/pikapi/utils/time.go index 6dc3864..8b1b471 100644 --- a/go/pikapi/utils/time.go +++ b/go/pikapi/utils/time.go @@ -2,6 +2,7 @@ package utils import "time" +// Timestamp 获取当前的Unix时间戳 func Timestamp() int64 { return time.Now().UnixNano() / int64(time.Millisecond) }