mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-03 20:26:36 +08:00
refactor media classifier helpers
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||
)
|
||||
|
||||
func (s *SubscriptionService) classifySubscriptionItem(ctx context.Context, sub *model.Subscription, title, sourceCategory string) (string, string) {
|
||||
mediaType := normalizeMediaType(sub.MediaType, title+" "+sub.Name+" "+sub.Filter, sourceCategory)
|
||||
category := strings.TrimSpace(sub.MediaCategory)
|
||||
if category == "" {
|
||||
if match := s.lookupSubscriptionMetadata(ctx, mediaType, title, sub); match != nil {
|
||||
category = classifyMediaCategory(mediaClassifyInput{
|
||||
MediaType: mediaType,
|
||||
Title: match.Title + " " + match.OriginalName,
|
||||
Languages: match.Languages,
|
||||
Countries: match.Countries,
|
||||
Genres: match.Genres,
|
||||
Category: sourceCategory,
|
||||
}, s.categoryMap())
|
||||
if s != nil && s.log != nil && category != "" {
|
||||
s.log.Info("subscription metadata classified",
|
||||
zap.String("title", title),
|
||||
zap.String("matched_title", match.Title),
|
||||
zap.String("media_type", mediaType),
|
||||
zap.String("media_category", category),
|
||||
zap.Int("tmdb_id", match.TMDbID),
|
||||
zap.Int("bangumi_id", match.BangumiID),
|
||||
zap.String("douban_id", match.DoubanID),
|
||||
zap.String("thetvdb_id", match.TheTVDBID))
|
||||
}
|
||||
}
|
||||
}
|
||||
if category == "" {
|
||||
category = classifyMediaCategory(mediaClassifyInput{
|
||||
MediaType: mediaType,
|
||||
Title: title + " " + sub.Name + " " + sub.Filter,
|
||||
Category: sourceCategory,
|
||||
}, s.categoryMap())
|
||||
}
|
||||
return mediaType, category
|
||||
}
|
||||
|
||||
func (s *SubscriptionService) lookupSubscriptionMetadata(ctx context.Context, mediaType, title string, sub *model.Subscription) *Match {
|
||||
if s == nil || s.scraper == nil || !s.scraper.AnyEnabled() {
|
||||
return nil
|
||||
}
|
||||
queries := subscriptionMetadataQueries(title, sub)
|
||||
if len(queries) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, libType := range subscriptionMetadataLibraryTypes(mediaType, title) {
|
||||
lib := &model.Library{Type: libType, Enabled: true}
|
||||
for _, query := range queries {
|
||||
cleaned, year := CleanQuery(query)
|
||||
if cleaned == "" {
|
||||
cleaned = strings.TrimSpace(query)
|
||||
}
|
||||
for _, candidate := range titleCandidates(cleaned) {
|
||||
if candidate == "" {
|
||||
continue
|
||||
}
|
||||
match := s.scraper.lookup(ctx, lib, nil, candidate, year)
|
||||
if match == nil || strings.TrimSpace(match.Title) == "" {
|
||||
continue
|
||||
}
|
||||
if !organizeMetadataMatchTrusted(candidate, year, match) {
|
||||
continue
|
||||
}
|
||||
return match
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func subscriptionMetadataQueries(title string, sub *model.Subscription) []string {
|
||||
seen := map[string]struct{}{}
|
||||
out := make([]string, 0, 3)
|
||||
add := func(value string) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
key := strings.ToLower(value)
|
||||
if _, ok := seen[key]; ok {
|
||||
return
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
out = append(out, value)
|
||||
}
|
||||
add(title)
|
||||
if sub != nil {
|
||||
add(sub.Filter)
|
||||
add(sub.Name)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func subscriptionMetadataLibraryTypes(mediaType, title string) []string {
|
||||
if strings.TrimSpace(mediaType) == "" {
|
||||
text := strings.ToLower(title)
|
||||
switch {
|
||||
case classifierEpisodeRE.MatchString(text) || classifierSeasonRE.MatchString(text):
|
||||
return []string{"tv", "anime", "movie"}
|
||||
case containsAnyText(text, "动漫", "动画", "anime", "bangumi"):
|
||||
return []string{"anime", "tv", "movie"}
|
||||
case containsAnyText(text, "电影", "movie", "film"):
|
||||
return []string{"movie", "tv", "anime"}
|
||||
default:
|
||||
return []string{"tv", "movie", "anime"}
|
||||
}
|
||||
}
|
||||
switch normalizeMediaType(mediaType, title, "") {
|
||||
case "movie":
|
||||
return []string{"movie"}
|
||||
case "anime":
|
||||
return []string{"anime", "tv"}
|
||||
case "tv", "variety":
|
||||
return []string{"tv", "anime"}
|
||||
default:
|
||||
if classifierEpisodeRE.MatchString(title) || classifierSeasonRE.MatchString(title) {
|
||||
return []string{"tv", "anime"}
|
||||
}
|
||||
return []string{"movie", "tv", "anime"}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SubscriptionService) categoryMap() map[string]string {
|
||||
if s == nil || s.cfg == nil || s.cfg.Organizer.Categories == nil {
|
||||
return nil
|
||||
}
|
||||
return s.cfg.Organizer.Categories
|
||||
}
|
||||
|
||||
func (s *SubscriptionService) resolveSubscriptionSavePath(ctx context.Context, sub *model.Subscription, mediaType, category string) string {
|
||||
if sub == nil {
|
||||
return ""
|
||||
}
|
||||
base := strings.TrimSpace(sub.SavePath)
|
||||
if base == "" {
|
||||
base = downloadDefaultSaveRoot(ctx, s.repo)
|
||||
}
|
||||
if base == "" {
|
||||
return ""
|
||||
}
|
||||
if !s.isSmartClassifyEnabled(ctx) || category == "" {
|
||||
return base
|
||||
}
|
||||
return downloadSavePathCategoryRoot(base, sanitizeFilename(category))
|
||||
}
|
||||
|
||||
func (s *SubscriptionService) isSmartClassifyEnabled(ctx context.Context) bool {
|
||||
if s != nil && s.repo != nil && s.repo.Setting != nil {
|
||||
val, err := s.repo.Setting.Get(ctx, DownloadSmartClassifySettingKey)
|
||||
if err == nil && val != "" {
|
||||
return parseBoolSetting(val, true)
|
||||
}
|
||||
val, err = s.repo.Setting.Get(ctx, "organizer.smart_classify")
|
||||
if err == nil && parseBoolSetting(val, false) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if s != nil && s.cfg != nil && s.cfg.Organizer.SmartClassify {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user