Răsfoiți Sursa

优化下载队列跳过已经超过下载时间限制范围的普通任务

Signed-off-by: allan716 <[email protected]>
allan716 3 ani în urmă
părinte
comite
c83fd019b6
2 a modificat fișierele cu 336 adăugiri și 276 ștergeri
  1. 236 0
      internal/pkg/downloader/download_processer.go
  2. 100 276
      internal/pkg/downloader/downloader.go

+ 236 - 0
internal/pkg/downloader/download_processer.go

@@ -0,0 +1,236 @@
+package downloader
+
+import (
+	"errors"
+	"fmt"
+	"path/filepath"
+
+	"github.com/allanpk716/ChineseSubFinder/internal/logic/series_helper"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/task_queue"
+	taskQueue2 "github.com/allanpk716/ChineseSubFinder/internal/types/task_queue"
+	"golang.org/x/net/context"
+)
+
+func (d *Downloader) movieDlFunc(ctx context.Context, job taskQueue2.OneJob, downloadIndex int64) error {
+
+	nowSubSupplierHub := d.subSupplierHub
+	if nowSubSupplierHub.Suppliers == nil || len(nowSubSupplierHub.Suppliers) < 1 {
+		d.log.Infoln("Wait SupplierCheck Update *subSupplierHub, movieDlFunc Skip this time")
+		return nil
+	}
+
+	// 字幕都下载缓存好了,需要抉择存哪一个,优先选择中文双语的,然后到中文
+	organizeSubFiles, err := nowSubSupplierHub.DownloadSub4Movie(job.VideoFPath, downloadIndex)
+	if err != nil {
+		err = errors.New(fmt.Sprintf("subSupplierHub.DownloadSub4Movie: %v, %v", job.VideoFPath, err))
+		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
+		return err
+	}
+	// 返回的两个值都是 nil 的时候,就是没有下载到字幕
+	if organizeSubFiles == nil || len(organizeSubFiles) < 1 {
+		d.log.Infoln(task_queue.ErrNoSubFound.Error(), filepath.Base(job.VideoFPath))
+		d.downloadQueue.AutoDetectUpdateJobStatus(job, task_queue.ErrNoSubFound)
+		return nil
+	}
+
+	err = d.oneVideoSelectBestSub(job.VideoFPath, organizeSubFiles)
+	if err != nil {
+		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
+		return err
+	}
+
+	d.downloadQueue.AutoDetectUpdateJobStatus(job, nil)
+
+	// TODO 刷新字幕,这里是 Emby 的,如果是其他的,需要再对接对应的媒体服务器
+	if d.settings.EmbySettings.Enable == true && d.embyHelper != nil && job.MediaServerInsideVideoID != "" {
+
+		d.log.Infoln("Refresh Emby Subtitle with id:", job.MediaServerInsideVideoID)
+		err = d.embyHelper.EmbyApi.UpdateVideoSubList(job.MediaServerInsideVideoID)
+		if err != nil {
+			d.log.Errorln("UpdateVideoSubList", job.VideoFPath, job.MediaServerInsideVideoID, "Error:", err)
+			return err
+		}
+	} else {
+		if d.settings.EmbySettings.Enable == false {
+			d.log.Infoln("UpdateVideoSubList", job.VideoFPath, "Skip, because Emby enable is false")
+		} else if d.embyHelper == nil {
+			d.log.Infoln("UpdateVideoSubList", job.VideoFPath, "Skip, because EmbyHelper is nil")
+		} else if job.MediaServerInsideVideoID == "" {
+			d.log.Infoln("UpdateVideoSubList", job.VideoFPath, "Skip, because MediaServerInsideVideoID is empty")
+		}
+	}
+
+	return nil
+}
+
+func (d *Downloader) seriesDlFunc(ctx context.Context, job taskQueue2.OneJob, downloadIndex int64) error {
+
+	nowSubSupplierHub := d.subSupplierHub
+	if nowSubSupplierHub == nil || nowSubSupplierHub.Suppliers == nil || len(nowSubSupplierHub.Suppliers) < 1 {
+		d.log.Infoln("Wait SupplierCheck Update *subSupplierHub, movieDlFunc Skip this time")
+		return nil
+	}
+	var err error
+	// 设置只有一集需要下载
+	epsMap := make(map[int][]int, 0)
+	epsMap[job.Season] = []int{job.Episode}
+	// 这里拿到了这一部连续剧的所有的剧集信息,以及所有下载到的字幕信息
+	seriesInfo, err := series_helper.ReadSeriesInfoFromDir(
+		d.log, job.SeriesRootDirPath,
+		d.settings.AdvancedSettings.TaskQueue.ExpirationTime,
+		false,
+		false,
+		epsMap)
+	if err != nil {
+		err = errors.New(fmt.Sprintf("seriesDlFunc.ReadSeriesInfoFromDir, Error: %v", err))
+		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
+		return err
+	}
+	// 下载好的字幕文件
+	var organizeSubFiles map[string][]string
+	// 下载的接口是统一的
+	organizeSubFiles, err = nowSubSupplierHub.DownloadSub4Series(job.SeriesRootDirPath,
+		seriesInfo,
+		downloadIndex)
+	if err != nil {
+		err = errors.New(fmt.Sprintf("seriesDlFunc.DownloadSub4Series %v S%vE%v %v", filepath.Base(job.SeriesRootDirPath), job.Season, job.Episode, err))
+		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
+		return err
+	}
+	// 是否下载到字幕了
+	if organizeSubFiles == nil || len(organizeSubFiles) < 1 {
+		d.log.Infoln(task_queue.ErrNoSubFound.Error(), filepath.Base(job.VideoFPath), job.Season, job.Episode)
+		d.downloadQueue.AutoDetectUpdateJobStatus(job, task_queue.ErrNoSubFound)
+		return nil
+	}
+
+	var errSave2Local error
+	save2LocalSubCount := 0
+	// 只针对需要下载字幕的视频进行字幕的选择保存
+	subVideoCount := 0
+	for epsKey, episodeInfo := range seriesInfo.NeedDlEpsKeyList {
+
+		// 创建一个 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)
+			}()
+			// 匹配对应的 Eps 去处理
+			done <- d.oneVideoSelectBestSub(episodeInfo.FileFullPath, organizeSubFiles[epsKey])
+		}()
+
+		select {
+		case errInterface := <-done:
+			if errInterface != nil {
+				errSave2Local = errInterface.(error)
+				d.log.Errorln(errInterface.(error))
+			} else {
+				save2LocalSubCount++
+			}
+			break
+		case p := <-panicChan:
+			// 遇到内部的 panic,向外抛出
+			d.log.Errorln("seriesDlFunc.oneVideoSelectBestSub panicChan", p)
+			break
+		case <-ctx.Done():
+			{
+				err = errors.New(fmt.Sprintf("cancel at NeedDlEpsKeyList.oneVideoSelectBestSub, %v S%dE%d", seriesInfo.Name, episodeInfo.Season, episodeInfo.Episode))
+				d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
+				return err
+			}
+		}
+
+		subVideoCount++
+	}
+	// 这里会拿到一份季度字幕的列表比如,Key 是 S1E0 S2E0 S3E0,value 是新的存储位置
+	fullSeasonSubDict := d.saveFullSeasonSub(seriesInfo, organizeSubFiles)
+	// TODO 季度的字幕包,应该优先于零散的字幕吧,暂定就这样了,注意是全部都替换
+	// 需要与有下载需求的季交叉
+	for _, episodeInfo := range seriesInfo.EpList {
+
+		_, ok := seriesInfo.NeedDlSeasonDict[episodeInfo.Season]
+		if ok == false {
+			continue
+		}
+
+		// 创建一个 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)
+			}()
+			// 匹配对应的 Eps 去处理
+			seasonEpsKey := my_util.GetEpisodeKeyName(episodeInfo.Season, episodeInfo.Episode)
+			if fullSeasonSubDict[seasonEpsKey] == nil || len(fullSeasonSubDict[seasonEpsKey]) < 1 {
+				d.log.Infoln("seriesDlFunc.saveFullSeasonSub, no sub found, Skip", seasonEpsKey)
+				done <- nil
+			}
+
+			done <- d.oneVideoSelectBestSub(episodeInfo.FileFullPath, fullSeasonSubDict[seasonEpsKey])
+		}()
+
+		select {
+		case errInterface := <-done:
+			if errInterface != nil {
+				errSave2Local = errInterface.(error)
+				d.log.Errorln(errInterface.(error))
+			} else {
+				save2LocalSubCount++
+			}
+
+			break
+		case p := <-panicChan:
+			// 遇到内部的 panic,向外抛出
+			d.log.Errorln("seriesDlFunc.oneVideoSelectBestSub panicChan", p)
+			break
+		case <-ctx.Done():
+			{
+				err = errors.New(fmt.Sprintf("cancel at NeedDlEpsKeyList.oneVideoSelectBestSub, %v S%dE%d", seriesInfo.Name, episodeInfo.Season, episodeInfo.Episode))
+				d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
+				return err
+			}
+		}
+	}
+	// 是否清理全季的缓存字幕文件夹
+	if d.settings.AdvancedSettings.SaveFullSeasonTmpSubtitles == false {
+		err = sub_helper.DeleteOneSeasonSubCacheFolder(seriesInfo.DirPath)
+		if err != nil {
+			d.log.Errorln("seriesDlFunc.DeleteOneSeasonSubCacheFolder", err)
+		}
+	}
+
+	if save2LocalSubCount < 1 {
+		// 下载的字幕都没有一个能够写入到本地的,那么就有问题了
+		d.downloadQueue.AutoDetectUpdateJobStatus(job, errSave2Local)
+		return errSave2Local
+	}
+	// 哪怕有一个写入到本地成功了,也无需对本次任务报错
+	d.downloadQueue.AutoDetectUpdateJobStatus(job, nil)
+	// TODO 刷新字幕,这里是 Emby 的,如果是其他的,需要再对接对应的媒体服务器
+	if d.settings.EmbySettings.Enable == true && d.embyHelper != nil && job.MediaServerInsideVideoID != "" {
+		err = d.embyHelper.EmbyApi.UpdateVideoSubList(job.MediaServerInsideVideoID)
+		if err != nil {
+			d.log.Errorln("UpdateVideoSubList", job.SeriesRootDirPath, job.MediaServerInsideVideoID, job.Season, job.Episode, "Error:", err)
+			return err
+		}
+	}
+
+	return nil
+}

+ 100 - 276
internal/pkg/downloader/downloader.go

@@ -3,30 +3,26 @@ package downloader
 import (
 	"errors"
 	"fmt"
-	"path/filepath"
 	"sync"
 
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
+
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/global_value"
 
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier/csf"
 
-	"github.com/allanpk716/ChineseSubFinder/internal/dao"
 	"github.com/allanpk716/ChineseSubFinder/internal/ifaces"
 	embyHelper "github.com/allanpk716/ChineseSubFinder/internal/logic/emby_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/file_downloader"
 	markSystem "github.com/allanpk716/ChineseSubFinder/internal/logic/mark_system"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/pre_download_process"
-	"github.com/allanpk716/ChineseSubFinder/internal/logic/series_helper"
 	subSupplier "github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_timeline_fixer"
-	"github.com/allanpk716/ChineseSubFinder/internal/models"
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_folder"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	subCommon "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/common"
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/task_queue"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/common"
 	taskQueue2 "github.com/allanpk716/ChineseSubFinder/internal/types/task_queue"
@@ -209,40 +205,112 @@ func (d *Downloader) QueueDownloader() {
 		return
 	}
 	// --------------------------------------------------
-	// 判断是否看过
-	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
+	// 这个任务如果是 series 那么需要考虑是否原始存入的信息是缺失的,需要补全
+	{
+		if oneJob.VideoType == common.Series && (oneJob.SeriesRootDirPath == "" || oneJob.Season <= 0 || oneJob.Episode <= 0) {
+			// 连续剧的时候需要额外提交信息
+			epsVideoNfoInfo, err := decode.GetVideoNfoInfo4OneSeriesEpisode(oneJob.VideoFPath)
+			if err != nil {
+				d.log.Errorln("decode.GetVideoNfoInfo4OneSeriesEpisode()", err)
+				return
+			}
+			seriesInfoDirPath := decode.GetSeriesDirRootFPath(oneJob.VideoFPath)
+			if seriesInfoDirPath == "" {
+				d.log.Errorln(fmt.Sprintf("decode.GetSeriesDirRootFPath == Empty, %s", oneJob.VideoFPath))
+				return
+			}
+			oneJob.Season = epsVideoNfoInfo.Season
+			oneJob.Episode = epsVideoNfoInfo.Episode
+			oneJob.SeriesRootDirPath = seriesInfoDirPath
 		}
 	}
-	// 不管如何,只要是发现数据库中有 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
+	// 判断是否看过,这个只有 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
+			}
 		}
-		if bok == false {
-			d.log.Errorln("d.downloadQueue.Update() Failed")
+		// 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
 		}
-		d.log.Infoln("Is Played, Ignore This Job")
-		return
 	}
+	// --------------------------------------------------
+	// 判断是否需要跳过,因为如果是 Normal 扫描出来的,那么可能因为视频时间久远,下载一次即可
+	{
+		if oneJob.TaskPriority > task_queue.HighTaskPriorityLevel {
+			// 优先级大于 3,那么就不是很急的任务,才需要判断
+			if oneJob.VideoType == common.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 == common.Series {
 
+				bNeedDlSub, seriesInfo, err := d.subSupplierHub.SeriesNeedDlSub(oneJob.SeriesRootDirPath,
+					false, false)
+				if err != nil {
+					d.log.Errorln("SeriesNeedDlSub", err)
+					return
+				}
+				// 需要下载的 Eps 是否与 Normal 判断这个连续剧中有那些剧集需要下载的,情况符合。通过下载的时间来判断
+				epsKey := my_util.GetEpisodeKeyName(oneJob.Season, oneJob.Episode)
+				_, found := seriesInfo.NeedDlEpsKeyList[epsKey]
+				if bNeedDlSub == false || found == 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("SeriesNeedDlSub == false, Ignore This Job")
+					return
+				}
+			}
+		}
+	}
 	// 取出来后,需要标记为正在下载
 	oneJob.JobStatus = taskQueue2.Downloading
 	bok, err = d.downloadQueue.Update(oneJob)
@@ -331,247 +399,3 @@ func (d *Downloader) Cancel() {
 	d.cancel()
 	d.log.Infoln("Downloader.Cancel()")
 }
-
-func (d *Downloader) movieDlFunc(ctx context.Context, job taskQueue2.OneJob, downloadIndex int64) error {
-
-	nowSubSupplierHub := d.subSupplierHub
-	if nowSubSupplierHub.Suppliers == nil || len(nowSubSupplierHub.Suppliers) < 1 {
-		d.log.Infoln("Wait SupplierCheck Update *subSupplierHub, movieDlFunc Skip this time")
-		return nil
-	}
-
-	// 字幕都下载缓存好了,需要抉择存哪一个,优先选择中文双语的,然后到中文
-	organizeSubFiles, err := nowSubSupplierHub.DownloadSub4Movie(job.VideoFPath, downloadIndex)
-	if err != nil {
-		err = errors.New(fmt.Sprintf("subSupplierHub.DownloadSub4Movie: %v, %v", job.VideoFPath, err))
-		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
-		return err
-	}
-	// 返回的两个值都是 nil 的时候,就是没有下载到字幕
-	if organizeSubFiles == nil || len(organizeSubFiles) < 1 {
-		d.log.Infoln(task_queue.ErrNoSubFound.Error(), filepath.Base(job.VideoFPath))
-		d.downloadQueue.AutoDetectUpdateJobStatus(job, task_queue.ErrNoSubFound)
-		return nil
-	}
-
-	err = d.oneVideoSelectBestSub(job.VideoFPath, organizeSubFiles)
-	if err != nil {
-		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
-		return err
-	}
-
-	d.downloadQueue.AutoDetectUpdateJobStatus(job, nil)
-
-	// TODO 刷新字幕,这里是 Emby 的,如果是其他的,需要再对接对应的媒体服务器
-	if d.settings.EmbySettings.Enable == true && d.embyHelper != nil && job.MediaServerInsideVideoID != "" {
-
-		d.log.Infoln("Refresh Emby Subtitle with id:", job.MediaServerInsideVideoID)
-		err = d.embyHelper.EmbyApi.UpdateVideoSubList(job.MediaServerInsideVideoID)
-		if err != nil {
-			d.log.Errorln("UpdateVideoSubList", job.VideoFPath, job.MediaServerInsideVideoID, "Error:", err)
-			return err
-		}
-	} else {
-		if d.settings.EmbySettings.Enable == false {
-			d.log.Infoln("UpdateVideoSubList", job.VideoFPath, "Skip, because Emby enable is false")
-		} else if d.embyHelper == nil {
-			d.log.Infoln("UpdateVideoSubList", job.VideoFPath, "Skip, because EmbyHelper is nil")
-		} else if job.MediaServerInsideVideoID == "" {
-			d.log.Infoln("UpdateVideoSubList", job.VideoFPath, "Skip, because MediaServerInsideVideoID is empty")
-		}
-	}
-
-	return nil
-}
-
-func (d *Downloader) seriesDlFunc(ctx context.Context, job taskQueue2.OneJob, downloadIndex int64) error {
-
-	nowSubSupplierHub := d.subSupplierHub
-	if nowSubSupplierHub == nil || nowSubSupplierHub.Suppliers == nil || len(nowSubSupplierHub.Suppliers) < 1 {
-		d.log.Infoln("Wait SupplierCheck Update *subSupplierHub, movieDlFunc Skip this time")
-		return nil
-	}
-	var err error
-	// 设置只有一集需要下载
-	epsMap := make(map[int][]int, 0)
-	epsMap[job.Season] = []int{job.Episode}
-
-	if job.VideoType == common.Series && (job.SeriesRootDirPath == "" || job.Season <= 0 || job.Episode <= 0) {
-
-		// 连续剧的时候需要额外提交信息
-		epsVideoNfoInfo, err := decode.GetVideoNfoInfo4OneSeriesEpisode(job.VideoFPath)
-		if err != nil {
-			return err
-		}
-		seriesInfoDirPath := decode.GetSeriesDirRootFPath(job.VideoFPath)
-		if seriesInfoDirPath == "" {
-			err = errors.New(fmt.Sprintf("decode.GetSeriesDirRootFPath == Empty, %s", job.VideoFPath))
-			return err
-		}
-
-		job.Season = epsVideoNfoInfo.Season
-		job.Episode = epsVideoNfoInfo.Episode
-		job.SeriesRootDirPath = seriesInfoDirPath
-		// 如果进来的时候是 Season 和 Eps 是 -1, 那么就需要重新赋值那些集是需要下载的,否则后面会跳过
-		epsMap = make(map[int][]int, 0)
-		epsMap[job.Season] = []int{job.Episode}
-	}
-
-	// 这里拿到了这一部连续剧的所有的剧集信息,以及所有下载到的字幕信息
-	seriesInfo, err := series_helper.ReadSeriesInfoFromDir(
-		d.log, job.SeriesRootDirPath,
-		d.settings.AdvancedSettings.TaskQueue.ExpirationTime,
-		false,
-		false,
-		epsMap)
-	if err != nil {
-		err = errors.New(fmt.Sprintf("seriesDlFunc.ReadSeriesInfoFromDir, Error: %v", err))
-		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
-		return err
-	}
-	// 下载好的字幕文件
-	var organizeSubFiles map[string][]string
-	// 下载的接口是统一的
-	organizeSubFiles, err = nowSubSupplierHub.DownloadSub4Series(job.SeriesRootDirPath,
-		seriesInfo,
-		downloadIndex)
-	if err != nil {
-		err = errors.New(fmt.Sprintf("seriesDlFunc.DownloadSub4Series %v S%vE%v %v", filepath.Base(job.SeriesRootDirPath), job.Season, job.Episode, err))
-		d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
-		return err
-	}
-	// 是否下载到字幕了
-	if organizeSubFiles == nil || len(organizeSubFiles) < 1 {
-		d.log.Infoln(task_queue.ErrNoSubFound.Error(), filepath.Base(job.VideoFPath), job.Season, job.Episode)
-		d.downloadQueue.AutoDetectUpdateJobStatus(job, task_queue.ErrNoSubFound)
-		return nil
-	}
-
-	var errSave2Local error
-	save2LocalSubCount := 0
-	// 只针对需要下载字幕的视频进行字幕的选择保存
-	subVideoCount := 0
-	for epsKey, episodeInfo := range seriesInfo.NeedDlEpsKeyList {
-
-		// 创建一个 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)
-			}()
-			// 匹配对应的 Eps 去处理
-			done <- d.oneVideoSelectBestSub(episodeInfo.FileFullPath, organizeSubFiles[epsKey])
-		}()
-
-		select {
-		case errInterface := <-done:
-			if errInterface != nil {
-				errSave2Local = errInterface.(error)
-				d.log.Errorln(errInterface.(error))
-			} else {
-				save2LocalSubCount++
-			}
-			break
-		case p := <-panicChan:
-			// 遇到内部的 panic,向外抛出
-			d.log.Errorln("seriesDlFunc.oneVideoSelectBestSub panicChan", p)
-			break
-		case <-ctx.Done():
-			{
-				err = errors.New(fmt.Sprintf("cancel at NeedDlEpsKeyList.oneVideoSelectBestSub, %v S%dE%d", seriesInfo.Name, episodeInfo.Season, episodeInfo.Episode))
-				d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
-				return err
-			}
-		}
-
-		subVideoCount++
-	}
-	// 这里会拿到一份季度字幕的列表比如,Key 是 S1E0 S2E0 S3E0,value 是新的存储位置
-	fullSeasonSubDict := d.saveFullSeasonSub(seriesInfo, organizeSubFiles)
-	// TODO 季度的字幕包,应该优先于零散的字幕吧,暂定就这样了,注意是全部都替换
-	// 需要与有下载需求的季交叉
-	for _, episodeInfo := range seriesInfo.EpList {
-
-		_, ok := seriesInfo.NeedDlSeasonDict[episodeInfo.Season]
-		if ok == false {
-			continue
-		}
-
-		// 创建一个 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)
-			}()
-			// 匹配对应的 Eps 去处理
-			seasonEpsKey := my_util.GetEpisodeKeyName(episodeInfo.Season, episodeInfo.Episode)
-			if fullSeasonSubDict[seasonEpsKey] == nil || len(fullSeasonSubDict[seasonEpsKey]) < 1 {
-				d.log.Infoln("seriesDlFunc.saveFullSeasonSub, no sub found, Skip", seasonEpsKey)
-				done <- nil
-			}
-
-			done <- d.oneVideoSelectBestSub(episodeInfo.FileFullPath, fullSeasonSubDict[seasonEpsKey])
-		}()
-
-		select {
-		case errInterface := <-done:
-			if errInterface != nil {
-				errSave2Local = errInterface.(error)
-				d.log.Errorln(errInterface.(error))
-			} else {
-				save2LocalSubCount++
-			}
-
-			break
-		case p := <-panicChan:
-			// 遇到内部的 panic,向外抛出
-			d.log.Errorln("seriesDlFunc.oneVideoSelectBestSub panicChan", p)
-			break
-		case <-ctx.Done():
-			{
-				err = errors.New(fmt.Sprintf("cancel at NeedDlEpsKeyList.oneVideoSelectBestSub, %v S%dE%d", seriesInfo.Name, episodeInfo.Season, episodeInfo.Episode))
-				d.downloadQueue.AutoDetectUpdateJobStatus(job, err)
-				return err
-			}
-		}
-	}
-	// 是否清理全季的缓存字幕文件夹
-	if d.settings.AdvancedSettings.SaveFullSeasonTmpSubtitles == false {
-		err = sub_helper.DeleteOneSeasonSubCacheFolder(seriesInfo.DirPath)
-		if err != nil {
-			d.log.Errorln("seriesDlFunc.DeleteOneSeasonSubCacheFolder", err)
-		}
-	}
-
-	if save2LocalSubCount < 1 {
-		// 下载的字幕都没有一个能够写入到本地的,那么就有问题了
-		d.downloadQueue.AutoDetectUpdateJobStatus(job, errSave2Local)
-		return errSave2Local
-	}
-	// 哪怕有一个写入到本地成功了,也无需对本次任务报错
-	d.downloadQueue.AutoDetectUpdateJobStatus(job, nil)
-	// TODO 刷新字幕,这里是 Emby 的,如果是其他的,需要再对接对应的媒体服务器
-	if d.settings.EmbySettings.Enable == true && d.embyHelper != nil && job.MediaServerInsideVideoID != "" {
-		err = d.embyHelper.EmbyApi.UpdateVideoSubList(job.MediaServerInsideVideoID)
-		if err != nil {
-			d.log.Errorln("UpdateVideoSubList", job.SeriesRootDirPath, job.MediaServerInsideVideoID, job.Season, job.Episode, "Error:", err)
-			return err
-		}
-	}
-
-	return nil
-}