mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
173 lines
4.9 KiB
Go
173 lines
4.9 KiB
Go
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 := CleanQueryWithRecognition(ctx, s.repo, 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
|
|
}
|