瀏覽代碼

保存进度

Signed-off-by: 716 <[email protected]>
716 3 年之前
父節點
當前提交
75ec8de1e8
共有 44 個文件被更改,包括 563 次插入842 次删除
  1. 3 1
      .gitignore
  2. 41 0
      TestCode/test_viper.go
  3. 2 25
      cmd/chinesesubfinder/main.go
  4. 4 3
      go.mod
  5. 10 0
      go.sum
  6. 10 19
      internal/backend/controllers/base/change_pwd.go
  7. 22 0
      internal/backend/controllers/base/controller_base.go
  8. 14 8
      internal/backend/controllers/base/login.go
  9. 2 2
      internal/backend/controllers/base/logout.go
  10. 8 14
      internal/backend/controllers/base/setup.go
  11. 7 6
      internal/backend/controllers/base/system_status.go
  12. 2 2
      internal/backend/controllers/v1/check_sub_supplier.go
  13. 41 1
      internal/backend/controllers/v1/settings.go
  14. 9 10
      internal/backend/routers/base_router.go
  15. 4 26
      internal/dao/init.go
  16. 0 1
      internal/dao/settings.go
  17. 0 23
      internal/dao/user_info.go
  18. 2 16
      internal/logic/forced_scan_and_down_sub/forced_scan_and_down_sub.go
  19. 55 2
      internal/logic/pre_download_process/pre_download_proces.go
  20. 3 23
      internal/logic/restore_fix_timeline_bk/restore_fix_timeline_bk.go
  21. 0 11
      internal/models/settings/advanced_settings.go
  22. 0 11
      internal/models/settings/common_settings.go
  23. 0 5
      internal/models/settings/developer_settings.go
  24. 0 11
      internal/models/settings/emby_settings.go
  25. 0 11
      internal/models/settings/settings.go
  26. 0 92
      internal/pkg/config/config.go
  27. 0 11
      internal/pkg/global_value/globalvalue.go
  28. 29 0
      internal/pkg/my_util/folder.go
  29. 3 0
      internal/pkg/my_util/util.go
  30. 17 0
      internal/pkg/settings/advanced_settings.go
  31. 20 0
      internal/pkg/settings/common_settings.go
  32. 9 0
      internal/pkg/settings/developer_settings.go
  33. 19 0
      internal/pkg/settings/emby_settings.go
  34. 85 0
      internal/pkg/settings/settings.go
  35. 8 4
      internal/pkg/settings/user_info.go
  36. 0 7
      internal/pkg/sqlite/errors.go
  37. 0 285
      internal/pkg/sqlite/migrator.go
  38. 0 180
      internal/pkg/sqlite/mysqlite.go
  39. 54 0
      internal/pkg/strcut_json/struct_json.go
  40. 64 0
      internal/pkg/strcut_json/struct_json_test.go
  41. 5 2
      internal/types/backend/reply_login.go
  42. 7 0
      internal/types/backend/req_settings.go
  43. 4 2
      internal/types/backend/req_setup_info.go
  44. 0 28
      internal/types/config.go

+ 3 - 1
.gitignore

@@ -60,7 +60,9 @@ TestData/
 /cmd/GetCAPTCHA
 /.idea
 /internal/backend/Logs
-/internal/backend
 /internal/pkg/task_control/Logs/*.log
 /internal/pkg/task_control/opendebuglog
 /internal/pkg/downloader/Logs/*.log
+/internal/dao/Logs/*.log
+/internal/dao/*.db
+/internal/pkg/strcut_json/*.json

+ 41 - 0
TestCode/test_viper.go

@@ -0,0 +1,41 @@
+package TestCode
+
+type CommonSettings struct {
+	UseHttpProxy     bool     `json:"use_http_proxy"`       // 是否使用 http 代理
+	HttpProxyAddress string   `json:"http_proxy_address"`   // Http 代理地址,内网
+	ScanInterval     string   `json:"scan_interval""`       // 一轮字幕扫描的间隔
+	Threads          int      `json:"threads"`              // 同时扫描的并发数
+	RunScanAtStartUp bool     `json:"run_scan_at_start_up"` // 完成引导设置后,下次运行程序就开始扫描
+	MoviePaths       []string `json:"movie_paths"`          // 电影的目录
+	SeriesPaths      []string `json:"series_paths"`         // 连续剧的目录
+}
+
+//// initConfigure 初始化配置文件实例
+//func initConfigure() (*viper.Viper, error) {
+//
+//	v := viper.New()
+//	v.SetConfigName("ChineseSubFinderConfig") // 设置文件名称(无后缀)
+//	v.SetConfigType("yaml")                   // 设置后缀名 {"1.6以后的版本可以不设置该后缀"}
+//	v.AddConfigPath(".")                      // 设置文件所在路径
+//
+//	err := v.ReadInConfig()
+//	if err != nil {
+//		return nil, errors.New("error reading config:" + err.Error())
+//	}
+//
+//	return v, nil
+//}
+//
+//func writeConfig(viper *viper.Viper, settings *CommonSettings) error {
+//	viper.WriteConfig()
+//}
+//
+//// readConfig 读取配置文件
+//func readConfig(viper *viper.Viper) (*CommonSettings, error) {
+//	conf := &CommonSettings{}
+//	err := viper.Unmarshal(conf)
+//	if err != nil {
+//		return nil, err
+//	}
+//	return conf, nil
+//}

+ 2 - 25
cmd/chinesesubfinder/main.go

@@ -12,35 +12,17 @@ import (
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sub_formatter/common"
 	"github.com/allanpk716/ChineseSubFinder/internal/types"
+	"github.com/prometheus/common/log"
 	"github.com/robfig/cron/v3"
 	"github.com/sirupsen/logrus"
 )
 
 func init() {
 
-	config = config2.GetConfig()
-	// 根据配置文件的情况去生成是否需要开启日志 Debug 记录级别的特殊文件
-	if config.DebugMode == true {
-		err := log_helper.WriteDebugFile()
-		if err != nil {
-			panic("log_helper.WriteDebugFile " + err.Error())
-		}
-	} else {
-		err := log_helper.DeleteDebugFile()
-		if err != nil {
-			panic("log_helper.DeleteDebugFile " + err.Error())
-		}
-	}
-
-	log = log_helper.GetLogger()
-
 	log.Infoln("ChineseSubFinder Version:", AppVersion)
 
 	if my_util.OSCheck() == false {
-		panic(`only support Linux and Windows, if you want support MacOS, 
-you need implement getDbName() in file: internal/dao/init.go 
-and 
-implement getSpeFileName() in internal/logic/forced_scan_and_down_sub/forced_scan_and_down_sub.go`)
+		panic(`You should search runtime.GOOS in the project, Implement unimplemented function`)
 	}
 }
 
@@ -157,11 +139,6 @@ func DownLoadStart(httpProxy string) {
 
 }
 
-var (
-	log    *logrus.Logger
-	config *types.Config
-)
-
 /*
 	没有很好的想法,因为喜欢使用 tag 进行版本的输出标记,但是 tag 的时候编译 docker 前确实可以修改源码替换关键词做到版本与 tag 同步变更
 	但是, goreleaser 却不支持这样,会提示源码被改了,无法进行编译发布

+ 4 - 3
go.mod

@@ -27,7 +27,7 @@ require (
 	github.com/huandu/go-clone v1.3.0
 	github.com/james-bowman/nlp v0.0.0-20210511120306-26d441fa0ded
 	github.com/james-bowman/sparse v0.0.0-20210729090128-1e6c7dd483e9 // indirect
-	github.com/jinzhu/now v1.1.2
+	github.com/jinzhu/now v1.1.4
 	github.com/jonboulle/clockwork v0.2.2 // indirect
 	github.com/klauspost/compress v1.12.2 // indirect
 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
@@ -62,8 +62,7 @@ require (
 	gopkg.in/errgo.v2 v2.1.0
 	gopkg.in/ini.v1 v1.62.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
-	gorm.io/gorm v1.21.12
-	modernc.org/sqlite v1.12.0
+	gorm.io/gorm v1.22.5
 )
 
 require (
@@ -98,6 +97,7 @@ require (
 	github.com/klauspost/pgzip v1.2.4 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
+	github.com/mattn/go-sqlite3 v1.14.10 // indirect
 	github.com/mitchellh/go-homedir v1.1.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -122,6 +122,7 @@ require (
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/protobuf v1.27.1 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
+	gorm.io/driver/sqlite v1.2.6 // indirect
 	lukechampine.com/uint128 v1.1.1 // indirect
 	modernc.org/cc/v3 v3.33.7 // indirect
 	modernc.org/ccgo/v3 v3.9.6 // indirect

+ 10 - 0
go.sum

@@ -312,6 +312,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
 github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
+github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
@@ -381,6 +383,9 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
 github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
+github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
 github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
@@ -921,8 +926,13 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
+gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
 gorm.io/gorm v1.21.12 h1:3fQM0Eiz7jcJEhPggHEpoYnsGZqynMzverL77DV40RM=
 gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
+gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
+gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU=
+gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 10 - 19
internal/backend/controllers/v1/change_pwd.go → internal/backend/controllers/base/change_pwd.go

@@ -1,10 +1,8 @@
-package v1
+package base
 
 import (
-	"errors"
-	"fmt"
 	"github.com/allanpk716/ChineseSubFinder/internal/backend/common"
-	"github.com/allanpk716/ChineseSubFinder/internal/dao"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/gin-gonic/gin"
 	"net/http"
@@ -24,31 +22,24 @@ func (cb ControllerBase) ChangePwdHandler(c *gin.Context) {
 		return
 	}
 
-	found, dbUserInfo, err := dao.GetUserInfo()
-	if err != nil {
+	if settings.GetSettings().UserInfo.Username == "" || settings.GetSettings().UserInfo.Password == "" {
+		// 配置文件中的账号和密码任意一个未空,提示用户需要进行 setup 流程
+		c.JSON(http.StatusNoContent, backend.ReplyCommon{Message: "You need do `Setup`"})
 		return
 	}
 
-	if found == false {
-		// 找不到用户
-		c.JSON(http.StatusInternalServerError, backend.ReplyCommon{Message: "Can't Found UserInfo"})
-	} else if dbUserInfo.Password != changePwd.OrgPwd {
+	if settings.GetSettings().UserInfo.Password != changePwd.OrgPwd {
 		// 原始的密码不对
 		c.JSON(http.StatusNoContent, backend.ReplyCommon{Message: "Org Password Error"})
 	} else {
 		// 同意修改密码
-		dbUserInfo.Password = changePwd.NewPwd
-		re := dao.GetDb().Updates(dbUserInfo)
-		if re == nil {
-			err = errors.New(fmt.Sprintf("dao.GetDb().Updates return nil"))
-			return
-		}
-		if re.Error != nil {
-			err = re.Error
+		settings.GetSettings().UserInfo.Password = changePwd.NewPwd
+		err = settings.GetSettings().Save()
+		if err != nil {
 			return
 		}
 		// 修改密码成功后,会清理 AccessToken,强制要求重写登录
 		common.SetAccessToken("")
-		c.JSON(http.StatusOK, backend.ReplyCommon{Message: "ok"})
+		c.JSON(http.StatusOK, backend.ReplyCommon{Message: "ok, need ReLogin"})
 	}
 }

+ 22 - 0
internal/backend/controllers/base/controller_base.go

@@ -0,0 +1,22 @@
+package base
+
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
+	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+type ControllerBase struct {
+}
+
+func NewControllerBase() *ControllerBase {
+	return &ControllerBase{}
+}
+
+func (cb *ControllerBase) ErrorProcess(c *gin.Context, funcName string, err error) {
+	if err != nil {
+		log_helper.GetLogger().Errorln(funcName, err.Error())
+		c.JSON(http.StatusInternalServerError, backend.ReplyCommon{Message: err.Error()})
+	}
+}

+ 14 - 8
internal/backend/controllers/v1/login.go → internal/backend/controllers/base/login.go

@@ -1,10 +1,9 @@
-package v1
+package base
 
 import (
 	"github.com/allanpk716/ChineseSubFinder/internal/backend/common"
-	"github.com/allanpk716/ChineseSubFinder/internal/dao"
-	"github.com/allanpk716/ChineseSubFinder/internal/models"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/gin-gonic/gin"
 	"net/http"
@@ -17,22 +16,29 @@ func (cb ControllerBase) LoginHandler(c *gin.Context) {
 		// 统一的异常处理
 		cb.ErrorProcess(c, "LoginHandler", err)
 	}()
-	nowUserInfo := models.UserInfo{}
+	nowUserInfo := settings.UserInfo{}
 	err = c.ShouldBindJSON(&nowUserInfo)
 	if err != nil {
 		return
 	}
 
-	found, dbUserInfo, err := dao.GetUserInfo()
-	if err != nil {
+	if settings.GetSettings().UserInfo.Username == "" || settings.GetSettings().UserInfo.Password == "" {
+		// 配置文件中的账号和密码任意一个未空,提示用户需要进行 setup 流程
+		c.JSON(http.StatusNoContent, backend.ReplyCommon{Message: "You need do `Setup`"})
 		return
 	}
 
-	if found == false || dbUserInfo.Username != nowUserInfo.Username || dbUserInfo.Password != nowUserInfo.Password {
+	if settings.GetSettings().UserInfo.Username != nowUserInfo.Username ||
+		settings.GetSettings().UserInfo.Password != nowUserInfo.Password {
+		// 账号密码不匹配
 		c.JSON(http.StatusNoContent, backend.ReplyCommon{Message: "Username or Password Error"})
+		return
 	} else {
+		// 用户账号密码匹配
 		nowAccessToken := my_util.GenerateAccessToken()
 		common.SetAccessToken(nowAccessToken)
-		c.JSON(http.StatusOK, backend.ReplyLogin{AccessToken: nowAccessToken})
+		c.JSON(http.StatusOK, backend.ReplyLogin{AccessToken: nowAccessToken,
+			Settings: settings.GetSettings().GetNoPasswordSettings()})
+		return
 	}
 }

+ 2 - 2
internal/backend/controllers/v1/logout.go → internal/backend/controllers/base/logout.go

@@ -1,4 +1,4 @@
-package v1
+package base
 
 import (
 	"github.com/allanpk716/ChineseSubFinder/internal/backend/common"
@@ -11,5 +11,5 @@ func (cb ControllerBase) LogoutHandler(c *gin.Context) {
 
 	// 注销
 	common.SetAccessToken("")
-	c.JSON(http.StatusOK, backend.ReplyCommon{Message: "ok"})
+	c.JSON(http.StatusOK, backend.ReplyCommon{Message: "ok, need ReLogin"})
 }

+ 8 - 14
internal/backend/controllers/v1/setup.go → internal/backend/controllers/base/setup.go

@@ -1,9 +1,7 @@
-package v1
+package base
 
 import (
-	"errors"
-	"fmt"
-	"github.com/allanpk716/ChineseSubFinder/internal/dao"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/gin-gonic/gin"
 	"net/http"
@@ -22,23 +20,19 @@ func (cb ControllerBase) SetupHandler(c *gin.Context) {
 		return
 	}
 	// 只有当用户不存在的时候才能够执行初始化操作
-	found, _, err := dao.GetUserInfo()
-	if err != nil {
-		return
+	found := false
+	if settings.GetSettings().UserInfo.Username != "" && settings.GetSettings().UserInfo.Password != "" {
+		found = true
 	}
+
 	if found == true {
 		// 存在则反馈无需初始化
 		c.JSON(http.StatusNoContent, backend.ReplyCommon{Message: "already setup"})
 		return
 	} else {
 		// 需要创建用户,因为上述判断了没有用户存在,所以就默认直接新建了
-		re := dao.GetDb().Create(&setupInfo.UserInfo)
-		if re == nil {
-			err = errors.New(fmt.Sprintf("dao.GetDb().Create return nil"))
-			return
-		}
-		if re.Error != nil {
-			err = re.Error
+		err = settings.SetFullNewSettings(&setupInfo.Settings)
+		if err != nil {
 			return
 		}
 		c.JSON(http.StatusOK, backend.ReplyCommon{Message: "ok"})

+ 7 - 6
internal/backend/controllers/v1/system_status.go → internal/backend/controllers/base/system_status.go

@@ -1,7 +1,7 @@
-package v1
+package base
 
 import (
-	"github.com/allanpk716/ChineseSubFinder/internal/dao"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/gin-gonic/gin"
 	"net/http"
@@ -16,12 +16,13 @@ func (cb ControllerBase) SystemStatusHandler(c *gin.Context) {
 		cb.ErrorProcess(c, "SystemStatusHandler", err)
 	}()
 
-	found, _, err := dao.GetUserInfo()
-	if err != nil {
-		return
+	isSetup := false
+	if settings.GetSettings().UserInfo.Username != "" && settings.GetSettings().UserInfo.Password != "" {
+		// 进行过 setup 了,那么就可以 Login 的流程
+		isSetup = true
 	}
 
 	c.JSON(http.StatusOK, backend.ReplySystemStatus{
-		IsSetup: found,
+		IsSetup: isSetup,
 	})
 }

+ 2 - 2
internal/backend/controllers/v1/check_sub_supplier.go

@@ -3,8 +3,8 @@ package v1
 import (
 	"github.com/allanpk716/ChineseSubFinder/internal/backend/common"
 	"github.com/allanpk716/ChineseSubFinder/internal/dao"
-	"github.com/allanpk716/ChineseSubFinder/internal/models"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
 	"github.com/gin-gonic/gin"
 	"net/http"
@@ -17,7 +17,7 @@ func (cb ControllerBase) CheckSubSupplierHandler(c *gin.Context) {
 		// 统一的异常处理
 		cb.ErrorProcess(c, "CheckSubSupplierHandler", err)
 	}()
-	nowUserInfo := models.UserInfo{}
+	nowUserInfo := settings.UserInfo{}
 	err = c.ShouldBindJSON(&nowUserInfo)
 	if err != nil {
 		return

+ 41 - 1
internal/backend/controllers/v1/settings.go

@@ -1,6 +1,11 @@
 package v1
 
-import "github.com/gin-gonic/gin"
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
+	"github.com/allanpk716/ChineseSubFinder/internal/types/backend"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
 
 func (cb ControllerBase) SettingsHandler(c *gin.Context) {
 	var err error
@@ -8,4 +13,39 @@ func (cb ControllerBase) SettingsHandler(c *gin.Context) {
 		// 统一的异常处理
 		cb.ErrorProcess(c, "SettingsHandler", err)
 	}()
+
+	switch c.Request.Method {
+	case "GET":
+		{
+			// 回复没有密码的 settings
+			c.JSON(http.StatusOK, settings.GetSettings().GetNoPasswordSettings())
+		}
+	case "PATH":
+		{
+			// 修改设置,这里不允许修改密码
+			reqSetupInfo := backend.ReqSettings{}
+			err = c.ShouldBindJSON(&reqSetupInfo)
+			if err != nil {
+				return
+			}
+			// 需要去除 user 的 password 信息再保存,也就是继承之前的 password 即可
+			err = settings.GetSettings().Read()
+			if err != nil {
+				return
+			}
+			nowPassword := settings.GetSettings().UserInfo.Password
+			reqSetupInfo.Settings.UserInfo.Password = nowPassword
+			err = settings.SetFullNewSettings(&reqSetupInfo.Settings)
+			if err != nil {
+				return
+			}
+			err = settings.GetSettings().Save()
+			if err != nil {
+				return
+			}
+			c.JSON(http.StatusOK, backend.ReplyCommon{Message: "Settings Save Success`"})
+		}
+	default:
+		c.JSON(http.StatusNoContent, backend.ReplyCommon{Message: "Settings Request.Method Error`"})
+	}
 }

+ 9 - 10
internal/backend/routers/base_router.go

@@ -1,29 +1,28 @@
 package routers
 
 import (
+	"github.com/allanpk716/ChineseSubFinder/internal/backend/controllers/base"
 	v1 "github.com/allanpk716/ChineseSubFinder/internal/backend/controllers/v1"
 	"github.com/allanpk716/ChineseSubFinder/internal/backend/middle"
 	"github.com/gin-gonic/gin"
 )
 
 func InitRouter(router *gin.Engine) {
-
+	cbBase := base.NewControllerBase()
 	cbV1 := v1.NewControllerBase()
+	// 基础的路由
+	router.GET("/system-status", cbBase.SystemStatusHandler)
 
-	router.GET("/system-status", cbV1.SystemStatusHandler)
-
-	router.POST("/setup", cbV1.SetupHandler)
-
-	router.POST("/login", cbV1.LoginHandler)
+	router.POST("/setup", cbBase.SetupHandler)
 
+	router.POST("/login", cbBase.LoginHandler)
+	router.POST("/logout", middle.CheckAuth(), cbBase.LogoutHandler)
+	router.POST("/change-pwd", middle.CheckAuth(), cbBase.ChangePwdHandler)
+	// v1路由: /v1/xxx
 	GroupV1 := router.Group("/" + cbV1.GetVersion())
 	{
 		GroupV1.Use(middle.CheckAuth())
 
-		GroupV1.POST("/logout", cbV1.LogoutHandler)
-
-		GroupV1.POST("/change-pwd", cbV1.ChangePwdHandler)
-
 		GroupV1.GET("/settings", cbV1.SettingsHandler)
 		GroupV1.PATCH("/settings", cbV1.SettingsHandler)
 

+ 4 - 26
internal/dao/init.go

@@ -4,14 +4,12 @@ import (
 	"errors"
 	"fmt"
 	"github.com/allanpk716/ChineseSubFinder/internal/models"
-	"github.com/allanpk716/ChineseSubFinder/internal/models/settings"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/sqlite"
+	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
 	"os"
 	"path/filepath"
-	"runtime"
 	"sync"
 )
 
@@ -58,10 +56,6 @@ func InitDb() error {
 	var err error
 	// 新建数据库
 	nowDbFileName := getDbName()
-	if nowDbFileName == "" {
-		return errors.New(fmt.Sprintf(`InitDb().getDbName() is empty, not support this OS.
-you need implement getDbName() in file: internal/dao/init.go `))
-	}
 
 	dbDir := filepath.Dir(nowDbFileName)
 	if my_util.IsDir(dbDir) == false {
@@ -72,9 +66,7 @@ you need implement getDbName() in file: internal/dao/init.go `))
 		return errors.New(fmt.Sprintf("failed to connect database, %s", err.Error()))
 	}
 	// 迁移 schema
-	err = db.AutoMigrate(&models.HotFix{}, &models.SubFormatRec{},
-		&models.UserInfo{},
-		&settings.Settings{})
+	err = db.AutoMigrate(&models.HotFix{}, &models.SubFormatRec{})
 	if err != nil {
 		return errors.New(fmt.Sprintf("db AutoMigrate error, %s", err.Error()))
 	}
@@ -83,19 +75,7 @@ you need implement getDbName() in file: internal/dao/init.go `))
 }
 
 func getDbName() string {
-	nowDbFileName := ""
-	sysType := runtime.GOOS
-	if sysType == "linux" {
-		nowDbFileName = dbFileNameLinux
-	}
-	if sysType == "windows" {
-		nowDbFileName = dbFileNameWindows
-	}
-	if sysType == "darwin" {
-		home, _ := os.UserHomeDir()
-		nowDbFileName = home + "/.config/chinesesubfinder/" + dbFileNameDarwin
-	}
-	return nowDbFileName
+	return my_util.GetConfigRootDirFPath() + dbFileName
 }
 
 var (
@@ -104,7 +84,5 @@ var (
 )
 
 const (
-	dbFileNameLinux   = "/config/settings.db"
-	dbFileNameWindows = "settings.db"
-	dbFileNameDarwin  = "settings.db"
+	dbFileName = "settings.db"
 )

+ 0 - 1
internal/dao/settings.go

@@ -1 +0,0 @@
-package dao

+ 0 - 23
internal/dao/user_info.go

@@ -1,23 +0,0 @@
-package dao
-
-import (
-	"github.com/allanpk716/ChineseSubFinder/internal/models"
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
-)
-
-func GetUserInfo() (bool, models.UserInfo, error) {
-	var userInfos []models.UserInfo
-	results := GetDb().Find(&userInfos)
-	if results == nil || len(userInfos) == 0 {
-		log_helper.GetLogger().Infoln("Need Setup For First Time Use.")
-		return false, models.UserInfo{}, nil
-	}
-	if results.Error != nil {
-		return false, models.UserInfo{}, results.Error
-	}
-	if len(userInfos) > 1 {
-		log_helper.GetLogger().Warningln("Found UserInfo len > 2 ")
-	}
-	// 导出第一个用户的信息
-	return true, userInfos[0], nil
-}

+ 2 - 16
internal/logic/forced_scan_and_down_sub/forced_scan_and_down_sub.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
 	"os"
-	"runtime"
 )
 
 // CheckSpeFile 目标是检测特定的文件,找到后,先删除,返回一个标志位用于后面的逻辑
@@ -29,19 +28,8 @@ you needd implement getSpeFileName() in internal/logic/forced_scan_and_down_sub/
 }
 
 func getSpeFileName() string {
-	nowSpeFileName := ""
-	sysType := runtime.GOOS
-	if sysType == "linux" {
-		nowSpeFileName = specialFileNameLinux
-	}
-	if sysType == "windows" {
-		nowSpeFileName = specialFileNameWindows
-	}
-	if sysType == "darwin" {
-		home, _ := os.UserHomeDir()
-		nowSpeFileName = home + "/.config/chinesesubfinder/" + specialFileNameDarwin
-	}
-	return nowSpeFileName
+
+	return my_util.GetConfigRootDirFPath() + specialFileNameWindows
 }
 
 /*
@@ -52,6 +40,4 @@ func getSpeFileName() string {
 */
 const (
 	specialFileNameWindows = "ForceFullScanAndDownloadSub"
-	specialFileNameLinux   = "/config/" + specialFileNameWindows
-	specialFileNameDarwin = "ForceFullScanAndDownloadSub"
 )

+ 55 - 2
internal/logic/pre_download_process/pre_download_proces.go

@@ -1,6 +1,12 @@
 package pre_download_process
 
-import "errors"
+import (
+	"errors"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/proxy_helper"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
+)
 
 type PreDownloadProcess struct {
 	stageName string
@@ -17,7 +23,54 @@ func (p *PreDownloadProcess) Init() *PreDownloadProcess {
 		return p
 	}
 	p.stageName = "Init"
-	// do something
+	// 如果是 Debug 模式,那么就需要写入特殊文件
+	if settings.GetSettings().AdvancedSettings.DebugMode == true {
+		err := log_helper.WriteDebugFile()
+		if err != nil {
+			p.gError = errors.New("log_helper.WriteDebugFile " + err.Error())
+			return p
+		}
+	} else {
+		err := log_helper.DeleteDebugFile()
+		if err != nil {
+			p.gError = errors.New("log_helper.DeleteDebugFile " + err.Error())
+			return p
+		}
+	}
+	// 测试代理
+	if settings.GetSettings().CommonSettings.UseHttpProxy == false {
+		log_helper.GetLogger().Infoln("UseHttpProxy = false")
+	} else {
+		log_helper.GetLogger().Infoln("UseHttpProxy:", settings.GetSettings().CommonSettings.HttpProxyAddress)
+		proxySpeed, proxyStatus, err := proxy_helper.ProxyTest(settings.GetSettings().CommonSettings.HttpProxyAddress)
+		if err != nil {
+			p.gError = errors.New("ProxyTest Target Site http://google.com " + err.Error())
+			return p
+		} else {
+			log_helper.GetLogger().Infoln("ProxyTest Target Site http://google.com", "Speed:", proxySpeed, "Status:", proxyStatus)
+		}
+	}
+	// 判断文件夹是否存在
+	if len(settings.GetSettings().CommonSettings.MoviePaths) < 1 {
+		log_helper.GetLogger().Infoln("MoviePaths not set, len == 0")
+	}
+	if len(settings.GetSettings().CommonSettings.SeriesPaths) < 1 {
+		log_helper.GetLogger().Infoln("SeriesPaths not set, len == 0")
+	}
+	for i, path := range settings.GetSettings().CommonSettings.MoviePaths {
+		if my_util.IsDir(path) == false {
+			log_helper.GetLogger().Errorln("MovieFolder not found Index", i, "--", path)
+		} else {
+			log_helper.GetLogger().Infoln("MovieFolder Index", i, "--", path)
+		}
+	}
+	for i, path := range settings.GetSettings().CommonSettings.SeriesPaths {
+		if my_util.IsDir(path) == false {
+			log_helper.GetLogger().Errorln("SeriesPaths not found Index", i, "--", path)
+		} else {
+			log_helper.GetLogger().Infoln("SeriesPaths Index", i, "--", path)
+		}
+	}
 
 	return p
 }

+ 3 - 23
internal/logic/restore_fix_timeline_bk/restore_fix_timeline_bk.go

@@ -1,21 +1,15 @@
 package restore_fix_timeline_bk
 
 import (
-	"errors"
-	"fmt"
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
 	"os"
-	"runtime"
 )
 
 // CheckSpeFile 目标是检测特定的文件,找到后,先删除,返回一个标志位用于后面的逻辑
 func CheckSpeFile() (bool, error) {
 
 	nowSpeFileName := getSpeFileName()
-	if nowSpeFileName == "" {
-		return false, errors.New(fmt.Sprintf(`restore_fix_timeline_bk.getSpeFileName() is empty, not support this OS. 
-you needd implement getSpeFileName() in internal/logic/restore_fix_timeline_bk/restore_fix_timeline_bk.go`))
-	}
+
 	if my_util.IsFile(nowSpeFileName) == false {
 		return false, nil
 	}
@@ -29,19 +23,7 @@ you needd implement getSpeFileName() in internal/logic/restore_fix_timeline_bk/r
 }
 
 func getSpeFileName() string {
-	nowSpeFileName := ""
-	sysType := runtime.GOOS
-	if sysType == "linux" {
-		nowSpeFileName = specialFileNameLinux
-	}
-	if sysType == "windows" {
-		nowSpeFileName = specialFileNameWindows
-	}
-	if sysType == "darwin" {
-		home, _ := os.UserHomeDir()
-		nowSpeFileName = home + "/.config/chinesesubfinder/" + specialFileNameDarwin
-	}
-	return nowSpeFileName
+	return my_util.GetConfigRootDirFPath() + specialFileName
 }
 
 /*
@@ -51,7 +33,5 @@ func getSpeFileName() string {
 	对于 MacOS 需要自行实现
 */
 const (
-	specialFileNameWindows = "RestoreFixTimelineBK"
-	specialFileNameLinux   = "/config/" + specialFileNameWindows
-	specialFileNameDarwin  = "RestoreFixTimelineBK"
+	specialFileName = "RestoreFixTimelineBK"
 )

+ 0 - 11
internal/models/settings/advanced_settings.go

@@ -1,11 +0,0 @@
-package settings
-
-type AdvancedSettings struct {
-	DebugMode                  bool     `json:"debug_mode"`                            // 是否开启调试模式,这个是写入一个特殊的文件来开启日志的 Debug 输出
-	SaveFullSeasonTmpSubtitles bool     `json:"save_full_season_tmp_subtitles"`        // 保存整季的缓存字幕
-	SubTypePriority            int      `json:"sub_type_priority" gorm:"default:'0'"`  // 字幕下载的优先级,0 是自动,1 是 srt 优先,2 是 ass/ssa 优先
-	SubNameFormatter           int      `json:"sub_name_formatter" gorm:"default:'0'"` // 字幕命名格式(默认不填写或者超出范围,则为 emby 格式),0,emby 支持的的格式(AAA.chinese(简英,subhd).ass or AAA.chinese(简英,xunlei).default.ass),1常规格式(兼容性更好,AAA.zh.ass or AAA.zh.default.ass)
-	SaveMultiSub               bool     `json:"save_multi_sub"`                        // 保存多个网站的 Top 1 字幕
-	CustomVideoExts            []string `json:"custom_video_exts"`                     // 自定义视频扩展名,是在原有基础上新增。
-	FixTimeLine                bool     `json:"fix_time_line"`                         // 开启校正字幕时间轴,默认 false
-}

+ 0 - 11
internal/models/settings/common_settings.go

@@ -1,11 +0,0 @@
-package settings
-
-type CommonSettings struct {
-	UseHttpProxy     bool     `json:"use_http_proxy"`                    // 是否使用 http 代理
-	HttpProxyAddress string   `json:"http_proxy_address"`                // Http 代理地址,内网
-	ScanInterval     string   `json:"scan_interval" gorm:"default:'6h'"` // 一轮字幕扫描的间隔
-	Threads          int      `json:"threads" gorm:"default:'1'"`        // 同时扫描的并发数
-	RunScanAtStartUp bool     `json:"run_scan_at_start_up"`              // 完成引导设置后,下次运行程序就开始扫描
-	MoviePaths       []string `json:"movie_paths"`                       // 电影的目录
-	SeriesPaths      []string `json:"series_paths"`                      // 连续剧的目录
-}

+ 0 - 5
internal/models/settings/developer_settings.go

@@ -1,5 +0,0 @@
-package settings
-
-type DeveloperSettings struct {
-	BarkServerUrl string `json:"barkServerUrl"` // Bark 服务器的地址
-}

+ 0 - 11
internal/models/settings/emby_settings.go

@@ -1,11 +0,0 @@
-package settings
-
-type EmbySettings struct {
-	Enable                 bool              `json:"enable"`                                      // 是否启用
-	AddressUrl             string            `json:"addressUrl"`                                  // 内网服务器的 url
-	APIKey                 string            `json:"APIKey"`                                      // API key
-	MaxRequestVideoNumber  int               `json:"maxRequestVideoNumber"  gorm:"default:'500'"` // 最大请求获取视频的数量
-	SkipWatched            bool              `json:"skipWatched"`                                 // 是否跳过已经观看的
-	MovieDirectoryMapping  map[string]string `json:"movieDirectoryMapping"`                       // 电影目录的映射,一旦 common setting 的目录修改,需要提示用户确认映射
-	SeriesDirectoryMapping map[string]string `json:"seriesDirectoryMapping"`                      // 连续剧目录的映射,一旦 common setting 的目录修改,需要提示用户确认映射
-}

+ 0 - 11
internal/models/settings/settings.go

@@ -1,11 +0,0 @@
-package settings
-
-import "gorm.io/gorm"
-
-type Settings struct {
-	gorm.Model
-	CommonSettings    CommonSettings    `gorm:"embedded;embeddedPrefix:common_"`
-	AdvancedSettings  AdvancedSettings  `gorm:"embedded;embeddedPrefix:advanced_"`
-	EmbySettings      EmbySettings      `gorm:"embedded;embeddedPrefix:emby_"`
-	DeveloperSettings DeveloperSettings `gorm:"embedded;embeddedPrefix:developer_"`
-}

+ 0 - 92
internal/pkg/config/config.go

@@ -1,92 +0,0 @@
-package config
-
-import (
-	"errors"
-	"fmt"
-	"github.com/allanpk716/ChineseSubFinder/internal/pkg/global_value"
-	"github.com/allanpk716/ChineseSubFinder/internal/types"
-	"github.com/spf13/viper"
-	"os"
-	"runtime"
-	"strings"
-	"sync"
-)
-
-// GetConfig 统一获取配置的接口
-func GetConfig() *types.Config {
-	configOnce.Do(func() {
-		configViper, err := initConfigure()
-		if err != nil {
-			panic("GetConfig - initConfigure " + err.Error())
-		}
-		config, err = readConfig(configViper)
-		if err != nil {
-			panic("GetConfig - readConfig " + err.Error())
-		}
-		// 读取用户自定义的视频后缀名列表
-		for _, customExt := range strings.Split(config.CustomVideoExts, ",") {
-			global_value.CustomVideoExts = append(global_value.CustomVideoExts, "."+customExt)
-		}
-
-		// 这里进行 Default 值的判断
-		config.SubTimelineFixerConfig.CheckDefault()
-	})
-	return config
-}
-
-// initConfigure 初始化配置文件实例
-func initConfigure() (*viper.Viper, error) {
-	nowConfigDir := getConfigDir()
-	if nowConfigDir == "" {
-		fmt.Sprintf("initConfigure().getConfigDir()")
-	}
-
-	v := viper.New()
-	v.SetConfigName("config")     // 设置文件名称(无后缀)
-	v.SetConfigType("yaml")       // 设置后缀名 {"1.6以后的版本可以不设置该后缀"}
-	v.AddConfigPath(nowConfigDir) // 设置文件所在路径
-
-	err := v.ReadInConfig()
-	if err != nil {
-		return nil, errors.New("error reading config:" + err.Error())
-	}
-
-	return v, nil
-}
-
-// readConfig 读取配置文件
-func readConfig(viper *viper.Viper) (*types.Config, error) {
-	conf := &types.Config{}
-	err := viper.Unmarshal(conf)
-	if err != nil {
-		return nil, err
-	}
-	return conf, nil
-}
-
-func getConfigDir() string {
-	nowConfigDir := ""
-	sysType := runtime.GOOS
-	if sysType == "linux" {
-		nowConfigDir = configDirLinux
-	}
-	if sysType == "windows" {
-		nowConfigDir = configDirWindows
-	}
-	if sysType == "darwin" {
-		home, _ := os.UserHomeDir()
-		nowConfigDir = home + "/.config/chinesesubfinder/" + configDirDarwin
-	}
-	return nowConfigDir
-}
-
-var (
-	config     *types.Config
-	configOnce sync.Once
-)
-
-const (
-	configDirLinux   = "/config/"
-	configDirWindows = "."
-	configDirDarwin  = "."
-)

+ 0 - 11
internal/pkg/global_value/globalvalue.go

@@ -1,11 +0,0 @@
-package global_value
-
-// util.go
-var (
-	DefDebugFolder       = ""
-	DefTmpFolder         = ""
-	DefSubFixCacheFolder = ""
-	WantedExtMap         = make(map[string]string) // 人工确认的需要监控的视频后缀名
-	DefExtMap            = make(map[string]string) // 内置支持的视频后缀名列表
-	CustomVideoExts      = make([]string, 0)       // 用户额外自定义的视频后缀名列表
-)

+ 29 - 0
internal/pkg/my_util/folder.go

@@ -4,6 +4,7 @@ import (
 	"github.com/allanpk716/ChineseSubFinder/internal/pkg/global_value"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strconv"
 	"strings"
 )
@@ -278,8 +279,36 @@ func ClearFolderEx(folderFullPath string, overtime int) error {
 	return nil
 }
 
+// GetConfigRootDirFPath 获取 Config 的根目录,不同系统不一样
+func GetConfigRootDirFPath() string {
+
+	nowConfigFPath := ""
+	sysType := runtime.GOOS
+	if sysType == "linux" {
+		nowConfigFPath = configDirRootFPathLinux
+	} else if sysType == "windows" {
+		nowConfigFPath = configDirRootFPathWindows
+	} else if sysType == "darwin" {
+		home, err := os.UserHomeDir()
+		if err != nil {
+			panic("GetConfigRootDirFPath darwin get UserHomeDir, Error:" + err.Error())
+		}
+		nowConfigFPath = home + configDirRootFPathDarwin
+	} else {
+		panic("GetConfigRootDirFPath can't matched OSType: " + sysType + " ,You Should Implement It Yourself")
+	}
+
+	return nowConfigFPath
+}
+
 const (
 	DebugFolder       = "CSF-DebugThings" // 调试相关的文件夹
 	TmpFolder         = "CSF-TmpThings"   // 临时缓存的文件夹
 	SubFixCacheFolder = "CSF-SubFixCache" // 字幕时间校正的缓存文件夹,一般可以不清理
 )
+
+const (
+	configDirRootFPathWindows = "./"                         // Windows 就是在当前的程序目录
+	configDirRootFPathLinux   = "/config/"                   // Linux 是在 /config 下
+	configDirRootFPathDarwin  = "/.config/chinesesubfinder/" // Darwin 是在 os.UserHomeDir()/.config/chinesesubfinder/ 下
+)

+ 3 - 0
internal/pkg/my_util/util.go

@@ -305,6 +305,9 @@ func OSCheck() bool {
 	if sysType == "windows" {
 		return true
 	}
+	if sysType == "darwin" {
+		return true
+	}
 
 	return false
 }

+ 17 - 0
internal/pkg/settings/advanced_settings.go

@@ -0,0 +1,17 @@
+package settings
+
+type AdvancedSettings struct {
+	DebugMode                  bool     `json:"debug_mode"`                     // 是否开启调试模式,这个是写入一个特殊的文件来开启日志的 Debug 输出
+	SaveFullSeasonTmpSubtitles bool     `json:"save_full_season_tmp_subtitles"` // 保存整季的缓存字幕
+	SubTypePriority            int      `json:"sub_type_priority"`              // 字幕下载的优先级,0 是自动,1 是 srt 优先,2 是 ass/ssa 优先
+	SubNameFormatter           int      `json:"sub_name_formatter"`             // 字幕命名格式(默认不填写或者超出范围,则为 emby 格式),0,emby 支持的的格式(AAA.chinese(简英,subhd).ass or AAA.chinese(简英,xunlei).default.ass),1常规格式(兼容性更好,AAA.zh.ass or AAA.zh.default.ass)
+	SaveMultiSub               bool     `json:"save_multi_sub"`                 // 保存多个网站的 Top 1 字幕
+	CustomVideoExts            []string `json:"custom_video_exts""`             // 自定义视频扩展名,是在原有基础上新增。
+	FixTimeLine                bool     `json:"fix_time_line"`                  // 开启校正字幕时间轴,默认 false
+}
+
+func NewAdvancedSettings() *AdvancedSettings {
+	return &AdvancedSettings{
+		CustomVideoExts: make([]string, 0),
+	}
+}

+ 20 - 0
internal/pkg/settings/common_settings.go

@@ -0,0 +1,20 @@
+package settings
+
+type CommonSettings struct {
+	UseHttpProxy     bool     `json:"use_http_proxy"`       // 是否使用 http 代理
+	HttpProxyAddress string   `json:"http_proxy_address"`   // Http 代理地址,内网
+	ScanInterval     string   `json:"scan_interval"`        // 一轮字幕扫描的间隔
+	Threads          int      `json:"threads"`              // 同时扫描的并发数
+	RunScanAtStartUp bool     `json:"run_scan_at_start_up"` // 完成引导设置后,下次运行程序就开始扫描
+	MoviePaths       []string `json:"movie_paths"`          // 电影的目录
+	SeriesPaths      []string `json:"series_paths"`         // 连续剧的目录
+}
+
+func NewCommonSettings() *CommonSettings {
+	return &CommonSettings{
+		ScanInterval: "6h",
+		Threads:      1,
+		MoviePaths:   make([]string, 0),
+		SeriesPaths:  make([]string, 0),
+	}
+}

+ 9 - 0
internal/pkg/settings/developer_settings.go

@@ -0,0 +1,9 @@
+package settings
+
+type DeveloperSettings struct {
+	BarkServerUrl string `json:"bark_server_url"` // Bark 服务器的地址
+}
+
+func NewDeveloperSettings() *DeveloperSettings {
+	return &DeveloperSettings{}
+}

+ 19 - 0
internal/pkg/settings/emby_settings.go

@@ -0,0 +1,19 @@
+package settings
+
+type EmbySettings struct {
+	Enable                 bool              `json:"enable"`                   // 是否启用
+	AddressUrl             string            `json:"address_url"`              // 内网服务器的 url
+	APIKey                 string            `json:"api_key"`                  // API key
+	MaxRequestVideoNumber  int               `json:"max_request_video_number"` // 最大请求获取视频的数量
+	SkipWatched            bool              `json:"skip_watched"`             // 是否跳过已经观看的
+	MovieDirectoryMapping  map[string]string `json:"movie_directory_mapping"`  // 电影目录的映射,一旦 common setting 的目录修改,需要提示用户确认映射
+	SeriesDirectoryMapping map[string]string `json:"series_directory_mapping"` // 连续剧目录的映射,一旦 common setting 的目录修改,需要提示用户确认映射
+}
+
+func NewEmbySettings() *EmbySettings {
+	return &EmbySettings{
+		MaxRequestVideoNumber:  500,
+		MovieDirectoryMapping:  make(map[string]string, 0),
+		SeriesDirectoryMapping: make(map[string]string, 0),
+	}
+}

+ 85 - 0
internal/pkg/settings/settings.go

@@ -0,0 +1,85 @@
+package settings
+
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/strcut_json"
+	"github.com/huandu/go-clone"
+	"sync"
+)
+
+type Settings struct {
+	configFPath       string
+	UserInfo          *UserInfo          `json:"user_info"`
+	CommonSettings    *CommonSettings    `json:"common_settings"`
+	AdvancedSettings  *AdvancedSettings  `json:"advanced_settings"`
+	EmbySettings      *EmbySettings      `json:"emby_settings"`
+	DeveloperSettings *DeveloperSettings `json:"developer_settings"`
+}
+
+// GetSettings 获取 Settings 的实例
+func GetSettings() *Settings {
+	if settings == nil {
+
+		settingsOnce.Do(func() {
+			settings = NewSettings()
+			if my_util.IsFile(settings.configFPath) == false {
+				// 配置文件不存在,新建一个空白的
+				err := settings.Save()
+				if err != nil {
+					panic("Can't Save Config File:" + configName + " Error: " + err.Error())
+				}
+			} else {
+				// 读取存在的文件
+				err := settings.Read()
+				if err != nil {
+					panic("Can't Read Config File:" + configName + " Error: " + err.Error())
+				}
+			}
+		})
+	}
+	return settings
+}
+
+// SetFullNewSettings 从 Web 端传入新的 Settings 完整设置
+func SetFullNewSettings(inSettings *Settings) error {
+	settings = inSettings
+	return settings.Save()
+}
+
+func NewSettings() *Settings {
+
+	nowConfigFPath := ""
+
+	return &Settings{
+		configFPath:       nowConfigFPath,
+		UserInfo:          &UserInfo{},
+		CommonSettings:    NewCommonSettings(),
+		AdvancedSettings:  NewAdvancedSettings(),
+		EmbySettings:      NewEmbySettings(),
+		DeveloperSettings: NewDeveloperSettings(),
+	}
+}
+
+func (s *Settings) Read() error {
+	return strcut_json.ToStruct(s.configFPath, s)
+}
+
+func (s *Settings) Save() error {
+	return strcut_json.ToFile(s.configFPath, s)
+}
+
+func (s Settings) GetNoPasswordSettings() *Settings {
+	nowSettings := clone.Clone(s).(*Settings)
+	nowSettings.UserInfo.Password = noPassword4Show
+	return nowSettings
+}
+
+var (
+	settings     *Settings
+	settingsOnce sync.Once
+)
+
+const (
+	noPassword4Show = "******" // 填充使用
+	configName      = "ChineseSubFinderSettings.json"
+)

+ 8 - 4
internal/models/user_info.go → internal/pkg/settings/user_info.go

@@ -1,9 +1,13 @@
-package models
-
-import "gorm.io/gorm"
+package settings
 
 type UserInfo struct {
-	gorm.Model
 	Username string `json:"username" binding:"required,alphanum"`     // 用户名
 	Password string `json:"password" binding:"required,min=6,max=12"` // 密码
 }
+
+func NewUserInfo(userName, password string) *UserInfo {
+	return &UserInfo{
+		Username: userName,
+		Password: password,
+	}
+}

+ 0 - 7
internal/pkg/sqlite/errors.go

@@ -1,7 +0,0 @@
-package sqlite
-
-import "errors"
-
-var (
-	ErrConstraintsNotImplemented = errors.New("constraints not implemented on sqlite, consider using DisableForeignKeyConstraintWhenMigrating, more details https://github.com/go-gorm/gorm/wiki/GORM-V2-Release-Note-Draft#all-new-migrator")
-)

+ 0 - 285
internal/pkg/sqlite/migrator.go

@@ -1,285 +0,0 @@
-package sqlite
-
-import (
-	"fmt"
-	"regexp"
-	"strings"
-
-	"gorm.io/gorm"
-	"gorm.io/gorm/clause"
-	"gorm.io/gorm/migrator"
-	"gorm.io/gorm/schema"
-)
-
-type Migrator struct {
-	migrator.Migrator
-}
-
-func (m *Migrator) RunWithoutForeignKey(fc func() error) error {
-	var enabled int
-	m.DB.Raw("PRAGMA foreign_keys").Scan(&enabled)
-	if enabled == 1 {
-		m.DB.Exec("PRAGMA foreign_keys = OFF")
-		defer m.DB.Exec("PRAGMA foreign_keys = ON")
-	}
-
-	return fc()
-}
-
-func (m Migrator) HasTable(value interface{}) bool {
-	var count int
-	m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error {
-		return m.DB.Raw("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", stmt.Table).Row().Scan(&count)
-	})
-	return count > 0
-}
-
-func (m Migrator) DropTable(values ...interface{}) error {
-	return m.RunWithoutForeignKey(func() error {
-		values = m.ReorderModels(values, false)
-		tx := m.DB.Session(&gorm.Session{})
-
-		for i := len(values) - 1; i >= 0; i-- {
-			if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
-				return tx.Exec("DROP TABLE IF EXISTS ?", clause.Table{Name: stmt.Table}).Error
-			}); err != nil {
-				return err
-			}
-		}
-
-		return nil
-	})
-
-	return nil
-}
-
-func (m Migrator) HasColumn(value interface{}, name string) bool {
-	var count int
-	m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if field := stmt.Schema.LookUpField(name); field != nil {
-			name = field.DBName
-		}
-
-		if name != "" {
-			m.DB.Raw(
-				"SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ?)",
-				"table", stmt.Table, `%"`+name+`" %`, `%`+name+` %`, "%`"+name+"`%",
-			).Row().Scan(&count)
-		}
-		return nil
-	})
-	return count > 0
-}
-
-func (m Migrator) AlterColumn(value interface{}, name string) error {
-	return m.RunWithoutForeignKey(func() error {
-		return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-			if field := stmt.Schema.LookUpField(name); field != nil {
-				var (
-					createSQL    string
-					newTableName = stmt.Table + "__temp"
-				)
-
-				m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "table", stmt.Table, stmt.Table).Row().Scan(&createSQL)
-
-				if reg, err := regexp.Compile("(`|'|\"| )" + field.DBName + "(`|'|\"| ) .*?,"); err == nil {
-					tableReg, err := regexp.Compile(" ('|`|\"| )" + stmt.Table + "('|`|\"| ) ")
-					if err != nil {
-						return err
-					}
-
-					createSQL = tableReg.ReplaceAllString(createSQL, fmt.Sprintf(" `%v` ", newTableName))
-					createSQL = reg.ReplaceAllString(createSQL, fmt.Sprintf("`%v` ?,", field.DBName))
-
-					var columns []string
-					columnTypes, _ := m.DB.Migrator().ColumnTypes(value)
-					for _, columnType := range columnTypes {
-						columns = append(columns, fmt.Sprintf("`%v`", columnType.Name()))
-					}
-
-					return m.DB.Transaction(func(tx *gorm.DB) error {
-						queries := []string{
-							createSQL,
-							fmt.Sprintf("INSERT INTO `%v`(%v) SELECT %v FROM `%v`", newTableName, strings.Join(columns, ","), strings.Join(columns, ","), stmt.Table),
-							fmt.Sprintf("DROP TABLE `%v`", stmt.Table),
-							fmt.Sprintf("ALTER TABLE `%v` RENAME TO `%v`", newTableName, stmt.Table),
-						}
-						for _, query := range queries {
-							if err := tx.Exec(query, m.FullDataTypeOf(field)).Error; err != nil {
-								return err
-							}
-						}
-						return nil
-					})
-				} else {
-					return err
-				}
-			} else {
-				return fmt.Errorf("failed to alter field with name %v", name)
-			}
-		})
-	})
-}
-
-func (m Migrator) DropColumn(value interface{}, name string) error {
-	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if field := stmt.Schema.LookUpField(name); field != nil {
-			name = field.DBName
-		}
-
-		var (
-			createSQL    string
-			newTableName = stmt.Table + "__temp"
-		)
-
-		m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "table", stmt.Table, stmt.Table).Row().Scan(&createSQL)
-
-		if reg, err := regexp.Compile("(`|'|\"| )" + name + "(`|'|\"| ) .*?,"); err == nil {
-			tableReg, err := regexp.Compile(" ('|`|\"| )" + stmt.Table + "('|`|\"| ) ")
-			if err != nil {
-				return err
-			}
-
-			createSQL = tableReg.ReplaceAllString(createSQL, fmt.Sprintf(" `%v` ", newTableName))
-			createSQL = reg.ReplaceAllString(createSQL, "")
-
-			var columns []string
-			columnTypes, _ := m.DB.Migrator().ColumnTypes(value)
-			for _, columnType := range columnTypes {
-				if columnType.Name() != name {
-					columns = append(columns, fmt.Sprintf("`%v`", columnType.Name()))
-				}
-			}
-
-			return m.DB.Transaction(func(tx *gorm.DB) error {
-				queries := []string{
-					createSQL,
-					fmt.Sprintf("INSERT INTO `%v`(%v) SELECT %v FROM `%v`", newTableName, strings.Join(columns, ","), strings.Join(columns, ","), stmt.Table),
-					fmt.Sprintf("DROP TABLE `%v`", stmt.Table),
-					fmt.Sprintf("ALTER TABLE `%v` RENAME TO `%v`", newTableName, stmt.Table),
-				}
-				for _, query := range queries {
-					if err := tx.Exec(query).Error; err != nil {
-						return err
-					}
-				}
-				return nil
-			})
-		} else {
-			return err
-		}
-	})
-}
-
-func (m Migrator) CreateConstraint(interface{}, string) error {
-	return ErrConstraintsNotImplemented
-}
-
-func (m Migrator) DropConstraint(interface{}, string) error {
-	return ErrConstraintsNotImplemented
-}
-
-func (m Migrator) HasConstraint(value interface{}, name string) bool {
-	var count int64
-	m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		m.DB.Raw(
-			"SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ?)",
-			"table", stmt.Table, `%CONSTRAINT "`+name+`" %`, `%CONSTRAINT `+name+` %`, "%CONSTRAINT `"+name+"`%",
-		).Row().Scan(&count)
-
-		return nil
-	})
-
-	return count > 0
-}
-
-func (m Migrator) CurrentDatabase() (name string) {
-	var null interface{}
-	m.DB.Raw("PRAGMA database_list").Row().Scan(&null, &name, &null)
-	return
-}
-
-func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) {
-	for _, opt := range opts {
-		str := stmt.Quote(opt.DBName)
-		if opt.Expression != "" {
-			str = opt.Expression
-		}
-
-		if opt.Collate != "" {
-			str += " COLLATE " + opt.Collate
-		}
-
-		if opt.Sort != "" {
-			str += " " + opt.Sort
-		}
-		results = append(results, clause.Expr{SQL: str})
-	}
-	return
-}
-
-func (m Migrator) CreateIndex(value interface{}, name string) error {
-	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if idx := stmt.Schema.LookIndex(name); idx != nil {
-			opts := m.BuildIndexOptions(idx.Fields, stmt)
-			values := []interface{}{clause.Column{Name: idx.Name}, clause.Table{Name: stmt.Table}, opts}
-
-			createIndexSQL := "CREATE "
-			if idx.Class != "" {
-				createIndexSQL += idx.Class + " "
-			}
-			createIndexSQL += "INDEX ?"
-
-			if idx.Type != "" {
-				createIndexSQL += " USING " + idx.Type
-			}
-			createIndexSQL += " ON ??"
-
-			if idx.Where != "" {
-				createIndexSQL += " WHERE " + idx.Where
-			}
-
-			return m.DB.Exec(createIndexSQL, values...).Error
-		}
-
-		return fmt.Errorf("failed to create index with name %v", name)
-	})
-}
-
-func (m Migrator) HasIndex(value interface{}, name string) bool {
-	var count int
-	m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if idx := stmt.Schema.LookIndex(name); idx != nil {
-			name = idx.Name
-		}
-
-		if name != "" {
-			m.DB.Raw(
-				"SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, name,
-			).Row().Scan(&count)
-		}
-		return nil
-	})
-	return count > 0
-}
-
-func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
-	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		var sql string
-		m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, oldName).Row().Scan(&sql)
-		if sql != "" {
-			return m.DB.Exec(strings.Replace(sql, oldName, newName, 1)).Error
-		}
-		return fmt.Errorf("failed to find index with name %v", oldName)
-	})
-}
-
-func (m Migrator) DropIndex(value interface{}, name string) error {
-	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if idx := stmt.Schema.LookIndex(name); idx != nil {
-			name = idx.Name
-		}
-
-		return m.DB.Exec("DROP INDEX ?", clause.Column{Name: name}).Error
-	})
-}

+ 0 - 180
internal/pkg/sqlite/mysqlite.go

@@ -1,180 +0,0 @@
-package sqlite
-
-import (
-	"database/sql"
-	"strconv"
-	"strings"
-
-	"gorm.io/gorm"
-	"gorm.io/gorm/callbacks"
-	"gorm.io/gorm/clause"
-	"gorm.io/gorm/logger"
-	"gorm.io/gorm/migrator"
-	"gorm.io/gorm/schema"
-	_ "modernc.org/sqlite"
-)
-
-// DriverName is the default driver name for SQLite.
-const DriverName = "sqlite"
-
-type Dialector struct {
-	DriverName string
-	DSN        string
-	Conn       gorm.ConnPool
-}
-
-func Open(dsn string) gorm.Dialector {
-	return &Dialector{DSN: dsn}
-}
-
-func (dialector Dialector) Name() string {
-	return "sqlite"
-}
-
-func (dialector Dialector) Initialize(db *gorm.DB) (err error) {
-	if dialector.DriverName == "" {
-		dialector.DriverName = DriverName
-	}
-
-	// register callbacks
-	callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{
-		LastInsertIDReversed: true,
-	})
-
-	if dialector.Conn != nil {
-		db.ConnPool = dialector.Conn
-	} else {
-		db.ConnPool, err = sql.Open(dialector.DriverName, dialector.DSN)
-		if err != nil {
-			return err
-		}
-	}
-
-	for k, v := range dialector.ClauseBuilders() {
-		db.ClauseBuilders[k] = v
-	}
-	return
-}
-
-func (dialector Dialector) ClauseBuilders() map[string]clause.ClauseBuilder {
-	return map[string]clause.ClauseBuilder{
-		"INSERT": func(c clause.Clause, builder clause.Builder) {
-			if insert, ok := c.Expression.(clause.Insert); ok {
-				if stmt, ok := builder.(*gorm.Statement); ok {
-					stmt.WriteString("INSERT ")
-					if insert.Modifier != "" {
-						stmt.WriteString(insert.Modifier)
-						stmt.WriteByte(' ')
-					}
-
-					stmt.WriteString("INTO ")
-					if insert.Table.Name == "" {
-						stmt.WriteQuoted(stmt.Table)
-					} else {
-						stmt.WriteQuoted(insert.Table)
-					}
-					return
-				}
-			}
-
-			c.Build(builder)
-		},
-		"LIMIT": func(c clause.Clause, builder clause.Builder) {
-			if limit, ok := c.Expression.(clause.Limit); ok {
-				if limit.Limit > 0 {
-					builder.WriteString("LIMIT ")
-					builder.WriteString(strconv.Itoa(limit.Limit))
-				}
-				if limit.Offset > 0 {
-					if limit.Limit > 0 {
-						builder.WriteString(" ")
-					}
-					builder.WriteString("OFFSET ")
-					builder.WriteString(strconv.Itoa(limit.Offset))
-				}
-			}
-		},
-		"FOR": func(c clause.Clause, builder clause.Builder) {
-			if _, ok := c.Expression.(clause.Locking); ok {
-				// SQLite3 does not support row-level locking.
-				return
-			}
-			c.Build(builder)
-		},
-	}
-}
-
-func (dialector Dialector) DefaultValueOf(field *schema.Field) clause.Expression {
-	if field.AutoIncrement {
-		return clause.Expr{SQL: "NULL"}
-	}
-
-	// doesn't work, will raise error
-	return clause.Expr{SQL: "DEFAULT"}
-}
-
-func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator {
-	return Migrator{migrator.Migrator{Config: migrator.Config{
-		DB:                          db,
-		Dialector:                   dialector,
-		CreateIndexAfterCreateTable: true,
-	}}}
-}
-
-func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) {
-	writer.WriteByte('?')
-}
-
-func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
-	writer.WriteByte('`')
-	if strings.Contains(str, ".") {
-		for idx, str := range strings.Split(str, ".") {
-			if idx > 0 {
-				writer.WriteString(".`")
-			}
-			writer.WriteString(str)
-			writer.WriteByte('`')
-		}
-	} else {
-		writer.WriteString(str)
-		writer.WriteByte('`')
-	}
-}
-
-func (dialector Dialector) Explain(sql string, vars ...interface{}) string {
-	return logger.ExplainSQL(sql, nil, `"`, vars...)
-}
-
-func (dialector Dialector) DataTypeOf(field *schema.Field) string {
-	switch field.DataType {
-	case schema.Bool:
-		return "numeric"
-	case schema.Int, schema.Uint:
-		if field.AutoIncrement && !field.PrimaryKey {
-			// https://www.sqlite.org/autoinc.html
-			return "integer PRIMARY KEY AUTOINCREMENT"
-		} else {
-			return "integer"
-		}
-	case schema.Float:
-		return "real"
-	case schema.String:
-		return "text"
-	case schema.Time:
-		return "datetime"
-	case schema.Bytes:
-		return "blob"
-	}
-
-	return string(field.DataType)
-}
-
-func (dialectopr Dialector) SavePoint(tx *gorm.DB, name string) error {
-	tx.Exec("SAVEPOINT " + name)
-	return nil
-}
-
-func (dialectopr Dialector) RollbackTo(tx *gorm.DB, name string) error {
-	tx.Exec("ROLLBACK TO SAVEPOINT " + name)
-	return nil
-}

+ 54 - 0
internal/pkg/strcut_json/struct_json.go

@@ -0,0 +1,54 @@
+package strcut_json
+
+import (
+	"encoding/json"
+	"io"
+	"os"
+	"path/filepath"
+)
+
+// ToFile 注意传入的不是指针
+func ToFile(srcJsonFileFPath string, input interface{}) error {
+	jsonBytes, err := json.Marshal(input)
+	if err != nil {
+		return err
+	}
+
+	file, err := os.Create(filepath.FromSlash(srcJsonFileFPath))
+	if err != nil {
+		return err
+	}
+	defer func() {
+		_ = file.Close()
+	}()
+
+	_, err = file.Write(jsonBytes)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// ToStruct 传入的必须是指针
+func ToStruct(desJsonFileFPath string, output interface{}) error {
+
+	file, err := os.Open(filepath.FromSlash(desJsonFileFPath))
+	if err != nil {
+		return err
+	}
+	defer func() {
+		_ = file.Close()
+	}()
+
+	bytes, err := io.ReadAll(file)
+	if err != nil {
+		return err
+	}
+
+	err = json.Unmarshal(bytes, output)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 64 - 0
internal/pkg/strcut_json/struct_json_test.go

@@ -0,0 +1,64 @@
+package strcut_json
+
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
+	"reflect"
+	"testing"
+)
+
+func TestToFile(t *testing.T) {
+
+	inSettings := settings.Settings{
+		UserInfo: &settings.UserInfo{
+			Username: "abcd",
+			Password: "123456",
+		},
+		CommonSettings: &settings.CommonSettings{
+			UseHttpProxy:     true,
+			HttpProxyAddress: "123",
+			ScanInterval:     "12h",
+			Threads:          12,
+			RunScanAtStartUp: true,
+			MoviePaths:       []string{"aaa", "bbb"},
+			SeriesPaths:      []string{"ccc", "ddd"},
+		},
+		AdvancedSettings: &settings.AdvancedSettings{
+			DebugMode:                  true,
+			SaveFullSeasonTmpSubtitles: true,
+			SubTypePriority:            1,
+			SubNameFormatter:           1,
+			SaveMultiSub:               true,
+			CustomVideoExts:            []string{"aaa", "bbb"},
+			FixTimeLine:                true,
+		},
+		EmbySettings: &settings.EmbySettings{
+			Enable:                 true,
+			AddressUrl:             "123456",
+			APIKey:                 "api123",
+			MaxRequestVideoNumber:  1000,
+			SkipWatched:            true,
+			MovieDirectoryMapping:  map[string]string{"aa": "123", "bb": "456"},
+			SeriesDirectoryMapping: map[string]string{"aab": "123", "bbc": "456"},
+		},
+		DeveloperSettings: &settings.DeveloperSettings{
+			BarkServerUrl: "bark",
+		},
+	}
+
+	err := ToFile(fileName, inSettings)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	outSettings := settings.NewSettings()
+	err = ToStruct(fileName, &outSettings)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if reflect.DeepEqual(inSettings.UserInfo, outSettings.UserInfo) == false {
+		t.Fatal("inSettings Write And Read Not The Same")
+	}
+}
+
+const fileName = "testfile.json"

+ 5 - 2
internal/types/backend/reply_login.go

@@ -1,6 +1,9 @@
 package backend
 
+import "github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
+
 type ReplyLogin struct {
-	AccessToken string `json:"access_token,omitempty"` // 登录成功后返回令牌
-	Message     string `json:"message,omitempty"`
+	AccessToken string             `json:"access_token,omitempty"` // 登录成功后返回令牌
+	Settings    *settings.Settings `json:"settings,omitempty"`     // 登录成功后返回当前的 Setting 信息
+	Message     string             `json:"message,omitempty"`
 }

+ 7 - 0
internal/types/backend/req_settings.go

@@ -0,0 +1,7 @@
+package backend
+
+import "github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
+
+type ReqSettings struct {
+	Settings settings.Settings `json:"settings" binding:"required"`
+}

+ 4 - 2
internal/types/backend/req_setup_info.go

@@ -1,7 +1,9 @@
 package backend
 
-import "github.com/allanpk716/ChineseSubFinder/internal/models"
+import (
+	"github.com/allanpk716/ChineseSubFinder/internal/pkg/settings"
+)
 
 type ReqSetupInfo struct {
-	UserInfo models.UserInfo `json:"user_info" binding:"required"`
+	Settings settings.Settings `json:"settings" binding:"required"`
 }

+ 0 - 28
internal/types/config.go

@@ -1,28 +0,0 @@
-package types
-
-import (
-	"github.com/allanpk716/ChineseSubFinder/internal/types/emby"
-	"github.com/allanpk716/ChineseSubFinder/internal/types/sub_timeline_fiexer"
-)
-
-type Config struct {
-	UseProxy                      bool                                       // 是否启用的代理
-	HttpProxy                     string                                     // http 代理地址
-	EveryTime                     string                                     // 一轮扫描字幕下载的间隔时间
-	DebugMode                     bool                                       // 是否启用 Debug 模式,调试功能
-	Threads                       int                                        // 同时并发的线程数(准确来说在go中不是线程,是 goroutine)
-	SubTypePriority               int                                        // 字幕下载的优先级,0 是自动,1 是 srt 优先,2 是 ass/ssa 优先
-	SubNameFormatter              int                                        // 字幕命名格式(默认不填写或者超出范围,则为 emby 格式),0,emby 支持的的格式(AAA.chinese(简英,subhd).ass or AAA.chinese(简英,xunlei).default.ass),1常规格式(兼容性更好,AAA.zh.ass or AAA.zh.default.ass)
-	WhenSubSupplierInvalidWebHook string                                     // 当字幕网站失效的时候,触发的 webhook 地址,默认是 get
-	EmbyConfig                    emby.EmbyConfig                            // Emby API 高阶设置参数
-	SaveMultiSub                  bool                                       // 保存多个网站的 Top 1 字幕
-	SaveOneSeasonSub              bool                                       // 保存整个季度的字幕
-	CustomVideoExts               string                                     // 自定义视频扩展名,多个扩展名用英文逗号分隔。是在原有基础上新增。
-	RunAtStartup                  bool                                       // 扫描任务是否在启动程序的时候马上执行 见,https://github.com/allanpk716/ChineseSubFinder/issues/50
-	SubTimelineFixerConfig        sub_timeline_fiexer.SubTimelineFixerConfig // 时间轴校正配置信息
-	FixTimeLine                   bool                                       // 	开启校正字幕时间轴,默认 false
-
-	MovieFolder  string // 电影文件夹
-	SeriesFolder string // 连续剧文件夹
-	AnimeFolder  string // 日本动画文件夹,很可能不会实现该功能
-}