Jelajahi Sumber

添加基础的分布式下载任务的接口

Signed-off-by: allan716 <[email protected]>
allan716 3 tahun lalu
induk
melakukan
212a7b2c6d

+ 5 - 272
pkg/downloader/downloader.go

@@ -6,9 +6,6 @@ import (
 	"sync"
 
 	"github.com/allanpk716/ChineseSubFinder/pkg/ifaces"
-	common2 "github.com/allanpk716/ChineseSubFinder/pkg/types/common"
-	taskQueue2 "github.com/allanpk716/ChineseSubFinder/pkg/types/task_queue"
-
 	embyHelper "github.com/allanpk716/ChineseSubFinder/pkg/logic/emby_helper"
 	"github.com/allanpk716/ChineseSubFinder/pkg/logic/file_downloader"
 	markSystem "github.com/allanpk716/ChineseSubFinder/pkg/logic/mark_system"
@@ -16,13 +13,8 @@ import (
 	subSupplier "github.com/allanpk716/ChineseSubFinder/pkg/logic/sub_supplier"
 	"github.com/allanpk716/ChineseSubFinder/pkg/logic/sub_supplier/csf"
 	"github.com/allanpk716/ChineseSubFinder/pkg/logic/sub_timeline_fixer"
+	common2 "github.com/allanpk716/ChineseSubFinder/pkg/types/common"
 
-	"github.com/allanpk716/ChineseSubFinder/pkg/decode"
-
-	"github.com/allanpk716/ChineseSubFinder/pkg/global_value"
-
-	"github.com/allanpk716/ChineseSubFinder/pkg/log_helper"
-	"github.com/allanpk716/ChineseSubFinder/pkg/my_folder"
 	"github.com/allanpk716/ChineseSubFinder/pkg/my_util"
 	"github.com/allanpk716/ChineseSubFinder/pkg/settings"
 	subCommon "github.com/allanpk716/ChineseSubFinder/pkg/sub_formatter/common"
@@ -177,269 +169,10 @@ func (d *Downloader) SupplierCheck() {
 // QueueDownloader 从字幕队列中取一个视频的字幕下载任务出来,并且开始下载
 func (d *Downloader) QueueDownloader() {
 
-	d.log.Debugln("Download.QueueDownloader() Try Start ...")
-	d.downloaderLock.Lock()
-	d.log.Debugln("Download.QueueDownloader() Start ...")
-
-	defer func() {
-		if p := recover(); p != nil {
-			d.log.Errorln("Downloader.QueueDownloader() panic")
-			my_util.PrintPanicStack(d.log)
-		}
-		d.downloaderLock.Unlock()
-		d.log.Debugln("Download.QueueDownloader() End")
-	}()
-
-	var downloadCounter int64
-	downloadCounter = 0
-	// 移除查过三个月的 Done 任务
-	d.downloadQueue.BeforeGetOneJob()
-	// 从队列取数据出来,见《任务生命周期》
-	bok, oneJob, err := d.downloadQueue.GetOneJob()
-	if err != nil {
-		d.log.Errorln("d.downloadQueue.GetOneWaitingJob()", err)
-		return
-	}
-	if bok == false {
-		d.log.Debugln("Download Queue Is Empty, Skip This Time")
-		return
-	}
-	// --------------------------------------------------
-	// 这个任务如果是 series 那么需要考虑是否原始存入的信息是缺失的,需要补全
-	{
-		if oneJob.VideoType == common2.Series && (oneJob.SeriesRootDirPath == "" || oneJob.Season <= 0 || oneJob.Episode <= 0) {
-			// 连续剧的时候需要额外提交信息
-			epsVideoNfoInfo, err := decode.GetVideoNfoInfo4OneSeriesEpisode(oneJob.VideoFPath)
-			if err != nil {
-				d.log.Errorln("decode.GetVideoNfoInfo4OneSeriesEpisode()", err)
-				d.log.Infoln("maybe you moved video file to another place or delete it, so will delete this job")
-				bok, err = d.downloadQueue.Del(oneJob.Id)
-				if err != nil {
-					d.log.Errorln("d.downloadQueue.Del()", err)
-					return
-				}
-				if bok == false {
-					d.log.Errorln(fmt.Sprintf("d.downloadQueue.Del(%d) == false", oneJob.Id))
-					return
-				}
-				return
-			}
-			seriesInfoDirPath := decode.GetSeriesDirRootFPath(oneJob.VideoFPath)
-			if seriesInfoDirPath == "" {
-				d.log.Errorln(fmt.Sprintf("decode.GetSeriesDirRootFPath == Empty, %s", oneJob.VideoFPath))
-				d.log.Infoln("you need check the directory structure of a series, so will delete this job")
-				bok, err = d.downloadQueue.Del(oneJob.Id)
-				if err != nil {
-					d.log.Errorln("d.downloadQueue.Del()", err)
-					return
-				}
-				if bok == false {
-					d.log.Errorln(fmt.Sprintf("d.downloadQueue.Del(%d) == false", oneJob.Id))
-					return
-				}
-				return
-			}
-			oneJob.Season = epsVideoNfoInfo.Season
-			oneJob.Episode = epsVideoNfoInfo.Episode
-			oneJob.SeriesRootDirPath = seriesInfoDirPath
-		}
-	}
-	// --------------------------------------------------
-	// 这个视频文件不存在了
-	{
-		isBlue, _, _ := decode.IsFakeBDMVWorked(oneJob.VideoFPath)
-		if isBlue == false && my_util.IsFile(oneJob.VideoFPath) == false {
-			// 不是蓝光,那么就判断文件是否存在,不存在,那么就标记 ignore
-			bok, err = d.downloadQueue.Del(oneJob.Id)
-			if err != nil {
-				d.log.Errorln("d.downloadQueue.Del()", err)
-				return
-			}
-			if bok == false {
-				d.log.Errorln(fmt.Sprintf("d.downloadQueue.Del(%d) == false", oneJob.Id))
-				return
-			}
-			d.log.Infoln(oneJob.VideoFPath, "is missing, Delete This Job")
-			return
-		}
-	}
-	// --------------------------------------------------
-	// 判断是否看过,这个只有 Emby 情况下才会生效
-	{
-		isPlayed := false
-		if d.embyHelper != nil {
-			// 在拿出来后,如果是有内部媒体服务器媒体 ID 的,那么就去查询是否已经观看过了
-			isPlayed, err = d.embyHelper.IsVideoPlayed(oneJob.MediaServerInsideVideoID)
-			if err != nil {
-				d.log.Errorln("d.embyHelper.IsVideoPlayed()", oneJob.VideoFPath, err)
-				return
-			}
-		}
-		// TODO 暂时屏蔽掉 http api 提交的已看字幕的接口上传
-		// 不管如何,只要是发现数据库中有 HTTP API 提交的信息,就认为是看过
-		//var videoPlayedInfos []models.ThirdPartSetVideoPlayedInfo
-		//dao.GetDb().Where("physical_video_file_full_path = ?", oneJob.VideoFPath).Find(&videoPlayedInfos)
-		//if len(videoPlayedInfos) > 0 {
-		//	isPlayed = true
-		//}
-		// --------------------------------------------------
-		// 如果已经播放过 且 这个任务的优先级 > 3 ,不是很急的那种,说明是可以设置忽略继续下载的
-		if isPlayed == true && oneJob.TaskPriority > task_queue.HighTaskPriorityLevel {
-			// 播放过了,那么就标记 ignore
-			oneJob.JobStatus = taskQueue2.Ignore
-			bok, err = d.downloadQueue.Update(oneJob)
-			if err != nil {
-				d.log.Errorln("d.downloadQueue.Update()", err)
-				return
-			}
-			if bok == false {
-				d.log.Errorln("d.downloadQueue.Update() Failed")
-				return
-			}
-			d.log.Infoln("Is Played, Ignore This Job")
-			return
-		}
-	}
-	// --------------------------------------------------
-	// 判断是否需要跳过,因为如果是 Normal 扫描出来的,那么可能因为视频时间久远,下载一次即可
-	{
-		if oneJob.TaskPriority > task_queue.HighTaskPriorityLevel {
-			// 优先级大于 3,那么就不是很急的任务,才需要判断
-			if oneJob.VideoType == common2.Movie {
-				if d.subSupplierHub.MovieNeedDlSub(oneJob.VideoFPath, false) == false {
-					// 需要标记忽略
-					oneJob.JobStatus = taskQueue2.Ignore
-					bok, err = d.downloadQueue.Update(oneJob)
-					if err != nil {
-						d.log.Errorln("d.downloadQueue.Update()", err)
-						return
-					}
-					if bok == false {
-						d.log.Errorln("d.downloadQueue.Update() Failed")
-						return
-					}
-					d.log.Infoln("MovieNeedDlSub == false, Ignore This Job")
-					return
-				}
-			} else if oneJob.VideoType == common2.Series {
-
-				bNeedDlSub, seriesInfo, err := d.subSupplierHub.SeriesNeedDlSub(oneJob.SeriesRootDirPath,
-					false, false)
-				if err != nil {
-					d.log.Errorln("SeriesNeedDlSub", err)
-					return
-				}
-				needMarkSkip := false
-				if bNeedDlSub == false {
-					// 需要跳过
-					needMarkSkip = true
-				} else {
-					// 需要下载的 Eps 是否与 Normal 判断这个连续剧中有那些剧集需要下载的,情况符合。通过下载的时间来判断
-					epsKey := my_util.GetEpisodeKeyName(oneJob.Season, oneJob.Episode)
-					_, found := seriesInfo.NeedDlEpsKeyList[epsKey]
-					if found == false {
-						// 需要跳过
-						needMarkSkip = true
-					}
-				}
-
-				if needMarkSkip == true {
-					// 需要标记忽略
-					oneJob.JobStatus = taskQueue2.Ignore
-					bok, err = d.downloadQueue.Update(oneJob)
-					if err != nil {
-						d.log.Errorln("d.downloadQueue.Update()", err)
-						return
-					}
-					if bok == false {
-						d.log.Errorln("d.downloadQueue.Update() Failed")
-						return
-					}
-					d.log.Infoln("SeriesNeedDlSub == false, Ignore This Job")
-					return
-				}
-			}
-		}
-	}
-	// 取出来后,需要标记为正在下载
-	oneJob.JobStatus = taskQueue2.Downloading
-	bok, err = d.downloadQueue.Update(oneJob)
-	if err != nil {
-		d.log.Errorln("d.downloadQueue.Update()", err)
-		return
-	}
-	if bok == false {
-		d.log.Errorln("d.downloadQueue.Update() Failed")
-		return
-	}
-	// ------------------------------------------------------------------------
-	// 开始标记,这个是单次扫描的开始,要注意格式,在日志的内部解析识别单个日志开头的时候需要特殊的格式
-	d.log.Infoln("------------------------------------------")
-	d.log.Infoln(log_helper.OnceSubsScanStart + "#" + oneJob.Id)
-	// ------------------------------------------------------------------------
-	defer func() {
-		d.log.Infoln(log_helper.OnceSubsScanEnd)
-		d.log.Infoln("------------------------------------------")
-	}()
-
-	downloadCounter++
-	// 创建一个 chan 用于任务的中断和超时
-	done := make(chan interface{}, 1)
-	// 接收内部任务的 panic
-	panicChan := make(chan interface{}, 1)
-
-	go func() {
-		defer func() {
-			if p := recover(); p != nil {
-				panicChan <- p
-			}
-			close(done)
-			close(panicChan)
-			// 没下载完毕一次,进行一次缓存和 Chrome 的清理
-			err = my_folder.ClearRootTmpFolder()
-			if err != nil {
-				d.log.Error("ClearRootTmpFolder", err)
-			}
-
-			if global_value.LiteMode() == false {
-				my_util.CloseChrome(d.log)
-			}
-		}()
-
-		if oneJob.VideoType == common2.Movie {
-			// 电影
-			// 具体的下载逻辑 func()
-			done <- d.movieDlFunc(d.ctx, oneJob, downloadCounter)
-		} else if oneJob.VideoType == common2.Series {
-			// 连续剧
-			// 具体的下载逻辑 func()
-			done <- d.seriesDlFunc(d.ctx, oneJob, downloadCounter)
-		} else {
-			d.log.Errorln("oneJob.VideoType not support, oneJob.VideoType = ", oneJob.VideoType)
-			done <- nil
-		}
-	}()
-
-	select {
-	case err := <-done:
-		// 跳出 select,可以外层继续,不会阻塞在这里
-		if err != nil {
-			d.log.Errorln(err)
-		}
-		// 刷新视频的缓存结构
-		d.UpdateInfo(oneJob)
-
-		break
-	case p := <-panicChan:
-		// 遇到内部的 panic,向外抛出
-		panic(p)
-	case <-d.ctx.Done():
-		{
-			// 取消这个 context
-			d.log.Warningln("cancel Downloader.QueueDownloader()")
-			return
-		}
-	}
+	// 本地的任务
+	d.queueDownloaderLocal()
+	// 云端分布式的任务
+	d.queueDownloaderCloud()
 }
 
 func (d *Downloader) Cancel() {

+ 19 - 0
pkg/downloader/queue_cloud.go

@@ -0,0 +1,19 @@
+package downloader
+
+import "github.com/allanpk716/ChineseSubFinder/pkg/global_value"
+
+func (d *Downloader) queueDownloaderCloud() {
+
+	if global_value.LiteMode() == true || d.settings.ExperimentalFunction.ShareSubSettings.ShareSubEnabled == false {
+		// Lite 版本无法执行复杂的任务,因为没有浏览器
+		// 如果没有开启共享字幕也不会出发获取云端任务的逻辑
+		return
+	}
+
+	// 查询云端是否又任务
+	//d.fileDownloader.SubtitleBestApi.
+
+	// 如果收到任务,那么就启动下载,这里下载的任务需要单独地方临时存储
+
+	// 如果下载成功就反馈到云端
+}

+ 280 - 0
pkg/downloader/queue_local.go

@@ -0,0 +1,280 @@
+package downloader
+
+import (
+	"fmt"
+	"github.com/allanpk716/ChineseSubFinder/pkg/decode"
+	"github.com/allanpk716/ChineseSubFinder/pkg/global_value"
+	"github.com/allanpk716/ChineseSubFinder/pkg/log_helper"
+	"github.com/allanpk716/ChineseSubFinder/pkg/my_folder"
+	"github.com/allanpk716/ChineseSubFinder/pkg/my_util"
+	"github.com/allanpk716/ChineseSubFinder/pkg/task_queue"
+	common2 "github.com/allanpk716/ChineseSubFinder/pkg/types/common"
+	taskQueue2 "github.com/allanpk716/ChineseSubFinder/pkg/types/task_queue"
+)
+
+func (d *Downloader) queueDownloaderLocal() {
+
+	d.log.Debugln("Download.QueueDownloader() Try Start ...")
+	d.downloaderLock.Lock()
+	d.log.Debugln("Download.QueueDownloader() Start ...")
+
+	defer func() {
+		if p := recover(); p != nil {
+			d.log.Errorln("Downloader.QueueDownloader() panic")
+			my_util.PrintPanicStack(d.log)
+		}
+		d.downloaderLock.Unlock()
+		d.log.Debugln("Download.QueueDownloader() End")
+	}()
+
+	var downloadCounter int64
+	downloadCounter = 0
+	// 移除查过三个月的 Done 任务
+	d.downloadQueue.BeforeGetOneJob()
+	// 从队列取数据出来,见《任务生命周期》
+	bok, oneJob, err := d.downloadQueue.GetOneJob()
+	if err != nil {
+		d.log.Errorln("d.downloadQueue.GetOneWaitingJob()", err)
+		return
+	}
+	if bok == false {
+		d.log.Debugln("Download Queue Is Empty, Skip This Time")
+		return
+	}
+	// --------------------------------------------------
+	// 这个任务如果是 series 那么需要考虑是否原始存入的信息是缺失的,需要补全
+	{
+		if oneJob.VideoType == common2.Series && (oneJob.SeriesRootDirPath == "" || oneJob.Season <= 0 || oneJob.Episode <= 0) {
+			// 连续剧的时候需要额外提交信息
+			epsVideoNfoInfo, err := decode.GetVideoNfoInfo4OneSeriesEpisode(oneJob.VideoFPath)
+			if err != nil {
+				d.log.Errorln("decode.GetVideoNfoInfo4OneSeriesEpisode()", err)
+				d.log.Infoln("maybe you moved video file to another place or delete it, so will delete this job")
+				bok, err = d.downloadQueue.Del(oneJob.Id)
+				if err != nil {
+					d.log.Errorln("d.downloadQueue.Del()", err)
+					return
+				}
+				if bok == false {
+					d.log.Errorln(fmt.Sprintf("d.downloadQueue.Del(%d) == false", oneJob.Id))
+					return
+				}
+				return
+			}
+			seriesInfoDirPath := decode.GetSeriesDirRootFPath(oneJob.VideoFPath)
+			if seriesInfoDirPath == "" {
+				d.log.Errorln(fmt.Sprintf("decode.GetSeriesDirRootFPath == Empty, %s", oneJob.VideoFPath))
+				d.log.Infoln("you need check the directory structure of a series, so will delete this job")
+				bok, err = d.downloadQueue.Del(oneJob.Id)
+				if err != nil {
+					d.log.Errorln("d.downloadQueue.Del()", err)
+					return
+				}
+				if bok == false {
+					d.log.Errorln(fmt.Sprintf("d.downloadQueue.Del(%d) == false", oneJob.Id))
+					return
+				}
+				return
+			}
+			oneJob.Season = epsVideoNfoInfo.Season
+			oneJob.Episode = epsVideoNfoInfo.Episode
+			oneJob.SeriesRootDirPath = seriesInfoDirPath
+		}
+	}
+	// --------------------------------------------------
+	// 这个视频文件不存在了
+	{
+		isBlue, _, _ := decode.IsFakeBDMVWorked(oneJob.VideoFPath)
+		if isBlue == false && my_util.IsFile(oneJob.VideoFPath) == false {
+			// 不是蓝光,那么就判断文件是否存在,不存在,那么就标记 ignore
+			bok, err = d.downloadQueue.Del(oneJob.Id)
+			if err != nil {
+				d.log.Errorln("d.downloadQueue.Del()", err)
+				return
+			}
+			if bok == false {
+				d.log.Errorln(fmt.Sprintf("d.downloadQueue.Del(%d) == false", oneJob.Id))
+				return
+			}
+			d.log.Infoln(oneJob.VideoFPath, "is missing, Delete This Job")
+			return
+		}
+	}
+	// --------------------------------------------------
+	// 判断是否看过,这个只有 Emby 情况下才会生效
+	{
+		isPlayed := false
+		if d.embyHelper != nil {
+			// 在拿出来后,如果是有内部媒体服务器媒体 ID 的,那么就去查询是否已经观看过了
+			isPlayed, err = d.embyHelper.IsVideoPlayed(oneJob.MediaServerInsideVideoID)
+			if err != nil {
+				d.log.Errorln("d.embyHelper.IsVideoPlayed()", oneJob.VideoFPath, err)
+				return
+			}
+		}
+		// TODO 暂时屏蔽掉 http api 提交的已看字幕的接口上传
+		// 不管如何,只要是发现数据库中有 HTTP API 提交的信息,就认为是看过
+		//var videoPlayedInfos []models.ThirdPartSetVideoPlayedInfo
+		//dao.GetDb().Where("physical_video_file_full_path = ?", oneJob.VideoFPath).Find(&videoPlayedInfos)
+		//if len(videoPlayedInfos) > 0 {
+		//	isPlayed = true
+		//}
+		// --------------------------------------------------
+		// 如果已经播放过 且 这个任务的优先级 > 3 ,不是很急的那种,说明是可以设置忽略继续下载的
+		if isPlayed == true && oneJob.TaskPriority > task_queue.HighTaskPriorityLevel {
+			// 播放过了,那么就标记 ignore
+			oneJob.JobStatus = taskQueue2.Ignore
+			bok, err = d.downloadQueue.Update(oneJob)
+			if err != nil {
+				d.log.Errorln("d.downloadQueue.Update()", err)
+				return
+			}
+			if bok == false {
+				d.log.Errorln("d.downloadQueue.Update() Failed")
+				return
+			}
+			d.log.Infoln("Is Played, Ignore This Job")
+			return
+		}
+	}
+	// --------------------------------------------------
+	// 判断是否需要跳过,因为如果是 Normal 扫描出来的,那么可能因为视频时间久远,下载一次即可
+	{
+		if oneJob.TaskPriority > task_queue.HighTaskPriorityLevel {
+			// 优先级大于 3,那么就不是很急的任务,才需要判断
+			if oneJob.VideoType == common2.Movie {
+				if d.subSupplierHub.MovieNeedDlSub(oneJob.VideoFPath, false) == false {
+					// 需要标记忽略
+					oneJob.JobStatus = taskQueue2.Ignore
+					bok, err = d.downloadQueue.Update(oneJob)
+					if err != nil {
+						d.log.Errorln("d.downloadQueue.Update()", err)
+						return
+					}
+					if bok == false {
+						d.log.Errorln("d.downloadQueue.Update() Failed")
+						return
+					}
+					d.log.Infoln("MovieNeedDlSub == false, Ignore This Job")
+					return
+				}
+			} else if oneJob.VideoType == common2.Series {
+
+				bNeedDlSub, seriesInfo, err := d.subSupplierHub.SeriesNeedDlSub(oneJob.SeriesRootDirPath,
+					false, false)
+				if err != nil {
+					d.log.Errorln("SeriesNeedDlSub", err)
+					return
+				}
+				needMarkSkip := false
+				if bNeedDlSub == false {
+					// 需要跳过
+					needMarkSkip = true
+				} else {
+					// 需要下载的 Eps 是否与 Normal 判断这个连续剧中有那些剧集需要下载的,情况符合。通过下载的时间来判断
+					epsKey := my_util.GetEpisodeKeyName(oneJob.Season, oneJob.Episode)
+					_, found := seriesInfo.NeedDlEpsKeyList[epsKey]
+					if found == false {
+						// 需要跳过
+						needMarkSkip = true
+					}
+				}
+
+				if needMarkSkip == true {
+					// 需要标记忽略
+					oneJob.JobStatus = taskQueue2.Ignore
+					bok, err = d.downloadQueue.Update(oneJob)
+					if err != nil {
+						d.log.Errorln("d.downloadQueue.Update()", err)
+						return
+					}
+					if bok == false {
+						d.log.Errorln("d.downloadQueue.Update() Failed")
+						return
+					}
+					d.log.Infoln("SeriesNeedDlSub == false, Ignore This Job")
+					return
+				}
+			}
+		}
+	}
+	// 取出来后,需要标记为正在下载
+	oneJob.JobStatus = taskQueue2.Downloading
+	bok, err = d.downloadQueue.Update(oneJob)
+	if err != nil {
+		d.log.Errorln("d.downloadQueue.Update()", err)
+		return
+	}
+	if bok == false {
+		d.log.Errorln("d.downloadQueue.Update() Failed")
+		return
+	}
+	// ------------------------------------------------------------------------
+	// 开始标记,这个是单次扫描的开始,要注意格式,在日志的内部解析识别单个日志开头的时候需要特殊的格式
+	d.log.Infoln("------------------------------------------")
+	d.log.Infoln(log_helper.OnceSubsScanStart + "#" + oneJob.Id)
+	// ------------------------------------------------------------------------
+	defer func() {
+		d.log.Infoln(log_helper.OnceSubsScanEnd)
+		d.log.Infoln("------------------------------------------")
+	}()
+
+	downloadCounter++
+	// 创建一个 chan 用于任务的中断和超时
+	done := make(chan interface{}, 1)
+	// 接收内部任务的 panic
+	panicChan := make(chan interface{}, 1)
+
+	go func() {
+		defer func() {
+			if p := recover(); p != nil {
+				panicChan <- p
+			}
+			close(done)
+			close(panicChan)
+			// 没下载完毕一次,进行一次缓存和 Chrome 的清理
+			err = my_folder.ClearRootTmpFolder()
+			if err != nil {
+				d.log.Error("ClearRootTmpFolder", err)
+			}
+
+			if global_value.LiteMode() == false {
+				my_util.CloseChrome(d.log)
+			}
+		}()
+
+		if oneJob.VideoType == common2.Movie {
+			// 电影
+			// 具体的下载逻辑 func()
+			done <- d.movieDlFunc(d.ctx, oneJob, downloadCounter)
+		} else if oneJob.VideoType == common2.Series {
+			// 连续剧
+			// 具体的下载逻辑 func()
+			done <- d.seriesDlFunc(d.ctx, oneJob, downloadCounter)
+		} else {
+			d.log.Errorln("oneJob.VideoType not support, oneJob.VideoType = ", oneJob.VideoType)
+			done <- nil
+		}
+	}()
+
+	select {
+	case err := <-done:
+		// 跳出 select,可以外层继续,不会阻塞在这里
+		if err != nil {
+			d.log.Errorln(err)
+		}
+		// 刷新视频的缓存结构
+		d.UpdateInfo(oneJob)
+
+		break
+	case p := <-panicChan:
+		// 遇到内部的 panic,向外抛出
+		panic(p)
+	case <-d.ctx.Done():
+		{
+			// 取消这个 context
+			d.log.Warningln("cancel Downloader.QueueDownloader()")
+			return
+		}
+	}
+}

+ 1 - 1
pkg/logic/cron_helper/upload_played_video_sub.go

@@ -358,7 +358,7 @@ func (ch *CronHelper) uploadLowTrustVideoSub() {
 			return
 		}
 		ch.log.Infoln("UploadSub", notUploadedVideoSubInfos[0].SubName)
-		uploadSubReply, err := ch.FileDownloader.SubtitleBestApi.UploadLowTrustSub(&notUploadedVideoSubInfos[0], shareRootDir, finalQueryIMDBInfo.TmdbId, strconv.Itoa(releaseTime.Year()))
+		uploadSubReply, err := ch.FileDownloader.SubtitleBestApi.UploadLowTrustSub(&notUploadedVideoSubInfos[0], shareRootDir, finalQueryIMDBInfo.TmdbId, strconv.Itoa(releaseTime.Year()), "")
 		if err != nil {
 			ch.log.Errorln("UploadLowTrustSub error:", err.Error())
 

+ 17 - 0
pkg/subtitle_best_api/ask_download_task.go

@@ -0,0 +1,17 @@
+package subtitle_best_api
+
+type AskDownloadTaskReply struct {
+	Status       int          `json:"status"`                 // 0 失败,1 有任务,2 没有任务
+	Message      string       `json:"message"`                // 返回的信息,包括成功和失败的原因
+	DownloadInfo DownloadInfo `json:"download_info,optional"` // 下载信息
+}
+
+type DownloadInfo struct {
+	TaskID       string `json:"task_id"`       // 任务id
+	VideoFeature string `json:"video_feature"` // VideoFeature ID
+	ImdbId       string `json:"imdb_id"`       // IMDB ID
+	TmdbId       string `json:"tmdb_id"`       // TMDB ID,这里是这个剧集的 TMDB ID 不是这一集的哈
+	Season       int    `json:"season"`        // 如果对应的是电影则可能是 0,没有
+	Episode      int    `json:"episode"`       // 如果对应的是电影则可能是 0,没有
+	IsMovie      bool   `json:"is_movie"`      // 是否是电影,如果是电影则 season 和 episode 可能是 0,没有
+}

+ 45 - 1
pkg/subtitle_best_api/subtitle_best_api.go

@@ -278,7 +278,7 @@ func (s *SubtitleBestApi) UploadSub(videoSubInfo *models.VideoSubInfo, subSaveRo
 	return &uploadSubReply, nil
 }
 
-func (s *SubtitleBestApi) UploadLowTrustSub(lowTrustVideoSubInfo *models.LowVideoSubInfo, subSaveRootDirPath string, tmdbId, year string) (*UploadSubReply, error) {
+func (s *SubtitleBestApi) UploadLowTrustSub(lowTrustVideoSubInfo *models.LowVideoSubInfo, subSaveRootDirPath string, tmdbId, year, taskID string) (*UploadSubReply, error) {
 
 	if s.authKey.BaseKey == random_auth_key.BaseKey || s.authKey.AESKey16 == random_auth_key.AESKey16 || s.authKey.AESIv16 == random_auth_key.AESIv16 {
 		return nil, errors.New("auth key is not set")
@@ -340,6 +340,7 @@ func (s *SubtitleBestApi) UploadLowTrustSub(lowTrustVideoSubInfo *models.LowVide
 			"video_feature":  lowTrustVideoSubInfo.Feature,
 			"year":           year,
 			"low_trust":      "true",
+			"task_id":        taskID,
 		}).
 		SetResult(&uploadSubReply).
 		Post(postUrl)
@@ -655,6 +656,49 @@ func (s SubtitleBestApi) FeedBack(id, version, MediaServer string, EnableShare,
 	return &feedReply, nil
 }
 
+func (s SubtitleBestApi) AskDownloadTask(id string) (*AskDownloadTaskReply, error) {
+
+	if s.authKey.BaseKey == random_auth_key.BaseKey || s.authKey.AESKey16 == random_auth_key.AESKey16 || s.authKey.AESIv16 == random_auth_key.AESIv16 {
+		return nil, errors.New("auth key is not set")
+	}
+	if len(s.authKey.AESKey16) != 16 {
+		return nil, errors.New(fmt.Sprintf("AESKey16 is not set, %s", s.authKey.AESKey16))
+	}
+	if len(s.authKey.AESIv16) != 16 {
+		return nil, errors.New(fmt.Sprintf("AESIv16 is not set, %s", s.authKey.AESIv16))
+	}
+
+	postUrl := webUrlBase + "/v1/ask-download-task"
+	httpClient, err := my_util.NewHttpClient(s.proxySettings)
+	if err != nil {
+		return nil, err
+	}
+
+	authKey, err := s.randomAuthKey.GetAuthKey()
+	if err != nil {
+		return nil, err
+	}
+
+	var askDownloadTaskReply AskDownloadTaskReply
+	resp, err := httpClient.R().
+		SetHeader("Authorization", "beer "+authKey).
+		SetFormData(map[string]string{
+			"fid": id,
+		}).
+		SetResult(&askDownloadTaskReply).
+		Post(postUrl)
+	if err != nil {
+		s.log.Errorln("ask download task error, status code:", resp.StatusCode(), "Error:", err)
+		return nil, err
+	}
+
+	if askDownloadTaskReply.Status == 0 {
+		s.log.Warningln("status code:", resp.StatusCode())
+	}
+
+	return &askDownloadTaskReply, nil
+}
+
 const (
 	webUrlBase = "https://api.subtitle.best"
 	//webUrlBase = "http://127.0.0.1:8893"

+ 6 - 0
pkg/subtitle_best_api/subtitle_best_api_test.go

@@ -23,6 +23,12 @@ func TestSubtitleBestApi_GetMediaInfo(t *testing.T) {
 			AESIv16:  global_value.AESIv16(),
 		}, settings.GetSettings().AdvancedSettings.ProxySettings)
 
+	downloadTaskReply, err := bapi.AskDownloadTask("123")
+	if err != nil {
+		t.Fatal(err)
+	}
+	println(downloadTaskReply.Status, downloadTaskReply.Message)
+
 	feedReply, err := bapi.FeedBack(my_util.RandStringBytesMaskImprSrcSB(64), "1.0.0", "None", true, true)
 	if err != nil {
 		t.Fatal(err)