瀏覽代碼

重构 socks5 和 http 代理的实现,现在由 goproxy 统一在本地开启 http 代理来中专

Signed-off-by: allan716 <[email protected]>
allan716 3 年之前
父節點
當前提交
520f391991

+ 1 - 0
go.mod

@@ -88,6 +88,7 @@ require (
 	github.com/dgraph-io/ristretto v0.1.0 // indirect
 	github.com/dsnet/compress v0.0.1 // indirect
 	github.com/dustin/go-humanize v1.0.0 // indirect
+	github.com/elazarl/goproxy v0.0.0-20220417044921-416226498f94 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-git/gcfg v1.5.0 // indirect
 	github.com/go-git/go-billy/v5 v5.3.1 // indirect

+ 4 - 0
go.sum

@@ -148,6 +148,9 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/elazarl/goproxy v0.0.0-20220417044921-416226498f94 h1:VIy7cdK7ufs7ctpTFkXJHm1uP3dJSnCGSPysEICB1so=
+github.com/elazarl/goproxy v0.0.0-20220417044921-416226498f94/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
 github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
@@ -518,6 +521,7 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
 github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=

+ 9 - 8
internal/backend/controllers/base/proxy.go

@@ -1,14 +1,15 @@
 package base
 
 import (
+	"github.com/allanpk716/ChineseSubFinder/internal/logic/file_downloader"
 	subSupplier "github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier/shooter"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier/subhd"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier/xunlei"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier/zimuku"
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/gin-gonic/gin"
+	"github.com/huandu/go-clone"
 	"net/http"
 )
 
@@ -25,17 +26,17 @@ func (cb *ControllerBase) CheckProxyHandler(c *gin.Context) {
 		return
 	}
 
-	tmpSettings := settings.GetSettings()
-	tmpSettings.AdvancedSettings.ProxySettings.UseHttpProxy = true
-	tmpSettings.AdvancedSettings.ProxySettings.HttpProxyAddress = checkProxy.HttpProxyAddress
+	tmpFileDownloader := clone.Clone(cb.fileDownloader).(*file_downloader.FileDownloader)
+	tmpFileDownloader.Settings.AdvancedSettings.ProxySettings = &checkProxy.ProxySettings
+	tmpFileDownloader.Settings.AdvancedSettings.ProxySettings.UseProxy = true
 
 	// 使用提交过来的这个代理地址,测试多个字幕网站的可用性
 	subSupplierHub := subSupplier.NewSubSupplierHub(
 		// 这里无需传递下载字幕的缓存实例
-		zimuku.NewSupplier(cb.fileDownloader),
-		xunlei.NewSupplier(cb.fileDownloader),
-		shooter.NewSupplier(cb.fileDownloader),
-		subhd.NewSupplier(cb.fileDownloader),
+		zimuku.NewSupplier(tmpFileDownloader),
+		xunlei.NewSupplier(tmpFileDownloader),
+		shooter.NewSupplier(tmpFileDownloader),
+		subhd.NewSupplier(tmpFileDownloader),
 	)
 
 	outStatus := subSupplierHub.CheckSubSiteStatus()

+ 6 - 0
internal/backend/controllers/v1/settings.go

@@ -43,6 +43,12 @@ func (cb ControllerBase) SettingsHandler(c *gin.Context) {
 			if err != nil {
 				return
 			}
+			if settings.GetSettings().AdvancedSettings.ProxySettings.UseProxy == false {
+				err = settings.GetSettings().AdvancedSettings.ProxySettings.CloseLocalHttpProxyServer()
+				if err != nil {
+					return
+				}
+			}
 			c.JSON(http.StatusOK, backend.ReplyCommon{Message: "Settings Save Success"})
 		}
 	default:

+ 1 - 1
internal/dao/init.go

@@ -68,7 +68,7 @@ func InitDb() error {
 	}
 	// 迁移 schema
 	err = db.AutoMigrate(&models.HotFix{}, &models.SubFormatRec{},
-		&models.IMDBInfo{}, &models.VideoSubInfo{}, &models.MovieOrSeriesLocalInfo{})
+		&models.IMDBInfo{}, &models.VideoSubInfo{})
 	if err != nil {
 		return errors.New(fmt.Sprintf("db AutoMigrate error, %s", err.Error()))
 	}

+ 5 - 0
internal/logic/cron_helper/cron_helper.go

@@ -242,6 +242,11 @@ func (ch *CronHelper) scanVideoProcessAdd2DownloadQueue() {
 		ch.log.Errorln("ScanMovieAndSeriesWait2DownloadSub", err)
 		return
 	}
+	err = videoScanAndRefreshHelper.UpdateLocalVideoCacheInfo(scanResult)
+	if err != nil {
+		ch.log.Errorln("UpdateLocalVideoCacheInfo", err)
+		return
+	}
 	// 过滤出需要下载的视频有那些,并放入队列中
 	err = videoScanAndRefreshHelper.FilterMovieAndSeriesNeedDownload(scanResult)
 	if err != nil {

+ 12 - 0
internal/logic/emby_helper/embyhelper.go

@@ -351,6 +351,18 @@ func (em *EmbyHelper) getMoreVideoInfo(videoID string, isMovieOrSeries bool) (*e
 	}
 }
 
+// 根据 IMDB ID 自动转换路径
+func (em *EmbyHelper) autoFindMappingPathWithMixInfoByIMDBId(mixInfo *emby.EmbyMixInfo, isMovieOrSeries bool) bool {
+
+	if isMovieOrSeries == true {
+
+	} else {
+
+	}
+
+	return false
+}
+
 // findMappingPathWithMixInfo 从 Emby 内置路径匹配到物理路径
 // X:\电影    - /mnt/share1/电影
 // X:\连续剧  - /mnt/share1/连续剧

+ 2 - 1
internal/logic/sub_supplier/subhd/subhd.go

@@ -68,7 +68,8 @@ func NewSupplier(fileDownloader *file_downloader.FileDownloader) *Supplier {
 
 func (s *Supplier) CheckAlive() (bool, int64) {
 
-	proxyStatus, proxySpeed, err := url_connectedness_helper.UrlConnectednessTest(s.settings.AdvancedSettings.SuppliersSettings.SubHD.RootUrl, s.settings.AdvancedSettings.ProxySettings.HttpProxyAddress)
+	proxyStatus, proxySpeed, err := url_connectedness_helper.UrlConnectednessTest(s.settings.AdvancedSettings.SuppliersSettings.SubHD.RootUrl,
+		s.settings.AdvancedSettings.ProxySettings.GetLocalHttpProxyUrl())
 	if err != nil {
 		s.log.Errorln(s.GetSupplierName(), "CheckAlive", "Error", err)
 		s.isAlive = false

+ 1 - 6
internal/logic/sub_supplier/zimuku/zimuku.go

@@ -61,12 +61,7 @@ func NewSupplier(fileDownloader *file_downloader.FileDownloader) *Supplier {
 		sup.tt = common2.OneMovieProcessTimeOut
 	}
 	// 判断是否启用代理
-	if sup.settings.AdvancedSettings.ProxySettings.UseHttpProxy == true {
-		sup.httpProxyAddress = sup.settings.AdvancedSettings.ProxySettings.HttpProxyAddress
-	} else {
-		sup.httpProxyAddress = ""
-	}
-
+	sup.httpProxyAddress = sup.settings.AdvancedSettings.ProxySettings.GetLocalHttpProxyUrl()
 	return &sup
 }
 

+ 9 - 8
internal/models/imdb_info.go

@@ -1,14 +1,15 @@
 package models
 
 type IMDBInfo struct {
-	IMDBID                  string                   `gorm:"primaryKey" json:"imdb_id"  binding:"required"`                   // IMDB ID
-	Name                    string                   `json:"name" binding:"required"`                                         // 视频名称
-	Year                    int                      `gorm:"default:0" json:"year"  binding:"required"`                       // 发布的时间
-	Description             string                   `json:"description"  binding:"required"`                                 // 描述
-	Languages               StringList               `gorm:"type:varchar(255);not null" json:"languages"  binding:"required"` // 语言
-	AKA                     StringList               `gorm:"type:varchar(255);not null" json:"AKA"  binding:"required"`       // 又名 xx xxx
-	VideoSubInfos           []VideoSubInfo           `gorm:"foreignKey:IMDBInfoID"`                                           // 视频对应的字幕,外键约束
-	MovieOrSeriesLocalInfos []MovieOrSeriesLocalInfo `gorm:"foreignKey:IMDBInfoID"`                                           // 视频对应的本地信息,外键约束
+	IMDBID        string         `gorm:"primaryKey" json:"imdb_id"  binding:"required"`                   // IMDB ID
+	Name          string         `json:"name" binding:"required"`                                         // 视频名称
+	Year          int            `gorm:"default:0" json:"year"  binding:"required"`                       // 发布的时间
+	Description   string         `json:"description"  binding:"required"`                                 // 描述
+	Languages     StringList     `gorm:"type:varchar(255);not null" json:"languages"  binding:"required"` // 语言
+	AKA           StringList     `gorm:"type:varchar(255);not null" json:"AKA"  binding:"required"`       // 又名 xx xxx
+	RootDirPath   string         `json:"root_dir_path"`                                                   // 这个电影或者连续剧(不是季的文件夹,而是这个连续剧的目录)路径
+	IsMovie       bool           `json:"is_movie"`                                                        // 不是电影就是连续剧
+	VideoSubInfos []VideoSubInfo `gorm:"foreignKey:IMDBInfoID"`                                           // 视频对应的字幕,外键约束
 }
 
 func NewIMDBInfo(IMDBID string, name string, year int, description string, languages StringList, AKA StringList) *IMDBInfo {

+ 0 - 12
internal/models/movie_or_series_local_info.go

@@ -1,12 +0,0 @@
-package models
-
-type MovieOrSeriesLocalInfo struct {
-	IsMovie      bool   `json:"is_movie"`                         // 不是电影就是连续剧
-	Season       int    `json:"season"`                           // 季度
-	RootDirRPath string `json:"root_dir_r_path"`                  // 这个电影或者连续剧(不是季的文件夹,而是这个连续剧的目录)的相对路径
-	IMDBInfoID   string `json:"imdb_info_id"  binding:"required"` // IMDB ID
-}
-
-func NewMovieOrSeriesLocalInfo(isMovie bool, season int, rootDirRPath string) *MovieOrSeriesLocalInfo {
-	return &MovieOrSeriesLocalInfo{IsMovie: isMovie, Season: season, RootDirRPath: rootDirRPath}
-}

+ 4 - 7
internal/pkg/imdb_helper/imdb.go

@@ -48,7 +48,9 @@ func GetVideoIMDBInfoFromLocal(imdbInfo types.VideoIMDBInfo, _proxySettings ...*
 	// 首先从数据库中查找是否存在这个 IMDB 信息,如果不存在再使用 Web 查找,且写入数据库
 	var imdbInfos []models.IMDBInfo
 	// 把嵌套关联的 has many 的信息都查询出来
-	dao.GetDb().Preload("VideoSubInfos").Limit(1).Where(&models.IMDBInfo{IMDBID: imdbInfo.ImdbId}).Find(&imdbInfos)
+	dao.GetDb().
+		Preload("VideoSubInfos").
+		Limit(1).Where(&models.IMDBInfo{IMDBID: imdbInfo.ImdbId}).Find(&imdbInfos)
 
 	log_helper.GetLogger().Debugln("GetVideoIMDBInfoFromLocal", 1)
 
@@ -83,12 +85,7 @@ func IsChineseVideo(imdbInfo types.VideoIMDBInfo, _proxySettings ...*settings.Pr
 	const chName0 = "chinese"
 	const chName1 = "mandarin"
 
-	var proxySettings *settings.ProxySettings
-	if len(_proxySettings) > 0 {
-		proxySettings = _proxySettings[0]
-	}
-
-	localIMDBInfo, err := GetVideoIMDBInfoFromLocal(imdbInfo, proxySettings)
+	localIMDBInfo, err := GetVideoIMDBInfoFromLocal(imdbInfo, _proxySettings...)
 	if err != nil {
 		return false, nil, err
 	}

+ 1 - 3
internal/pkg/imdb_helper/imdb_test.go

@@ -1,7 +1,6 @@
 package imdb_helper
 
 import (
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types"
 	"testing"
 )
@@ -23,8 +22,7 @@ func TestGetVideoInfoFromIMDB(t *testing.T) {
 
 func TestIsChineseVideo(t *testing.T) {
 	type args struct {
-		imdbID    string
-		_reqParam []settings.ProxySettings
+		imdbID string
 	}
 	tests := []struct {
 		name    string

+ 160 - 0
internal/pkg/local_http_proxy_server/local_http_proxy_server.go

@@ -0,0 +1,160 @@
+package local_http_proxy_server
+
+import (
+	"encoding/base64"
+	"fmt"
+	"github.com/elazarl/goproxy"
+	"golang.org/x/net/context"
+	"golang.org/x/net/proxy"
+	"net"
+	"net/http"
+	"net/url"
+)
+
+// LocalHttpProxyServer see https://github.com/go-rod/rod/issues/305
+type LocalHttpProxyServer struct {
+	srv                      *http.Server
+	isRunning                bool
+	LocalHttpProxyServerPort string // 本地开启的 Http 代理服务器端口
+	LocalHttpProxyUrl        string // 本地开启的 Http 代理服务器地址包含端口
+}
+
+func NewLocalHttpProxyServer() *LocalHttpProxyServer {
+	return &LocalHttpProxyServer{}
+}
+
+func setBasicAuth(username, password string, req *http.Request) {
+	req.Header.Set(ProxyAuthHeader, fmt.Sprintf("Basic %s", basicAuth(username, password)))
+}
+
+func basicAuth(username, password string) string {
+	return base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
+}
+
+// Start 传入参数为 [0] protocol [1] Address [2] Port [3] Username [4] Password,如果没得账号密码,则后面两个可以不传入
+func (l *LocalHttpProxyServer) Start(settings []string, localHttpProxyServerPort string) (string, error) {
+
+	if len(settings) < 3 {
+		return "", nil
+	}
+
+	l.LocalHttpProxyServerPort = localHttpProxyServerPort
+
+	protocol := settings[0]
+	InputProxyAddress := settings[1]
+	InputProxyPort := settings[2]
+
+	InputProxyUsername := ""
+	InputProxyPassword := ""
+	if len(settings) >= 5 {
+		InputProxyUsername = settings[3]
+		InputProxyPassword = settings[4]
+	}
+
+	proxyAddress := InputProxyAddress + ":" + InputProxyPort
+
+	switch protocol {
+	case "http":
+		middleProxy := goproxy.NewProxyHttpServer()
+		middleProxy.Tr.Proxy = func(req *http.Request) (*url.URL, error) {
+			return url.Parse("http://" + proxyAddress)
+		}
+		//middleProxy.Logger = httpLogger()
+		connectReqHandler := func(req *http.Request) {
+			setBasicAuth(InputProxyUsername, InputProxyPassword, req)
+		}
+		middleProxy.ConnectDial = middleProxy.NewConnectDialToProxyWithHandler("http://"+proxyAddress, connectReqHandler)
+
+		middleProxy.OnRequest().Do(goproxy.FuncReqHandler(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
+			setBasicAuth(InputProxyUsername, InputProxyPassword, req)
+			return req, nil
+		}))
+
+		l.srv = &http.Server{Addr: ":" + l.LocalHttpProxyServerPort, Handler: middleProxy}
+
+		go func() {
+			if err := l.srv.ListenAndServe(); err != http.ErrServerClosed {
+				panic(fmt.Sprintln("ListenAndServe() http proxy:", err))
+			}
+		}()
+
+		l.isRunning = true
+		l.LocalHttpProxyUrl = "http://127.0.0.1:" + l.LocalHttpProxyServerPort
+
+		return l.LocalHttpProxyUrl, nil
+
+	case "socks5":
+
+		var dialer proxy.Dialer
+		var err error
+		if len(settings) >= 5 {
+			auth := proxy.Auth{
+				User:     InputProxyUsername,
+				Password: InputProxyPassword,
+			}
+			dialer, err = proxy.SOCKS5("tcp", proxyAddress, &auth, proxy.Direct)
+			if err != nil {
+				return "", err
+			}
+		} else {
+			dialer, err = proxy.SOCKS5("tcp", proxyAddress, nil, proxy.Direct)
+			if err != nil {
+				return "", err
+			}
+		}
+		dialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
+			return dialer.Dial(network, address)
+		}
+		dialContextOld := func(network, address string) (net.Conn, error) {
+			return dialer.Dial(network, address)
+		}
+		transport := &http.Transport{DialContext: dialContext, DisableKeepAlives: true}
+
+		middleProxy := goproxy.NewProxyHttpServer()
+		middleProxy.Tr = transport
+		//middleProxy.Logger = httpLogger()
+		middleProxy.ConnectDial = dialContextOld
+		middleProxy.OnRequest().Do(goproxy.FuncReqHandler(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
+			setBasicAuth(InputProxyUsername, InputProxyPassword, req)
+			return req, nil
+		}))
+
+		l.srv = &http.Server{Addr: ":" + l.LocalHttpProxyServerPort, Handler: middleProxy}
+
+		go func() {
+			if err := l.srv.ListenAndServe(); err != http.ErrServerClosed {
+				panic(fmt.Sprintln("ListenAndServe() socks5 proxy:", err))
+			}
+		}()
+
+		l.isRunning = true
+		l.LocalHttpProxyUrl = "http://127.0.0.1:" + l.LocalHttpProxyServerPort
+
+		return l.LocalHttpProxyUrl, nil
+	}
+	return "", fmt.Errorf("proxy type invalid, not http or socks5")
+}
+
+func (l *LocalHttpProxyServer) Stop() error {
+	if l.srv != nil {
+		err := l.srv.Close()
+		if err != nil {
+			return err
+		}
+	}
+
+	l.srv = nil
+	l.isRunning = false
+	l.LocalHttpProxyUrl = ""
+
+	return nil
+}
+
+func (l *LocalHttpProxyServer) IsRunning() bool {
+	return l.isRunning
+}
+
+const (
+	LocalHttpProxyPort = "19036"
+	ProxyAuthHeader    = "Proxy-Authorization"
+)

+ 8 - 28
internal/pkg/my_util/util.go

@@ -17,11 +17,8 @@ import (
 	"github.com/go-resty/resty/v2"
 	"github.com/google/uuid"
 	"github.com/sirupsen/logrus"
-	"golang.org/x/net/context"
-	"golang.org/x/net/proxy"
 	"io"
 	"math"
-	"net"
 	"net/http"
 	"net/url"
 	"os"
@@ -42,16 +39,10 @@ func NewHttpClient(_proxySettings ...*settings.ProxySettings) (*resty.Client, er
 	//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"
 
 	var proxySettings *settings.ProxySettings
-	var HttpProxyAddress, socket5ProxyAddress, UserAgent, Referer string
+	var UserAgent, Referer string
 
 	if len(_proxySettings) > 0 {
 		proxySettings = _proxySettings[0]
-		if proxySettings.UseHttpProxy == true && len(proxySettings.HttpProxyAddress) > 0 {
-			HttpProxyAddress = proxySettings.HttpProxyAddress
-		}
-		if proxySettings.UseSocks5Proxy == true && len(proxySettings.Socks5ProxyAddress) > 0 {
-			socket5ProxyAddress = proxySettings.Socks5ProxyAddress
-		}
 	}
 	// ------------------------------------------------
 	// 随机的 Browser
@@ -80,29 +71,18 @@ func NewHttpClient(_proxySettings ...*settings.ProxySettings) (*resty.Client, er
 	// 不要求安全链接
 	httpClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
 	// ------------------------------------------------
-	if len(_proxySettings) == 0 {
+	if len(_proxySettings) == 0 ||
+		(proxySettings != nil && proxySettings.UseProxy == false) {
 		// 无需设置代理
 		return httpClient, nil
 	}
 	// ------------------------------------------------
-	if proxySettings.UseSocks5OrHttpProxy == false {
-		// http 代理
-		if HttpProxyAddress != "" {
-			httpClient.SetProxy(HttpProxyAddress)
-		} else {
-			httpClient.RemoveProxy()
-		}
+	// http 代理
+	HttpProxyAddress := proxySettings.GetLocalHttpProxyUrl()
+	if HttpProxyAddress != "" {
+		httpClient.SetProxy(HttpProxyAddress)
 	} else {
-		// socket5 代理
-		dialer, err := proxy.SOCKS5("tcp", socket5ProxyAddress, nil, proxy.Direct)
-		if err != nil {
-			return nil, err
-		}
-		dialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
-			return dialer.Dial(network, address)
-		}
-		transport := &http.Transport{DialContext: dialContext, DisableKeepAlives: true}
-		httpClient.SetTransport(transport)
+		httpClient.RemoveProxy()
 	}
 
 	return httpClient, nil

+ 19 - 4
internal/pkg/my_util/util_test.go

@@ -1,6 +1,7 @@
 package my_util
 
 import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/local_http_proxy_server"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/unit_test_helper"
@@ -36,10 +37,24 @@ func TestGetPublicIP(t *testing.T) {
 	got := GetPublicIP(log_helper.GetLogger(), settings.NewTaskQueue())
 	println("NoProxy:", got)
 
-	got = GetPublicIP(log_helper.GetLogger(), settings.NewTaskQueue(),
-		settings.NewProxySettings(true, false, "",
-			true, "127.0.0.1:10808"))
-	println("UseProxy", got)
+	sock5ProxySettings := settings.NewProxySettings(true, "socks5", local_http_proxy_server.LocalHttpProxyPort,
+		"127.0.0.1", "10808", "", "")
+
+	got = GetPublicIP(log_helper.GetLogger(), settings.NewTaskQueue(), sock5ProxySettings)
+	println("UseProxy socks5:", got)
+	err := sock5ProxySettings.CloseLocalHttpProxyServer()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	httpProxySettings := settings.NewProxySettings(true, "http", local_http_proxy_server.LocalHttpProxyPort,
+		"127.0.0.1", "10809", "", "")
+	got = GetPublicIP(log_helper.GetLogger(), settings.NewTaskQueue(), httpProxySettings)
+	println("UseProxy http:", got)
+	err = httpProxySettings.CloseLocalHttpProxyServer()
+	if err != nil {
+		t.Fatal(err)
+	}
 }
 
 func TestSortByModTime(t *testing.T) {

+ 3 - 10
internal/pkg/rod_helper/rodHelper.go

@@ -22,18 +22,11 @@ import (
 
 func NewBrowserEx(loadAdblock bool, _settings *settings.Settings, preLoadUrl ...string) (*rod.Browser, error) {
 
-	httpProxyURL := ""
-
-	if _settings.AdvancedSettings.ProxySettings.UseHttpProxy == true &&
-		len(_settings.AdvancedSettings.ProxySettings.HttpProxyAddress) > 0 {
-
-		httpProxyURL = _settings.AdvancedSettings.ProxySettings.HttpProxyAddress
-	}
-
 	if _settings.ExperimentalFunction.RemoteChromeSettings.Enable == false {
-		return NewBrowser(httpProxyURL, loadAdblock, preLoadUrl...)
+		return NewBrowser(_settings.AdvancedSettings.ProxySettings.GetLocalHttpProxyUrl(), loadAdblock, preLoadUrl...)
 	} else {
-		return NewBrowserFromDocker(httpProxyURL, _settings.ExperimentalFunction.RemoteChromeSettings.RemoteDockerURL,
+		return NewBrowserFromDocker(_settings.AdvancedSettings.ProxySettings.GetLocalHttpProxyUrl(),
+			_settings.ExperimentalFunction.RemoteChromeSettings.RemoteDockerURL,
 			_settings.ExperimentalFunction.RemoteChromeSettings.RemoteAdblockPath,
 			_settings.ExperimentalFunction.RemoteChromeSettings.ReMoteUserDataDir,
 			loadAdblock, preLoadUrl...)

+ 2 - 1
internal/pkg/settings/advanced_settings.go

@@ -1,6 +1,7 @@
 package settings
 
 import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/local_http_proxy_server"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/common"
 )
 
@@ -22,7 +23,7 @@ type AdvancedSettings struct {
 
 func NewAdvancedSettings() *AdvancedSettings {
 	return &AdvancedSettings{
-		ProxySettings:     &ProxySettings{},
+		ProxySettings:     NewProxySettings(false, "http", local_http_proxy_server.LocalHttpProxyPort, "127.0.0.1", "10809", "", ""),
 		CustomVideoExts:   make([]string, 0),
 		Topic:             common.DownloadSubsPerSite,
 		SuppliersSettings: NewSuppliersSettings(),

+ 82 - 8
internal/pkg/settings/proxy_settings.go

@@ -1,14 +1,88 @@
 package settings
 
+import (
+	"fmt"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/local_http_proxy_server"
+	"sync"
+)
+
 type ProxySettings struct {
-	UseSocks5OrHttpProxy bool   `json:"use_socks5_or_http_proxy"` // 是使用 socks5(true) 还是 http(false) 代理,默认是 http
-	UseHttpProxy         bool   `json:"use_http_proxy"`           // 是否使用 http 代理
-	HttpProxyAddress     string `json:"http_proxy_address"`       // Http 代理地址,内网
-	UseSocks5Proxy       bool   `json:"use_socks5_proxy"`         // 是否使用 socks5 代理
-	Socks5ProxyAddress   string `json:"socks5_proxy_address"`     // Socks5 代理地址,内网
-	Referer              string `json:"-"`                        // 可能下载文件的时候需要设置
+	UseProxy                 bool   `json:"use_proxy"`                                    // 是否使用代理
+	UseWhichProxyProtocol    string `json:"use_which_proxy_protocol"`                     // 是使用 socks5 还是 http 代理
+	LocalHttpProxyServerPort string `json:"local_http_proxy_server_port" default:"19036"` // 本地代理服务器端口
+	InputProxyAddress        string `json:"input_proxy_address"`                          // 输入的代理地址
+	InputProxyPort           string `json:"input_proxy_port"`                             // 输入的代理端口
+	UserPWD                  bool   `json:"user_pwd"`                                     //是否使用用户名密码
+	InputProxyUsername       string `json:"input_proxy_username"`                         // 输入的代理用户名
+	InputProxyPassword       string `json:"input_proxy_password"`                         // 输入的代理密码
+	Referer                  string `json:"-"`                                            // 可能下载文件的时候需要设置
+
+	localHttpProxyServer *local_http_proxy_server.LocalHttpProxyServer // 本地代理服务器
+	locker               sync.Mutex
+}
+
+func NewProxySettings(useProxy bool, useWhichProxyProtocol string,
+	localHttpProxyServerPort string,
+	inputProxyAddress string, inputProxyPort string,
+	inputProxyUsername string, inputProxyPassword string) *ProxySettings {
+
+	set := ProxySettings{UseProxy: useProxy, UseWhichProxyProtocol: useWhichProxyProtocol,
+		LocalHttpProxyServerPort: localHttpProxyServerPort,
+		InputProxyAddress:        inputProxyAddress, InputProxyPort: inputProxyPort,
+		InputProxyUsername: inputProxyUsername, InputProxyPassword: inputProxyPassword}
+
+	if inputProxyUsername != "" && inputProxyPassword != "" {
+		set.UserPWD = true
+	}
+
+	return &set
+}
+
+func (p *ProxySettings) GetLocalHttpProxyUrl() string {
+	defer p.locker.Unlock()
+	p.locker.Lock()
+
+	if p.UseProxy == false {
+		return ""
+	}
+
+	if p.localHttpProxyServer == nil {
+		p.localHttpProxyServer = local_http_proxy_server.NewLocalHttpProxyServer()
+	}
+
+	if p.localHttpProxyServer.IsRunning() == true {
+		return p.localHttpProxyServer.LocalHttpProxyUrl
+	}
+
+	inputInfo := []string{
+		p.UseWhichProxyProtocol,
+		p.InputProxyAddress,
+		p.InputProxyPort,
+	}
+	if p.InputProxyUsername != "" && p.InputProxyPassword != "" {
+		inputInfo = append(inputInfo, p.InputProxyUsername, p.InputProxyPassword)
+	}
+
+	localHttpProxyUrl, err := p.localHttpProxyServer.Start(inputInfo, p.LocalHttpProxyServerPort)
+	if err != nil {
+		panic(fmt.Sprintln("start local http proxy server error:", err))
+		return ""
+	}
+
+	return localHttpProxyUrl
 }
 
-func NewProxySettings(useSocks5OrHttpProxy bool, useHttpProxy bool, httpProxyAddress string, useSocks5Proxy bool, socks5ProxyAddress string) *ProxySettings {
-	return &ProxySettings{UseSocks5OrHttpProxy: useSocks5OrHttpProxy, UseHttpProxy: useHttpProxy, HttpProxyAddress: httpProxyAddress, UseSocks5Proxy: useSocks5Proxy, Socks5ProxyAddress: socks5ProxyAddress}
+func (p *ProxySettings) CloseLocalHttpProxyServer() error {
+	defer p.locker.Unlock()
+	p.locker.Lock()
+
+	if p.localHttpProxyServer == nil {
+		return nil
+	}
+
+	if p.localHttpProxyServer.IsRunning() == false {
+		return nil
+	}
+
+	return p.localHttpProxyServer.Stop()
 }

+ 32 - 7
internal/pkg/video_scan_and_refresh_helper/video_scan_and_refresh_helper.go

@@ -10,12 +10,12 @@ import (
 	subSupplier "github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/sub_supplier/xunlei"
 	"github.com/allanpk716/ChineseSubFinder/internal/logic/task_queue"
-	"github.com/allanpk716/ChineseSubFinder/internal/models"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/decode"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/imdb_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	subTimelineFixerPKG "github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_timeline_fixer"
+	"github.com/allanpk716/ChineseSubFinder/internal/types"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/common"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/emby"
 	TTaskqueue "github.com/allanpk716/ChineseSubFinder/internal/types/task_queue"
@@ -159,13 +159,38 @@ func (v *VideoScanAndRefreshHelper) UpdateLocalVideoCacheInfo(scanVideoResult *S
 			continue
 		}
 		// 插入数据
-		localInfo := models.NewMovieOrSeriesLocalInfo(true, 0, filepath.Dir(oneMovieFPath))
-		err = dao.GetDb().Model(localIMDBInfo).Association("MovieOrSeriesLocalInfos").Append(localInfo)
-		if err != nil {
-			v.log.Errorln("UpdateLocalVideoCacheInfo", oneMovieFPath, err)
-			continue
-		}
+		localIMDBInfo.RootDirPath = filepath.Dir(oneMovieFPath)
+		localIMDBInfo.IsMovie = true
+		dao.GetDb().Save(localIMDBInfo)
 	}
+	// 连续剧
+	scanVideoResult.Normal.SeriesDirMap.Each(func(seriesRootPathName interface{}, seriesNames interface{}) {
+
+		for _, oneSeriesRootDir := range seriesNames.([]string) {
+
+			seriesInfo, err := seriesHelper.ReadSeriesInfoFromDir(oneSeriesRootDir,
+				v.settings.AdvancedSettings.TaskQueue.ExpirationTime,
+				false,
+				v.settings.AdvancedSettings.ProxySettings)
+
+			if err != nil {
+				v.log.Warningln("ReadSeriesInfoFromDir", oneSeriesRootDir, err)
+				continue
+			}
+			// 获取 IMDB 信息
+			localIMDBInfo, err := imdb_helper.GetVideoIMDBInfoFromLocal(
+				types.VideoIMDBInfo{ImdbId: seriesInfo.ImdbId},
+				v.settings.AdvancedSettings.ProxySettings)
+			if err != nil {
+				v.log.Warningln("GetVideoIMDBInfoFromLocal,IMDB:", seriesInfo.ImdbId, seriesInfo.DirPath, err)
+				continue
+			}
+			// 插入数据
+			localIMDBInfo.RootDirPath = filepath.Dir(seriesInfo.DirPath)
+			localIMDBInfo.IsMovie = false
+			dao.GetDb().Save(localIMDBInfo)
+		}
+	})
 
 	return nil
 }

+ 3 - 1
internal/types/backend/req_check_proxy.go

@@ -1,5 +1,7 @@
 package backend
 
+import "github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
+
 type ReqCheckProxy struct {
-	HttpProxyAddress string `json:"http_proxy_address"  binding:"required"`
+	ProxySettings settings.ProxySettings `json:"proxy_settings"  binding:"required"`
 }