downloader_things.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package downloader
  2. import (
  3. "github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
  4. "github.com/allanpk716/ChineseSubFinder/internal/pkg/folder_helper"
  5. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  6. subcommon "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/common"
  7. "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_helper"
  8. "github.com/allanpk716/ChineseSubFinder/internal/types/series"
  9. "github.com/allanpk716/ChineseSubFinder/internal/types/subparser"
  10. "os"
  11. "path/filepath"
  12. )
  13. // oneVideoSelectBestSub 一个视频,选择最佳的一个字幕(也可以保存所有网站第一个最佳字幕)
  14. func (d *Downloader) oneVideoSelectBestSub(oneVideoFullPath string, organizeSubFiles []string) {
  15. // 如果没有则直接跳过
  16. if organizeSubFiles == nil || len(organizeSubFiles) < 1 {
  17. return
  18. }
  19. var err error
  20. // 得到目标视频文件的文件名
  21. videoFileName := filepath.Base(oneVideoFullPath)
  22. // -------------------------------------------------
  23. // 调试缓存,把下载好的字幕写到对应的视频目录下,方便调试
  24. if d.settings.AdvancedSettings.DebugMode == true {
  25. err = folder_helper.CopyFiles2DebugFolder([]string{videoFileName}, organizeSubFiles)
  26. if err != nil {
  27. d.log.Errorln("copySubFile2DesFolder", err)
  28. }
  29. }
  30. // -------------------------------------------------
  31. /*
  32. 这里需要额外考虑一点,有可能当前目录已经有一个 .Default .Forced 标记的字幕了
  33. 那么下载字幕丢进来的时候就需要提前把这个字幕找出来,去除整个 .Default .Forced 标记
  34. 然后进行正常的下载,存储和替换字幕,最后将本次操作的第一次标记为 .Default
  35. */
  36. // 不管是不是保存多个字幕,都要先扫描本地的字幕,进行 .Default .Forced 去除
  37. // 这个视频的所有字幕,去除 .default .Forced 标记
  38. err = sub_helper.SearchVideoMatchSubFileAndRemoveExtMark(oneVideoFullPath)
  39. if err != nil {
  40. // 找个错误可以忍
  41. d.log.Errorln("SearchVideoMatchSubFileAndRemoveExtMark,", oneVideoFullPath, err)
  42. }
  43. if d.settings.AdvancedSettings.SaveMultiSub == false {
  44. // 选择最优的一个字幕
  45. var finalSubFile *subparser.FileInfo
  46. finalSubFile = d.mk.SelectOneSubFile(organizeSubFiles)
  47. if finalSubFile == nil {
  48. d.log.Warnln("Found", len(organizeSubFiles), " subtitles but not one fit:", oneVideoFullPath)
  49. return
  50. }
  51. /*
  52. 这里还有一个梗,Emby、jellyfin 支持 default 和 forced 扩展字段
  53. 但是,plex 只支持 forced
  54. 那么就比较麻烦,干脆,normal 的命名格式化实例,就不设置 default 了,forced 不想用,因为可能会跟你手动选择的字幕冲突(下次观看的时候,理论上也可能不会)
  55. */
  56. // 判断配置文件中的字幕命名格式化的选择
  57. bSetDefault := true
  58. if d.subNameFormatter == subcommon.Normal {
  59. bSetDefault = false
  60. }
  61. // 找到了,写入文件
  62. err = d.writeSubFile2VideoPath(oneVideoFullPath, *finalSubFile, "", bSetDefault, false)
  63. if err != nil {
  64. d.log.Errorln("SaveMultiSub:", d.settings.AdvancedSettings.SaveMultiSub, "writeSubFile2VideoPath:", err)
  65. return
  66. }
  67. } else {
  68. // 每个网站 Top1 的字幕
  69. siteNames, finalSubFiles := d.mk.SelectEachSiteTop1SubFile(organizeSubFiles)
  70. if len(siteNames) < 0 {
  71. d.log.Warnln("SelectEachSiteTop1SubFile found none sub file")
  72. return
  73. }
  74. // 多网站 Top 1 字幕保存的时候,第一个设置为 Default 即可
  75. /*
  76. 由于新功能支持了字幕命名格式的选择,那么如果触发了多个字幕保存的逻辑,如果不调整
  77. 则会遇到,top1 先写入,然后 top2 覆盖 top1 ,以此类推的情况出现
  78. 所以如果开启了 Normal SubNameFormatter 的功能,则要反序写入文件
  79. 如果是 Emby 的字幕命名格式则无需考虑此问题,因为每个网站只会有一个字幕,且字幕命名格式决定了不会重复写入覆盖
  80. */
  81. if d.subNameFormatter == subcommon.Emby {
  82. for i, file := range finalSubFiles {
  83. setDefault := false
  84. if i == 0 {
  85. setDefault = true
  86. }
  87. err = d.writeSubFile2VideoPath(oneVideoFullPath, file, siteNames[i], setDefault, false)
  88. if err != nil {
  89. d.log.Errorln("SaveMultiSub:", d.settings.AdvancedSettings.SaveMultiSub, "writeSubFile2VideoPath:", err)
  90. return
  91. }
  92. }
  93. } else {
  94. // 默认这里就是 normal 模式
  95. // 逆序写入
  96. /*
  97. 这里还有一个梗,Emby、jellyfin 支持 default 和 forced 扩展字段
  98. 但是,plex 只支持 forced
  99. 那么就比较麻烦,干脆,normal 的命名格式化实例,就不设置 default 了,forced 不想用,因为可能会跟你手动选择的字幕冲突(下次观看的时候,理论上也可能不会)
  100. */
  101. for i := len(finalSubFiles) - 1; i > -1; i-- {
  102. err = d.writeSubFile2VideoPath(oneVideoFullPath, finalSubFiles[i], siteNames[i], false, false)
  103. if err != nil {
  104. d.log.Errorln("SaveMultiSub:", d.settings.AdvancedSettings.SaveMultiSub, "writeSubFile2VideoPath:", err)
  105. return
  106. }
  107. }
  108. }
  109. }
  110. // -------------------------------------------------
  111. }
  112. // saveFullSeasonSub 这里就需要单独存储到连续剧每一季的文件夹的特殊文件夹中。需要跟 DeleteOneSeasonSubCacheFolder 关联起来
  113. func (d *Downloader) saveFullSeasonSub(seriesInfo *series.SeriesInfo, organizeSubFiles map[string][]string) map[string][]string {
  114. var fullSeasonSubDict = make(map[string][]string)
  115. for _, season := range seriesInfo.SeasonDict {
  116. seasonKey := my_util.GetEpisodeKeyName(season, 0)
  117. subs, ok := organizeSubFiles[seasonKey]
  118. if ok == false {
  119. continue
  120. }
  121. for _, sub := range subs {
  122. subFileName := filepath.Base(sub)
  123. newSeasonSubRootPath, err := folder_helper.GetDebugFolderByName([]string{
  124. filepath.Base(seriesInfo.DirPath),
  125. "Sub_" + seasonKey})
  126. if err != nil {
  127. d.log.Errorln("saveFullSeasonSub.GetDebugFolderByName", subFileName, err)
  128. continue
  129. }
  130. newSubFullPath := filepath.Join(newSeasonSubRootPath, subFileName)
  131. err = my_util.CopyFile(sub, newSubFullPath)
  132. if err != nil {
  133. d.log.Errorln("saveFullSeasonSub.CopyFile", subFileName, err)
  134. continue
  135. }
  136. // 从字幕的文件名推断是 哪一季 的 那一集
  137. _, gusSeason, gusEpisode, err := decode.GetSeasonAndEpisodeFromSubFileName(subFileName)
  138. if err != nil {
  139. return nil
  140. }
  141. // 把整季的字幕缓存位置也提供出去,如果之前没有下载到的,这里返回出来的可以补上
  142. seasonEpsKey := my_util.GetEpisodeKeyName(gusSeason, gusEpisode)
  143. _, ok := fullSeasonSubDict[seasonEpsKey]
  144. if ok == false {
  145. // 初始化
  146. fullSeasonSubDict[seasonEpsKey] = make([]string, 0)
  147. }
  148. fullSeasonSubDict[seasonEpsKey] = append(fullSeasonSubDict[seasonEpsKey], sub)
  149. }
  150. }
  151. return fullSeasonSubDict
  152. }
  153. // 在前面需要进行语言的筛选、排序,这里仅仅是存储, extraSubPreName 这里传递是字幕的网站,有就认为是多字幕的存储。空就是单字幕,单字幕就可以setDefault
  154. func (d *Downloader) writeSubFile2VideoPath(videoFileFullPath string, finalSubFile subparser.FileInfo, extraSubPreName string, setDefault bool, skipExistFile bool) error {
  155. defer d.log.Infoln("----------------------------------")
  156. videoRootPath := filepath.Dir(videoFileFullPath)
  157. subNewName, subNewNameWithDefault, _ := d.subFormatter.GenerateMixSubName(videoFileFullPath, finalSubFile.Ext, finalSubFile.Lang, extraSubPreName)
  158. desSubFullPath := filepath.Join(videoRootPath, subNewName)
  159. if setDefault == true {
  160. // 先判断没有 default 的字幕是否存在了,在的话,先删除,然后再写入
  161. if my_util.IsFile(desSubFullPath) == true {
  162. _ = os.Remove(desSubFullPath)
  163. }
  164. desSubFullPath = filepath.Join(videoRootPath, subNewNameWithDefault)
  165. }
  166. if skipExistFile == true {
  167. // 需要判断文件是否存在在,有则跳过
  168. if my_util.IsFile(desSubFullPath) == true {
  169. d.log.Infoln("OrgSubName:", finalSubFile.Name)
  170. d.log.Infoln("Sub Skip DownAt:", desSubFullPath)
  171. return nil
  172. }
  173. }
  174. // 最后写入字幕
  175. err := my_util.WriteFile(desSubFullPath, finalSubFile.Data)
  176. if err != nil {
  177. return err
  178. }
  179. d.log.Infoln("----------------------------------")
  180. d.log.Infoln("OrgSubName:", finalSubFile.Name)
  181. d.log.Infoln("SubDownAt:", desSubFullPath)
  182. // 然后还需要判断是否需要校正字幕的时间轴
  183. if d.settings.AdvancedSettings.FixTimeLine == true {
  184. err = d.subTimelineFixerHelperEx.Process(videoFileFullPath, desSubFullPath)
  185. if err != nil {
  186. return err
  187. }
  188. }
  189. return nil
  190. }
  191. type DownloadInputData struct {
  192. OneVideoFullPath string
  193. OneSeriesPath string
  194. RootDirPath string
  195. }