mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-08 22:36:37 +08:00
split metadata enrichment helpers
This commit is contained in:
@@ -2,17 +2,12 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
|
||||||
"github.com/ShukeBta/MediaStationGo/internal/service"
|
"github.com/ShukeBta/MediaStationGo/internal/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,276 +21,6 @@ type cachedDisplayMetadata struct {
|
|||||||
expiresAt time.Time
|
expiresAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func enrichSubscriptionArtwork(ctx context.Context, svc *service.Container, sub *model.Subscription) {
|
|
||||||
if svc == nil || sub == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 已有图片且媒体展示字段也齐全时才跳过;否则仍需查一次补 年份/评分/类型 等
|
|
||||||
// 富通知字段(老订阅只存了 poster/overview 的情况)。
|
|
||||||
if strings.TrimSpace(sub.PosterURL) != "" && strings.TrimSpace(sub.BackdropURL) != "" &&
|
|
||||||
sub.Year > 0 && sub.Rating > 0 && strings.TrimSpace(sub.Genres) != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
meta := lookupDisplayMetadata(ctx, svc, sub.Name, sub.Filter, sub.MediaType)
|
|
||||||
if meta.Title == "" && meta.PosterURL == "" && meta.BackdropURL == "" && meta.Overview == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(sub.Source) == "" {
|
|
||||||
sub.Source = meta.Source
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(sub.PosterURL) == "" {
|
|
||||||
sub.PosterURL = meta.PosterURL
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(sub.BackdropURL) == "" {
|
|
||||||
sub.BackdropURL = meta.BackdropURL
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(sub.Overview) == "" {
|
|
||||||
sub.Overview = meta.Overview
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(sub.OriginalName) == "" {
|
|
||||||
sub.OriginalName = meta.OriginalName
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(sub.OriginalLanguage) == "" {
|
|
||||||
sub.OriginalLanguage = meta.OriginalLanguage
|
|
||||||
}
|
|
||||||
if sub.Year <= 0 && meta.Year > 0 {
|
|
||||||
sub.Year = meta.Year
|
|
||||||
}
|
|
||||||
if sub.Rating <= 0 && meta.Rating > 0 {
|
|
||||||
sub.Rating = meta.Rating
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(sub.Genres) == "" {
|
|
||||||
sub.Genres = meta.Genres
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enrichAndPersistSubscriptions(ctx context.Context, svc *service.Container, items []model.Subscription) {
|
|
||||||
for i := range items {
|
|
||||||
before := items[i]
|
|
||||||
enrichSubscriptionArtwork(ctx, svc, &items[i])
|
|
||||||
if before.Source == items[i].Source &&
|
|
||||||
before.PosterURL == items[i].PosterURL &&
|
|
||||||
before.BackdropURL == items[i].BackdropURL &&
|
|
||||||
before.Overview == items[i].Overview &&
|
|
||||||
before.OriginalName == items[i].OriginalName &&
|
|
||||||
before.OriginalLanguage == items[i].OriginalLanguage &&
|
|
||||||
before.Year == items[i].Year &&
|
|
||||||
before.Rating == items[i].Rating &&
|
|
||||||
before.Genres == items[i].Genres {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
updates := map[string]any{}
|
|
||||||
if before.Source != items[i].Source {
|
|
||||||
updates["source"] = items[i].Source
|
|
||||||
}
|
|
||||||
if before.PosterURL != items[i].PosterURL {
|
|
||||||
updates["poster_url"] = items[i].PosterURL
|
|
||||||
}
|
|
||||||
if before.BackdropURL != items[i].BackdropURL {
|
|
||||||
updates["backdrop_url"] = items[i].BackdropURL
|
|
||||||
}
|
|
||||||
if before.Overview != items[i].Overview {
|
|
||||||
updates["overview"] = items[i].Overview
|
|
||||||
}
|
|
||||||
if before.OriginalName != items[i].OriginalName {
|
|
||||||
updates["original_name"] = items[i].OriginalName
|
|
||||||
}
|
|
||||||
if before.OriginalLanguage != items[i].OriginalLanguage {
|
|
||||||
updates["original_language"] = items[i].OriginalLanguage
|
|
||||||
}
|
|
||||||
if before.Year != items[i].Year {
|
|
||||||
updates["year"] = items[i].Year
|
|
||||||
}
|
|
||||||
if before.Rating != items[i].Rating {
|
|
||||||
updates["rating"] = items[i].Rating
|
|
||||||
}
|
|
||||||
if before.Genres != items[i].Genres {
|
|
||||||
updates["genres"] = items[i].Genres
|
|
||||||
}
|
|
||||||
if len(updates) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := svc.Repo.DB.WithContext(ctx).Model(&model.Subscription{}).Where("id = ?", items[i].ID).Updates(updates).Error; err != nil {
|
|
||||||
svc.Log.Debug("subscription artwork backfill failed", zap.String("id", items[i].ID), zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enrichAndPersistDownloadRows(ctx context.Context, svc *service.Container, rows []model.DownloadTask) {
|
|
||||||
for i := range rows {
|
|
||||||
before := rows[i]
|
|
||||||
title := strings.TrimSpace(rows[i].Title)
|
|
||||||
if title == "" {
|
|
||||||
title = downloadDisplayTitle(rows[i].URL)
|
|
||||||
rows[i].Title = title
|
|
||||||
}
|
|
||||||
needsMeta := strings.TrimSpace(rows[i].PosterURL) == "" || strings.TrimSpace(rows[i].BackdropURL) == "" ||
|
|
||||||
strings.TrimSpace(rows[i].Overview) == "" || rows[i].Year <= 0 || rows[i].Rating <= 0 ||
|
|
||||||
strings.TrimSpace(rows[i].Genres) == "" || strings.TrimSpace(rows[i].OriginalName) == "" ||
|
|
||||||
strings.TrimSpace(rows[i].OriginalLanguage) == ""
|
|
||||||
if needsMeta {
|
|
||||||
meta := enrichDownloadTaskMeta(ctx, svc, service.DownloadTaskMeta{
|
|
||||||
Title: rows[i].Title,
|
|
||||||
PosterURL: rows[i].PosterURL,
|
|
||||||
BackdropURL: rows[i].BackdropURL,
|
|
||||||
Overview: rows[i].Overview,
|
|
||||||
OriginalName: rows[i].OriginalName,
|
|
||||||
OriginalLanguage: rows[i].OriginalLanguage,
|
|
||||||
Year: rows[i].Year,
|
|
||||||
Rating: rows[i].Rating,
|
|
||||||
Genres: rows[i].Genres,
|
|
||||||
}, firstNonEmptyString(rows[i].Title, rows[i].URL), rows[i].MediaType)
|
|
||||||
rows[i].Title = firstNonEmptyString(rows[i].Title, meta.Title)
|
|
||||||
rows[i].PosterURL = meta.PosterURL
|
|
||||||
rows[i].BackdropURL = meta.BackdropURL
|
|
||||||
rows[i].Overview = meta.Overview
|
|
||||||
rows[i].OriginalName = meta.OriginalName
|
|
||||||
rows[i].OriginalLanguage = meta.OriginalLanguage
|
|
||||||
rows[i].Year = meta.Year
|
|
||||||
rows[i].Rating = meta.Rating
|
|
||||||
rows[i].Genres = meta.Genres
|
|
||||||
}
|
|
||||||
if before.Title == rows[i].Title &&
|
|
||||||
before.PosterURL == rows[i].PosterURL &&
|
|
||||||
before.BackdropURL == rows[i].BackdropURL &&
|
|
||||||
before.Overview == rows[i].Overview &&
|
|
||||||
before.OriginalName == rows[i].OriginalName &&
|
|
||||||
before.OriginalLanguage == rows[i].OriginalLanguage &&
|
|
||||||
before.Year == rows[i].Year &&
|
|
||||||
before.Rating == rows[i].Rating &&
|
|
||||||
before.Genres == rows[i].Genres {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
updates := map[string]any{}
|
|
||||||
if before.Title != rows[i].Title {
|
|
||||||
updates["title"] = rows[i].Title
|
|
||||||
}
|
|
||||||
if before.PosterURL != rows[i].PosterURL {
|
|
||||||
updates["poster_url"] = rows[i].PosterURL
|
|
||||||
}
|
|
||||||
if before.BackdropURL != rows[i].BackdropURL {
|
|
||||||
updates["backdrop_url"] = rows[i].BackdropURL
|
|
||||||
}
|
|
||||||
if before.Overview != rows[i].Overview {
|
|
||||||
updates["overview"] = rows[i].Overview
|
|
||||||
}
|
|
||||||
if before.OriginalName != rows[i].OriginalName {
|
|
||||||
updates["original_name"] = rows[i].OriginalName
|
|
||||||
}
|
|
||||||
if before.OriginalLanguage != rows[i].OriginalLanguage {
|
|
||||||
updates["original_language"] = rows[i].OriginalLanguage
|
|
||||||
}
|
|
||||||
if before.Year != rows[i].Year {
|
|
||||||
updates["year"] = rows[i].Year
|
|
||||||
}
|
|
||||||
if before.Rating != rows[i].Rating {
|
|
||||||
updates["rating"] = rows[i].Rating
|
|
||||||
}
|
|
||||||
if before.Genres != rows[i].Genres {
|
|
||||||
updates["genres"] = rows[i].Genres
|
|
||||||
}
|
|
||||||
if len(updates) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := svc.Repo.DB.WithContext(ctx).Model(&model.DownloadTask{}).Where("id = ?", rows[i].ID).Updates(updates).Error; err != nil {
|
|
||||||
svc.Log.Debug("download artwork backfill failed", zap.String("id", rows[i].ID), zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enrichDownloadTorrentViews(ctx context.Context, svc *service.Container, views []service.DownloadTorrentView) {
|
|
||||||
cache := map[string]displayMetadata{}
|
|
||||||
for i := range views {
|
|
||||||
if strings.TrimSpace(views[i].PosterURL) != "" && strings.TrimSpace(views[i].BackdropURL) != "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
query := firstNonEmptyString(views[i].Title, views[i].Name)
|
|
||||||
cacheKey, _ := metadataSearchQuery(query)
|
|
||||||
meta, ok := cache[cacheKey]
|
|
||||||
if !ok {
|
|
||||||
meta = lookupDisplayMetadata(ctx, svc, query, "", "")
|
|
||||||
cache[cacheKey] = meta
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(views[i].PosterURL) == "" {
|
|
||||||
views[i].PosterURL = meta.PosterURL
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(views[i].BackdropURL) == "" {
|
|
||||||
views[i].BackdropURL = meta.BackdropURL
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(views[i].Overview) == "" {
|
|
||||||
views[i].Overview = meta.Overview
|
|
||||||
}
|
|
||||||
if (views[i].Title == "" || views[i].Title == views[i].Name) && meta.Title != "" {
|
|
||||||
views[i].Title = meta.Title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enrichDownloadTaskMeta(ctx context.Context, svc *service.Container, meta service.DownloadTaskMeta, fallbackTitle, mediaType string) service.DownloadTaskMeta {
|
|
||||||
if strings.TrimSpace(meta.Title) == "" {
|
|
||||||
meta.Title = strings.TrimSpace(downloadDisplayTitle(fallbackTitle))
|
|
||||||
}
|
|
||||||
// 仅当展示字段都齐时才跳过查询(含媒体富通知所需的年份/评分/类型/原始信息)。
|
|
||||||
if strings.TrimSpace(meta.PosterURL) != "" && strings.TrimSpace(meta.BackdropURL) != "" &&
|
|
||||||
strings.TrimSpace(meta.Overview) != "" && meta.Year > 0 && meta.Rating > 0 &&
|
|
||||||
strings.TrimSpace(meta.Genres) != "" && strings.TrimSpace(meta.OriginalName) != "" &&
|
|
||||||
strings.TrimSpace(meta.OriginalLanguage) != "" {
|
|
||||||
return meta
|
|
||||||
}
|
|
||||||
found := lookupDisplayMetadata(ctx, svc, meta.Title, fallbackTitle, mediaType)
|
|
||||||
if strings.TrimSpace(meta.Title) == "" {
|
|
||||||
meta.Title = found.Title
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(meta.PosterURL) == "" {
|
|
||||||
meta.PosterURL = found.PosterURL
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(meta.BackdropURL) == "" {
|
|
||||||
meta.BackdropURL = found.BackdropURL
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(meta.Overview) == "" {
|
|
||||||
meta.Overview = found.Overview
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(meta.OriginalName) == "" {
|
|
||||||
meta.OriginalName = found.OriginalName
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(meta.OriginalLanguage) == "" {
|
|
||||||
meta.OriginalLanguage = found.OriginalLanguage
|
|
||||||
}
|
|
||||||
if meta.Year <= 0 && found.Year > 0 {
|
|
||||||
meta.Year = found.Year
|
|
||||||
}
|
|
||||||
if meta.Rating <= 0 && found.Rating > 0 {
|
|
||||||
meta.Rating = found.Rating
|
|
||||||
}
|
|
||||||
if strings.TrimSpace(meta.Genres) == "" {
|
|
||||||
meta.Genres = found.Genres
|
|
||||||
}
|
|
||||||
return meta
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadDisplayTitle(raw string) string {
|
|
||||||
raw = strings.TrimSpace(raw)
|
|
||||||
if raw == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if u, err := url.Parse(raw); err == nil {
|
|
||||||
if dn := strings.TrimSpace(u.Query().Get("dn")); dn != "" {
|
|
||||||
if decoded, err := url.QueryUnescape(dn); err == nil && strings.TrimSpace(decoded) != "" {
|
|
||||||
return strings.TrimSpace(decoded)
|
|
||||||
}
|
|
||||||
return dn
|
|
||||||
}
|
|
||||||
if u.Host != "" {
|
|
||||||
base := path.Base(u.Path)
|
|
||||||
if base != "." && base != "/" && base != "" {
|
|
||||||
base = strings.TrimSuffix(base, path.Ext(base))
|
|
||||||
return strings.TrimSpace(base)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return raw
|
|
||||||
}
|
|
||||||
|
|
||||||
type displayMetadata struct {
|
type displayMetadata struct {
|
||||||
Source string
|
Source string
|
||||||
Title string
|
Title string
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||||
|
"github.com/ShukeBta/MediaStationGo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func enrichAndPersistDownloadRows(ctx context.Context, svc *service.Container, rows []model.DownloadTask) {
|
||||||
|
for i := range rows {
|
||||||
|
before := rows[i]
|
||||||
|
enrichDownloadRow(ctx, svc, &rows[i])
|
||||||
|
updates := downloadTaskMetadataUpdates(before, rows[i])
|
||||||
|
if len(updates) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := svc.Repo.DB.WithContext(ctx).Model(&model.DownloadTask{}).Where("id = ?", rows[i].ID).Updates(updates).Error; err != nil {
|
||||||
|
svc.Log.Debug("download artwork backfill failed", zap.String("id", rows[i].ID), zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enrichDownloadRow(ctx context.Context, svc *service.Container, row *model.DownloadTask) {
|
||||||
|
if strings.TrimSpace(row.Title) == "" {
|
||||||
|
row.Title = downloadDisplayTitle(row.URL)
|
||||||
|
}
|
||||||
|
if !downloadTaskNeedsMetadata(*row) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
meta := enrichDownloadTaskMeta(ctx, svc, service.DownloadTaskMeta{
|
||||||
|
Title: row.Title,
|
||||||
|
PosterURL: row.PosterURL,
|
||||||
|
BackdropURL: row.BackdropURL,
|
||||||
|
Overview: row.Overview,
|
||||||
|
OriginalName: row.OriginalName,
|
||||||
|
OriginalLanguage: row.OriginalLanguage,
|
||||||
|
Year: row.Year,
|
||||||
|
Rating: row.Rating,
|
||||||
|
Genres: row.Genres,
|
||||||
|
}, firstNonEmptyString(row.Title, row.URL), row.MediaType)
|
||||||
|
row.Title = firstNonEmptyString(row.Title, meta.Title)
|
||||||
|
row.PosterURL = meta.PosterURL
|
||||||
|
row.BackdropURL = meta.BackdropURL
|
||||||
|
row.Overview = meta.Overview
|
||||||
|
row.OriginalName = meta.OriginalName
|
||||||
|
row.OriginalLanguage = meta.OriginalLanguage
|
||||||
|
row.Year = meta.Year
|
||||||
|
row.Rating = meta.Rating
|
||||||
|
row.Genres = meta.Genres
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadTaskNeedsMetadata(row model.DownloadTask) bool {
|
||||||
|
return strings.TrimSpace(row.PosterURL) == "" || strings.TrimSpace(row.BackdropURL) == "" ||
|
||||||
|
strings.TrimSpace(row.Overview) == "" || row.Year <= 0 || row.Rating <= 0 ||
|
||||||
|
strings.TrimSpace(row.Genres) == "" || strings.TrimSpace(row.OriginalName) == "" ||
|
||||||
|
strings.TrimSpace(row.OriginalLanguage) == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadTaskMetadataUpdates(before, after model.DownloadTask) map[string]any {
|
||||||
|
updates := map[string]any{}
|
||||||
|
if before.Title != after.Title {
|
||||||
|
updates["title"] = after.Title
|
||||||
|
}
|
||||||
|
if before.PosterURL != after.PosterURL {
|
||||||
|
updates["poster_url"] = after.PosterURL
|
||||||
|
}
|
||||||
|
if before.BackdropURL != after.BackdropURL {
|
||||||
|
updates["backdrop_url"] = after.BackdropURL
|
||||||
|
}
|
||||||
|
if before.Overview != after.Overview {
|
||||||
|
updates["overview"] = after.Overview
|
||||||
|
}
|
||||||
|
if before.OriginalName != after.OriginalName {
|
||||||
|
updates["original_name"] = after.OriginalName
|
||||||
|
}
|
||||||
|
if before.OriginalLanguage != after.OriginalLanguage {
|
||||||
|
updates["original_language"] = after.OriginalLanguage
|
||||||
|
}
|
||||||
|
if before.Year != after.Year {
|
||||||
|
updates["year"] = after.Year
|
||||||
|
}
|
||||||
|
if before.Rating != after.Rating {
|
||||||
|
updates["rating"] = after.Rating
|
||||||
|
}
|
||||||
|
if before.Genres != after.Genres {
|
||||||
|
updates["genres"] = after.Genres
|
||||||
|
}
|
||||||
|
return updates
|
||||||
|
}
|
||||||
|
|
||||||
|
func enrichDownloadTorrentViews(ctx context.Context, svc *service.Container, views []service.DownloadTorrentView) {
|
||||||
|
cache := map[string]displayMetadata{}
|
||||||
|
for i := range views {
|
||||||
|
if strings.TrimSpace(views[i].PosterURL) != "" && strings.TrimSpace(views[i].BackdropURL) != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
query := firstNonEmptyString(views[i].Title, views[i].Name)
|
||||||
|
cacheKey, _ := metadataSearchQuery(query)
|
||||||
|
meta, ok := cache[cacheKey]
|
||||||
|
if !ok {
|
||||||
|
meta = lookupDisplayMetadata(ctx, svc, query, "", "")
|
||||||
|
cache[cacheKey] = meta
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(views[i].PosterURL) == "" {
|
||||||
|
views[i].PosterURL = meta.PosterURL
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(views[i].BackdropURL) == "" {
|
||||||
|
views[i].BackdropURL = meta.BackdropURL
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(views[i].Overview) == "" {
|
||||||
|
views[i].Overview = meta.Overview
|
||||||
|
}
|
||||||
|
if (views[i].Title == "" || views[i].Title == views[i].Name) && meta.Title != "" {
|
||||||
|
views[i].Title = meta.Title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enrichDownloadTaskMeta(ctx context.Context, svc *service.Container, meta service.DownloadTaskMeta, fallbackTitle, mediaType string) service.DownloadTaskMeta {
|
||||||
|
if strings.TrimSpace(meta.Title) == "" {
|
||||||
|
meta.Title = strings.TrimSpace(downloadDisplayTitle(fallbackTitle))
|
||||||
|
}
|
||||||
|
// 仅当展示字段都齐时才跳过查询(含媒体富通知所需的年份/评分/类型/原始信息)。
|
||||||
|
if strings.TrimSpace(meta.PosterURL) != "" && strings.TrimSpace(meta.BackdropURL) != "" &&
|
||||||
|
strings.TrimSpace(meta.Overview) != "" && meta.Year > 0 && meta.Rating > 0 &&
|
||||||
|
strings.TrimSpace(meta.Genres) != "" && strings.TrimSpace(meta.OriginalName) != "" &&
|
||||||
|
strings.TrimSpace(meta.OriginalLanguage) != "" {
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
found := lookupDisplayMetadata(ctx, svc, meta.Title, fallbackTitle, mediaType)
|
||||||
|
if strings.TrimSpace(meta.Title) == "" {
|
||||||
|
meta.Title = found.Title
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(meta.PosterURL) == "" {
|
||||||
|
meta.PosterURL = found.PosterURL
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(meta.BackdropURL) == "" {
|
||||||
|
meta.BackdropURL = found.BackdropURL
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(meta.Overview) == "" {
|
||||||
|
meta.Overview = found.Overview
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(meta.OriginalName) == "" {
|
||||||
|
meta.OriginalName = found.OriginalName
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(meta.OriginalLanguage) == "" {
|
||||||
|
meta.OriginalLanguage = found.OriginalLanguage
|
||||||
|
}
|
||||||
|
if meta.Year <= 0 && found.Year > 0 {
|
||||||
|
meta.Year = found.Year
|
||||||
|
}
|
||||||
|
if meta.Rating <= 0 && found.Rating > 0 {
|
||||||
|
meta.Rating = found.Rating
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(meta.Genres) == "" {
|
||||||
|
meta.Genres = found.Genres
|
||||||
|
}
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadDisplayTitle(raw string) string {
|
||||||
|
raw = strings.TrimSpace(raw)
|
||||||
|
if raw == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if u, err := url.Parse(raw); err == nil {
|
||||||
|
if dn := strings.TrimSpace(u.Query().Get("dn")); dn != "" {
|
||||||
|
if decoded, err := url.QueryUnescape(dn); err == nil && strings.TrimSpace(decoded) != "" {
|
||||||
|
return strings.TrimSpace(decoded)
|
||||||
|
}
|
||||||
|
return dn
|
||||||
|
}
|
||||||
|
if u.Host != "" {
|
||||||
|
base := path.Base(u.Path)
|
||||||
|
if base != "." && base != "/" && base != "" {
|
||||||
|
base = strings.TrimSuffix(base, path.Ext(base))
|
||||||
|
return strings.TrimSpace(base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raw
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||||
|
"github.com/ShukeBta/MediaStationGo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func enrichSubscriptionArtwork(ctx context.Context, svc *service.Container, sub *model.Subscription) {
|
||||||
|
if svc == nil || sub == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 已有图片且媒体展示字段也齐全时才跳过;否则仍需查一次补 年份/评分/类型 等
|
||||||
|
// 富通知字段(老订阅只存了 poster/overview 的情况)。
|
||||||
|
if strings.TrimSpace(sub.PosterURL) != "" && strings.TrimSpace(sub.BackdropURL) != "" &&
|
||||||
|
sub.Year > 0 && sub.Rating > 0 && strings.TrimSpace(sub.Genres) != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
meta := lookupDisplayMetadata(ctx, svc, sub.Name, sub.Filter, sub.MediaType)
|
||||||
|
if meta.Title == "" && meta.PosterURL == "" && meta.BackdropURL == "" && meta.Overview == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(sub.Source) == "" {
|
||||||
|
sub.Source = meta.Source
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(sub.PosterURL) == "" {
|
||||||
|
sub.PosterURL = meta.PosterURL
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(sub.BackdropURL) == "" {
|
||||||
|
sub.BackdropURL = meta.BackdropURL
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(sub.Overview) == "" {
|
||||||
|
sub.Overview = meta.Overview
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(sub.OriginalName) == "" {
|
||||||
|
sub.OriginalName = meta.OriginalName
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(sub.OriginalLanguage) == "" {
|
||||||
|
sub.OriginalLanguage = meta.OriginalLanguage
|
||||||
|
}
|
||||||
|
if sub.Year <= 0 && meta.Year > 0 {
|
||||||
|
sub.Year = meta.Year
|
||||||
|
}
|
||||||
|
if sub.Rating <= 0 && meta.Rating > 0 {
|
||||||
|
sub.Rating = meta.Rating
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(sub.Genres) == "" {
|
||||||
|
sub.Genres = meta.Genres
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enrichAndPersistSubscriptions(ctx context.Context, svc *service.Container, items []model.Subscription) {
|
||||||
|
for i := range items {
|
||||||
|
before := items[i]
|
||||||
|
enrichSubscriptionArtwork(ctx, svc, &items[i])
|
||||||
|
updates := subscriptionMetadataUpdates(before, items[i])
|
||||||
|
if len(updates) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := svc.Repo.DB.WithContext(ctx).Model(&model.Subscription{}).Where("id = ?", items[i].ID).Updates(updates).Error; err != nil {
|
||||||
|
svc.Log.Debug("subscription artwork backfill failed", zap.String("id", items[i].ID), zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func subscriptionMetadataUpdates(before, after model.Subscription) map[string]any {
|
||||||
|
updates := map[string]any{}
|
||||||
|
if before.Source != after.Source {
|
||||||
|
updates["source"] = after.Source
|
||||||
|
}
|
||||||
|
if before.PosterURL != after.PosterURL {
|
||||||
|
updates["poster_url"] = after.PosterURL
|
||||||
|
}
|
||||||
|
if before.BackdropURL != after.BackdropURL {
|
||||||
|
updates["backdrop_url"] = after.BackdropURL
|
||||||
|
}
|
||||||
|
if before.Overview != after.Overview {
|
||||||
|
updates["overview"] = after.Overview
|
||||||
|
}
|
||||||
|
if before.OriginalName != after.OriginalName {
|
||||||
|
updates["original_name"] = after.OriginalName
|
||||||
|
}
|
||||||
|
if before.OriginalLanguage != after.OriginalLanguage {
|
||||||
|
updates["original_language"] = after.OriginalLanguage
|
||||||
|
}
|
||||||
|
if before.Year != after.Year {
|
||||||
|
updates["year"] = after.Year
|
||||||
|
}
|
||||||
|
if before.Rating != after.Rating {
|
||||||
|
updates["rating"] = after.Rating
|
||||||
|
}
|
||||||
|
if before.Genres != after.Genres {
|
||||||
|
updates["genres"] = after.Genres
|
||||||
|
}
|
||||||
|
return updates
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user