downloader_things.go 9.8 KB

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