diff --git a/db/const.go b/db/const.go index cd92b63..e5e6088 100644 --- a/db/const.go +++ b/db/const.go @@ -15,6 +15,8 @@ const ( SettingAllowQiangban = "filter_qiangban" SettingEnableTmdbAdultContent = "tmdb_adult_content" SetttingSizeLimiter = "size_limiter" + SettingTvNamingFormat = "tv_naming_format" + SettingMovieNamingFormat = "movie_naming_format" ) const ( @@ -35,6 +37,15 @@ const ( LanguageCN = "zh-CN" ) +const DefaultNamingFormat = "{{.NameCN}} {{.NameEN}} ({{.Year}})" + +type NamingInfo struct { + NameCN string + NameEN string + Year string + TmdbID int +} + type ResolutionType string const JwtSerectKey = "jwt_secrect_key" diff --git a/db/db.go b/db/db.go index 92b1aa0..bcad5cd 100644 --- a/db/db.go +++ b/db/db.go @@ -627,4 +627,20 @@ func (c *Client) SetSizeLimiter(limiter *SizeLimiter) error { return err } return c.SetSetting(SetttingSizeLimiter, string(data)) +} + +func (c *Client) GetTvNamingFormat() string { + s := c.GetSetting(SettingTvNamingFormat) + if s == "" { + return DefaultNamingFormat + } + return s +} + +func (c *Client) GetMovingNamingFormat() string { + s := c.GetSetting(SettingMovieNamingFormat) + if s == "" { + return DefaultNamingFormat + } + return s } \ No newline at end of file diff --git a/server/core/importlist.go b/server/core/importlist.go index 1a07b94..a45b435 100644 --- a/server/core/importlist.go +++ b/server/core/importlist.go @@ -1,7 +1,9 @@ package core import ( + "bytes" "fmt" + "html/template" "io" "net/http" "os" @@ -366,27 +368,35 @@ func (c *Client) SuggestedMovieFolderName(tmdbId int) (string, error) { return javid, nil } } - - //if name is already in english, no need to query again - if !utils.IsASCII(name) && c.language == db.LanguageCN { + info := db.NamingInfo{TmdbID: tmdbId} + if utils.IsASCII(name) { + info.NameEN = stripExtraCharacters(name) + } else { + info.NameCN = stripExtraCharacters(name) en, err := c.MustTMDB().GetMovieDetails(tmdbId, db.LanguageEN) if err != nil { - log.Errorf("get en movie detail error: %v", err) + log.Errorf("get en tv detail error: %v", err) } else { - name = fmt.Sprintf("%s %s", name, en.Title) + info.NameEN = stripExtraCharacters(en.Title) } } - //remove extra characters - re := regexp.MustCompile(`[^\p{L}\w\s]`) - name = re.ReplaceAllString(name, " ") - name = strings.Join(strings.Fields(name), " ") year := strings.Split(d1.ReleaseDate, "-")[0] - if year != "" { - name = fmt.Sprintf("%s (%s)", name, year) - } + info.Year = year + movieNamingFormat := c.db.GetMovingNamingFormat() - log.Infof("tv series of tmdb id %v suggestting name is %v", tmdbId, name) - return name, nil + tmpl, err := template.New("test").Parse(movieNamingFormat) + if err != nil { + return "", errors.Wrap(err, "naming format") + } + buff := &bytes.Buffer{} + err = tmpl.Execute(buff, info) + if err != nil { + return "", errors.Wrap(err, "tmpl exec") + } + res := strings.TrimSpace(buff.String()) + + log.Infof("tv series of tmdb id %v suggestting name is %v", tmdbId, res) + return res, nil } func (c *Client) SuggestedSeriesFolderName(tmdbId int) (string, error) { @@ -398,24 +408,40 @@ func (c *Client) SuggestedSeriesFolderName(tmdbId int) (string, error) { name := d.Name - //if name is already in english, no need to query again - if !utils.IsASCII(name) && c.language == db.LanguageCN { + info := db.NamingInfo{TmdbID: tmdbId} + if utils.IsASCII(name) { + info.NameEN = stripExtraCharacters(name) + } else { + info.NameCN = stripExtraCharacters(name) en, err := c.MustTMDB().GetTvDetails(tmdbId, db.LanguageEN) if err != nil { log.Errorf("get en tv detail error: %v", err) } else { - name = fmt.Sprintf("%s %s", name, en.Name) + info.NameEN = stripExtraCharacters(en.Name) } } - //remove extra characters - re := regexp.MustCompile(`[^\p{L}\w\s]`) - name = re.ReplaceAllString(name, " ") - name = strings.Join(strings.Fields(name), " ") year := strings.Split(d.FirstAirDate, "-")[0] - if year != "" { - name = fmt.Sprintf("%s (%s)", name, year) - } + info.Year = year - log.Infof("tv series of tmdb id %v suggestting name is %v", tmdbId, name) - return name, nil + tvNamingFormat := c.db.GetTvNamingFormat() + + tmpl, err := template.New("test").Parse(tvNamingFormat) + if err != nil { + return "", errors.Wrap(err, "naming format") + } + buff := &bytes.Buffer{} + err = tmpl.Execute(buff, info) + if err != nil { + return "", errors.Wrap(err, "tmpl exec") + } + res := strings.TrimSpace(buff.String()) + + log.Infof("tv series of tmdb id %v suggestting name is %v", tmdbId, res) + return res, nil +} + +func stripExtraCharacters(s string) string { + re := regexp.MustCompile(`[^\p{L}\w\s]`) + s = re.ReplaceAllString(s, " ") + return strings.Join(strings.Fields(s), " ") } diff --git a/server/setting.go b/server/setting.go index 8779516..fd47402 100644 --- a/server/setting.go +++ b/server/setting.go @@ -22,6 +22,8 @@ type GeneralSettings struct { EnableNfo bool `json:"enable_nfo"` AllowQiangban bool `json:"allow_qiangban"` EnableAdultContent bool `json:"enable_adult_content"` + TvNamingFormat string `json:"tv_naming_format"` + MovieNamingFormat string `json:"movie_naming_format"` } func (s *Server) SetSetting(c *gin.Context) (interface{}, error) { @@ -46,7 +48,12 @@ func (s *Server) SetSetting(c *gin.Context) (interface{}, error) { if err := s.db.SetSetting(db.SettingLogLevel, in.LogLevel); err != nil { return nil, errors.Wrap(err, "save log level") } - + } + if in.TvNamingFormat != "" { + s.db.SetSetting(db.SettingTvNamingFormat, in.TvNamingFormat) + } + if in.MovieNamingFormat != "" { + s.db.SetSetting(db.SettingMovieNamingFormat, in.MovieNamingFormat) } plexmatchEnabled := s.db.GetSetting(db.SettingPlexMatchEnabled) @@ -74,7 +81,6 @@ func (s *Server) SetSetting(c *gin.Context) (interface{}, error) { } else { s.db.SetSetting(db.SettingEnableTmdbAdultContent, "false") } - return nil, nil } @@ -86,15 +92,19 @@ func (s *Server) GetSetting(c *gin.Context) (interface{}, error) { allowQiangban := s.db.GetSetting(db.SettingAllowQiangban) enableNfo := s.db.GetSetting(db.SettingNfoSupportEnabled) enableAdult := s.db.GetSetting(db.SettingEnableTmdbAdultContent) + tvFormat := s.db.GetTvNamingFormat() + movieFormat := s.db.GetMovingNamingFormat() return &GeneralSettings{ - TmdbApiKey: tmdb, - DownloadDir: downloadDir, - LogLevel: logLevel, - Proxy: s.db.GetSetting(db.SettingProxy), - EnablePlexmatch: plexmatchEnabled == "true", - AllowQiangban: allowQiangban == "true", - EnableNfo: enableNfo == "true", - EnableAdultContent: enableAdult == "true", + TmdbApiKey: tmdb, + DownloadDir: downloadDir, + LogLevel: logLevel, + Proxy: s.db.GetSetting(db.SettingProxy), + EnablePlexmatch: plexmatchEnabled == "true", + AllowQiangban: allowQiangban == "true", + EnableNfo: enableNfo == "true", + EnableAdultContent: enableAdult == "true", + TvNamingFormat: tvFormat, + MovieNamingFormat: movieFormat, }, nil } @@ -262,4 +272,4 @@ func (s *Server) TriggerCronJob(c *gin.Context) (interface{}, error) { return nil, err } return "success", nil -} \ No newline at end of file +} diff --git a/ui/lib/activity.dart b/ui/lib/activity.dart index 8d6f3a5..02d4994 100644 --- a/ui/lib/activity.dart +++ b/ui/lib/activity.dart @@ -138,7 +138,7 @@ class _ActivityPageState extends ConsumerState trailing: selectedTab == 0 ? IconButton( tooltip: "删除任务", - onPressed: () => onDelete()(ac.id!), + onPressed: () => onDelete()(ac.id!.toString()), icon: const Icon(Icons.delete)) : const Text("-"), ), diff --git a/ui/lib/providers/activity.dart b/ui/lib/providers/activity.dart index 57832ad..6e71227 100644 --- a/ui/lib/providers/activity.dart +++ b/ui/lib/providers/activity.dart @@ -72,7 +72,7 @@ class Activity { required this.size, required this.seedRatio}); - final String? id; + final int? id; final int? mediaId; final int? episodeId; final String? sourceTitle; diff --git a/ui/lib/providers/settings.dart b/ui/lib/providers/settings.dart index 00ef5f7..1016d51 100644 --- a/ui/lib/providers/settings.dart +++ b/ui/lib/providers/settings.dart @@ -59,6 +59,8 @@ class GeneralSetting { bool? allowQiangban; bool? enableNfo; bool? enableAdult; + String? tvNamingFormat; + String? movieNamingFormat; GeneralSetting( {this.tmdbApiKey, @@ -68,6 +70,8 @@ class GeneralSetting { this.enablePlexmatch, this.enableNfo, this.allowQiangban, + this.tvNamingFormat, + this.movieNamingFormat, this.enableAdult}); factory GeneralSetting.fromJson(Map json) { @@ -79,6 +83,8 @@ class GeneralSetting { enableAdult: json["enable_adult_content"] ?? false, allowQiangban: json["allow_qiangban"] ?? false, enableNfo: json["enable_nfo"] ?? false, + tvNamingFormat: json["tv_naming_format"], + movieNamingFormat: json["movie_naming_format"], enablePlexmatch: json["enable_plexmatch"] ?? false); } @@ -92,6 +98,8 @@ class GeneralSetting { data["allow_qiangban"] = allowQiangban; data["enable_nfo"] = enableNfo; data["enable_adult_content"] = enableAdult; + data["tv_naming_format"] = tvNamingFormat; + data["movie_naming_format"] = movieNamingFormat; return data; } } diff --git a/ui/lib/settings/general.dart b/ui/lib/settings/general.dart index 0895476..3c6b5ba 100644 --- a/ui/lib/settings/general.dart +++ b/ui/lib/settings/general.dart @@ -38,6 +38,8 @@ class _GeneralState extends ConsumerState { "allow_qiangban": v.allowQiangban, "enable_nfo": v.enableNfo, "enable_adult": v.enableAdult, + "tv_naming_format": v.tvNamingFormat, + "movie_naming_format": v.movieNamingFormat, }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -66,6 +68,22 @@ class _GeneralState extends ConsumerState { hintText: "http://10.0.0.1:1080", helperText: "后台联网代理地址,留空表示不启用代理"), ), + FormBuilderTextField( + decoration: const InputDecoration( + icon: Icon(Icons.folder), + labelText: "电视剧路径命名规则", + helperText: + "go template语法,可用的变量为:.NameCN, .NameEN, .Year, .TmdbID"), + name: "tv_naming_format", + ), + FormBuilderTextField( + decoration: const InputDecoration( + icon: Icon(Icons.folder), + labelText: "电影路径命名规则", + helperText: + "go template语法,可用的变量为:.NameCN, .NameEN, .Year, .TmdbID"), + name: "movie_naming_format", + ), SizedBox( width: 300, child: FormBuilderDropdown( @@ -137,6 +155,9 @@ class _GeneralState extends ConsumerState { allowQiangban: values["allow_qiangban"], enableAdult: values["enable_adult"], enableNfo: values["enable_nfo"], + tvNamingFormat: values["tv_naming_format"], + movieNamingFormat: + values["movie_naming_format"], enablePlexmatch: values["enable_plexmatch"])) .then((v) => showSnakeBar("更新成功"));