util.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package model
  2. import (
  3. "fmt"
  4. "github.com/allanpk716/ChineseSubFinder/common"
  5. "github.com/go-resty/resty/v2"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "regexp"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. // NewHttpClient 新建一个 resty 的对象
  18. func NewHttpClient(_reqParam ...common.ReqParam) *resty.Client {
  19. //const defUserAgent = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"
  20. const defUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 Edg/91.0.864.41"
  21. var reqParam common.ReqParam
  22. var HttpProxy, UserAgent, Referer string
  23. if len(_reqParam) > 0 {
  24. reqParam = _reqParam[0]
  25. }
  26. if len(reqParam.HttpProxy) > 0 {
  27. HttpProxy = reqParam.HttpProxy
  28. }
  29. if len(reqParam.UserAgent) > 0 {
  30. UserAgent = reqParam.UserAgent
  31. } else {
  32. UserAgent = defUserAgent
  33. }
  34. if len(reqParam.Referer) > 0 {
  35. Referer = reqParam.Referer
  36. }
  37. httpClient := resty.New()
  38. httpClient.SetTimeout(common.HTMLTimeOut)
  39. if HttpProxy != "" {
  40. httpClient.SetProxy(HttpProxy)
  41. }
  42. httpClient.SetHeaders(map[string]string{
  43. "Content-Type": "application/json",
  44. "User-Agent": UserAgent,
  45. })
  46. if len(Referer) > 0 {
  47. httpClient.SetHeader("Referer", Referer)
  48. }
  49. return httpClient
  50. }
  51. // DownFile 从指定的 url 下载文件
  52. func DownFile(urlStr string, _reqParam ...common.ReqParam) ([]byte, string, error) {
  53. var reqParam common.ReqParam
  54. if len(_reqParam) > 0 {
  55. reqParam = _reqParam[0]
  56. }
  57. httpClient := NewHttpClient(reqParam)
  58. resp, err := httpClient.R().Get(urlStr)
  59. if err != nil {
  60. return nil, "", err
  61. }
  62. filename := GetFileName(resp.RawResponse)
  63. return resp.Body(), filename, nil
  64. }
  65. // GetFileName 获取下载文件的文件名
  66. func GetFileName(resp *http.Response) string {
  67. contentDisposition := resp.Header.Get("Content-Disposition")
  68. if len(contentDisposition) == 0 {
  69. return ""
  70. }
  71. re := regexp.MustCompile(`filename=["]*([^"]+)["]*`)
  72. matched := re.FindStringSubmatch(contentDisposition)
  73. if matched == nil || len(matched) == 0 || len(matched[0]) == 0 {
  74. //fmt.Println("######")
  75. return ""
  76. }
  77. return matched[1]
  78. }
  79. // AddBaseUrl 判断传入的 url 是否需要拼接 baseUrl
  80. func AddBaseUrl(baseUrl, url string) string {
  81. if strings.Contains(url, "://") {
  82. return url
  83. }
  84. return fmt.Sprintf("%s%s", baseUrl, url)
  85. }
  86. func GetDebugFolder() (string, error) {
  87. if defDebugFolder == "" {
  88. nowProcessRoot, _ := os.Getwd()
  89. nowProcessRoot = path.Join(nowProcessRoot, common.DebugFolder)
  90. err := os.MkdirAll(nowProcessRoot, os.ModePerm)
  91. if err != nil {
  92. return "", err
  93. }
  94. defDebugFolder = nowProcessRoot
  95. return nowProcessRoot, err
  96. }
  97. return defDebugFolder, nil
  98. }
  99. // GetRootTmpFolder 获取缓存的根目录,每一个视频的缓存将在其中额外新建子集文件夹
  100. func GetRootTmpFolder() (string, error) {
  101. if defTmpFolder == "" {
  102. nowProcessRoot, _ := os.Getwd()
  103. nowProcessRoot = path.Join(nowProcessRoot, common.TmpFolder)
  104. err := os.MkdirAll(nowProcessRoot, os.ModePerm)
  105. if err != nil {
  106. return "", err
  107. }
  108. defTmpFolder = nowProcessRoot
  109. return nowProcessRoot, err
  110. }
  111. return defTmpFolder, nil
  112. }
  113. // ClearRootTmpFolder 清理缓存的根目录,将里面的子文件夹一并清理
  114. func ClearRootTmpFolder() error {
  115. nowTmpFolder, err := GetRootTmpFolder()
  116. if err != nil {
  117. return err
  118. }
  119. pathSep := string(os.PathSeparator)
  120. files, err := ioutil.ReadDir(nowTmpFolder)
  121. if err != nil {
  122. return err
  123. }
  124. for _, curFile := range files {
  125. fullPath := nowTmpFolder + pathSep + curFile.Name()
  126. if curFile.IsDir() {
  127. err = os.RemoveAll(fullPath)
  128. if err != nil {
  129. return err
  130. }
  131. } else {
  132. // 这里就是文件了
  133. err = os.Remove(fullPath)
  134. if err != nil {
  135. return err
  136. }
  137. }
  138. }
  139. return nil
  140. }
  141. // GetTmpFolder 获取缓存的文件夹,没有则新建
  142. func GetTmpFolder(folderName string) (string, error) {
  143. rootPath, err := GetRootTmpFolder()
  144. if err != nil {
  145. return "", err
  146. }
  147. tmpFolderFullPath :=path.Join(rootPath, folderName)
  148. err = os.MkdirAll(tmpFolderFullPath, os.ModePerm)
  149. if err != nil {
  150. return "", err
  151. }
  152. return tmpFolderFullPath, nil
  153. }
  154. // ClearTmpFolder 清理指定的缓存文件夹
  155. func ClearTmpFolder(folderName string) error {
  156. nowTmpFolder, err := GetTmpFolder(folderName)
  157. if err != nil {
  158. return err
  159. }
  160. pathSep := string(os.PathSeparator)
  161. files, err := ioutil.ReadDir(nowTmpFolder)
  162. if err != nil {
  163. return err
  164. }
  165. for _, curFile := range files {
  166. fullPath := nowTmpFolder + pathSep + curFile.Name()
  167. if curFile.IsDir() {
  168. err = os.RemoveAll(fullPath)
  169. if err != nil {
  170. return err
  171. }
  172. } else {
  173. // 这里就是文件了
  174. err = os.Remove(fullPath)
  175. if err != nil {
  176. return err
  177. }
  178. }
  179. }
  180. return nil
  181. }
  182. func CopyFile(dstName, srcName string) (written int64, err error) {
  183. src, err := os.Open(srcName)
  184. if err != nil {
  185. return
  186. }
  187. defer src.Close()
  188. dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
  189. if err != nil {
  190. return
  191. }
  192. defer dst.Close()
  193. return io.Copy(dst, src)
  194. }
  195. func IsDir(path string) bool {
  196. s, err := os.Stat(path)
  197. if err != nil {
  198. return false
  199. }
  200. return s.IsDir()
  201. }
  202. // VideoNameSearchKeywordMaker 拼接视频搜索的 title 和 年份
  203. func VideoNameSearchKeywordMaker(title string, year string) string {
  204. iYear, err := strconv.Atoi(year)
  205. if err != nil {
  206. // 允许的错误
  207. GetLogger().Errorln("VideoNameSearchKeywordMaker", "year to int", err)
  208. iYear = 0
  209. }
  210. searchKeyword := title
  211. if iYear >= 2020 {
  212. searchKeyword = searchKeyword + " " + year
  213. }
  214. return searchKeyword
  215. }
  216. // SearchMatchedVideoFile 搜索符合后缀名的视频文件
  217. func SearchMatchedVideoFile(dir string) ([]string, error) {
  218. var fileFullPathList = make([]string, 0)
  219. pathSep := string(os.PathSeparator)
  220. files, err := ioutil.ReadDir(dir)
  221. if err != nil {
  222. return nil, err
  223. }
  224. for _, curFile := range files {
  225. fullPath := dir + pathSep + curFile.Name()
  226. if curFile.IsDir() {
  227. // 内层的错误就无视了
  228. oneList, _ := SearchMatchedVideoFile(fullPath)
  229. if oneList != nil {
  230. fileFullPathList = append(fileFullPathList, oneList...)
  231. }
  232. } else {
  233. // 这里就是文件了
  234. if IsWantedVideoExtDef(curFile.Name()) == true {
  235. fileFullPathList = append(fileFullPathList, fullPath)
  236. }
  237. }
  238. }
  239. return fileFullPathList, nil
  240. }
  241. // SearchMatchedSubFile 搜索符合后缀名的视频文件
  242. func SearchMatchedSubFile(dir string) ([]string, error) {
  243. // 这里有个梗,会出现 __MACOSX 这类文件夹,那么里面会有一样的文件,需要用文件大小排除一下,至少大于 1 kb 吧
  244. var fileFullPathList = make([]string, 0)
  245. pathSep := string(os.PathSeparator)
  246. files, err := ioutil.ReadDir(dir)
  247. if err != nil {
  248. return nil, err
  249. }
  250. for _, curFile := range files {
  251. fullPath := dir + pathSep + curFile.Name()
  252. if curFile.IsDir() {
  253. // 内层的错误就无视了
  254. oneList, _ := SearchMatchedSubFile(fullPath)
  255. if oneList != nil {
  256. fileFullPathList = append(fileFullPathList, oneList...)
  257. }
  258. } else {
  259. // 这里就是文件了
  260. if curFile.Size() < 1000 {
  261. continue
  262. }
  263. if IsSubExtWanted(filepath.Ext(curFile.Name())) == true {
  264. fileFullPathList = append(fileFullPathList, fullPath)
  265. }
  266. }
  267. }
  268. return fileFullPathList, nil
  269. }
  270. // IsWantedVideoExtDef 后缀名是否符合规则
  271. func IsWantedVideoExtDef(fileName string) bool {
  272. // TODO 强制使用固定的视频后缀名匹配列表,后续有需求再考虑额实现外部可配置的列表
  273. if len(wantedExtList) < 1 {
  274. defExtList = append(defExtList, common.VideoExtMp4)
  275. defExtList = append(defExtList, common.VideoExtMkv)
  276. defExtList = append(defExtList, common.VideoExtRmvb)
  277. defExtList = append(defExtList, common.VideoExtIso)
  278. wantedExtList = append(defExtList, common.VideoExtMp4)
  279. wantedExtList = append(defExtList, common.VideoExtMkv)
  280. wantedExtList = append(defExtList, common.VideoExtRmvb)
  281. wantedExtList = append(defExtList, common.VideoExtIso)
  282. }
  283. fileName = strings.ToLower(filepath.Ext(fileName))
  284. for _, s := range wantedExtList {
  285. if s == fileName {
  286. return true
  287. }
  288. }
  289. return false
  290. }
  291. func GetEpisodeKeyName(season, eps int) string {
  292. return "S" + strconv.Itoa(season) + "E" +strconv.Itoa(eps)
  293. }
  294. // ReloadBrowser 提前把浏览器下载好
  295. func ReloadBrowser() {
  296. page, err := NewBrowserLoadPage("https://www.baidu.com", "", 300*time.Second, 2)
  297. if err != nil {
  298. return
  299. }
  300. defer page.Close()
  301. }
  302. var (
  303. defDebugFolder = ""
  304. defTmpFolder = ""
  305. wantedExtList = make([]string, 0) // 人工确认的需要监控的视频后缀名
  306. defExtList = make([]string, 0) // 内置支持的视频后缀名列表
  307. )