mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 11:36:36 +08:00
fix: 使 TMDbProvider 支持从数据库读取 API Key
- 修改 TMDbProvider 添加 apiConfig *APIConfigService 字段 - NewTMDbProvider 接受 apiConfig 参数(可为 nil) - 添加 resolveAPIKey(ctx) 方法:优先从 config 读取,回退到数据库 - 添加 resolveBaseURL(ctx) 方法:解析基础 URL - 修改 SearchMovie 使用 resolveAPIKey / resolveBaseURL - 修改 DiscoverService.Fetch 使用 resolveAPIKey / resolveBaseURL - 调整 service.go 初始化顺序,确保 tmdb 在 scraper 之前初始化 - 构建验证通过
This commit is contained in:
@@ -53,14 +53,22 @@ func (d *DiscoverService) fetch(ctx context.Context, path string) ([]Match, erro
|
||||
// It paginates page=1 only — that's all the home page needs and it
|
||||
// keeps us under TMDb's 50 rps limit.
|
||||
func (d *DiscoverService) Fetch(ctx context.Context, path string) ([]Match, error) {
|
||||
if d.tmdb == nil || !d.tmdb.Enabled() {
|
||||
if d.tmdb == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Resolve API key from config or database
|
||||
apiKey := d.tmdb.resolveAPIKey(ctx)
|
||||
if apiKey == "" {
|
||||
return nil, nil
|
||||
}
|
||||
base := d.tmdb.resolveBaseURL(ctx)
|
||||
|
||||
q := url.Values{}
|
||||
q.Set("api_key", d.tmdb.cfg.Secrets.TMDbAPIKey)
|
||||
q.Set("api_key", apiKey)
|
||||
q.Set("language", "zh-CN")
|
||||
q.Set("page", "1")
|
||||
u := d.tmdb.base + path + "?" + q.Encode()
|
||||
u := base + path + "?" + q.Encode()
|
||||
|
||||
type result struct {
|
||||
ID int `json:"id"`
|
||||
|
||||
@@ -81,7 +81,9 @@ func New(cfg *config.Config, log *zap.Logger, repos *repository.Container) *Cont
|
||||
go sseHub.Run()
|
||||
|
||||
probe := NewFFprobeService(cfg, log)
|
||||
tmdb := NewTMDbProvider(cfg, log)
|
||||
crypto := NewCryptoService(cfg.Secrets.JWTSecret, log)
|
||||
apiConfig := NewAPIConfigService(log, repos, crypto)
|
||||
tmdb := NewTMDbProvider(cfg, log, apiConfig)
|
||||
bangumi := NewBangumiProvider(cfg, log)
|
||||
thetvdb := NewTheTVDBProvider(cfg, log)
|
||||
fanart := NewFanartProvider(cfg, log)
|
||||
@@ -94,8 +96,6 @@ func New(cfg *config.Config, log *zap.Logger, repos *repository.Container) *Cont
|
||||
watcher := NewWatcherService(log, repos, scanner)
|
||||
nfo := NewNFOService(log, repos)
|
||||
ai := NewAIService(cfg, log)
|
||||
crypto := NewCryptoService(cfg.Secrets.JWTSecret, log)
|
||||
apiConfig := NewAPIConfigService(log, repos, crypto)
|
||||
duplicate := NewDuplicateService(log, repos, hub)
|
||||
filemanager := NewFileManagerService(cfg, log, repos)
|
||||
dlna := NewDLNAService(log)
|
||||
|
||||
+69
-17
@@ -31,16 +31,19 @@ import (
|
||||
|
||||
// TMDbProvider talks to https://api.themoviedb.org/3.
|
||||
type TMDbProvider struct {
|
||||
cfg *config.Config
|
||||
log *zap.Logger
|
||||
client *http.Client
|
||||
base string
|
||||
imgCDN string
|
||||
cfg *config.Config
|
||||
log *zap.Logger
|
||||
client *http.Client
|
||||
base string
|
||||
imgCDN string
|
||||
apiConfig *APIConfigService
|
||||
}
|
||||
|
||||
// NewTMDbProvider is the constructor. APIBase / image CDN can be overridden
|
||||
// via secrets.tmdb_api_proxy + tmdb_image_proxy for users behind GFW.
|
||||
func NewTMDbProvider(cfg *config.Config, log *zap.Logger) *TMDbProvider {
|
||||
// apiConfig is optional; when non-nil, the provider will also check the
|
||||
// api_configs table for TMDB API key.
|
||||
func NewTMDbProvider(cfg *config.Config, log *zap.Logger, apiConfig *APIConfigService) *TMDbProvider {
|
||||
base := cfg.Secrets.TMDbAPIProxy
|
||||
if base == "" {
|
||||
base = "https://api.themoviedb.org/3"
|
||||
@@ -50,16 +53,61 @@ func NewTMDbProvider(cfg *config.Config, log *zap.Logger) *TMDbProvider {
|
||||
img = "https://image.tmdb.org/t/p"
|
||||
}
|
||||
return &TMDbProvider{
|
||||
cfg: cfg,
|
||||
log: log,
|
||||
base: base,
|
||||
imgCDN: img,
|
||||
client: &http.Client{Timeout: 15 * time.Second},
|
||||
cfg: cfg,
|
||||
log: log,
|
||||
apiConfig: apiConfig,
|
||||
base: base,
|
||||
imgCDN: img,
|
||||
client: &http.Client{Timeout: 15 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled reports whether the operator has supplied an API key.
|
||||
func (t *TMDbProvider) Enabled() bool { return t.cfg.Secrets.TMDbAPIKey != "" }
|
||||
// It checks both the config file and the database (via apiConfig).
|
||||
func (t *TMDbProvider) Enabled() bool {
|
||||
// Fast path: check config
|
||||
if t.cfg.Secrets.TMDbAPIKey != "" {
|
||||
return true
|
||||
}
|
||||
// Secondary check: if we have apiConfig, the key might be in the database
|
||||
// We can't query the database here (no ctx), so we rely on the caller
|
||||
// to check properly before making API calls.
|
||||
// The actual key resolution happens in resolveAPIKey(ctx).
|
||||
return t.apiConfig != nil
|
||||
}
|
||||
|
||||
// resolveAPIKey returns the TMDb API key, checking config first, then database.
|
||||
func (t *TMDbProvider) resolveAPIKey(ctx context.Context) string {
|
||||
// Check config first (fast path)
|
||||
if t.cfg.Secrets.TMDbAPIKey != "" {
|
||||
return t.cfg.Secrets.TMDbAPIKey
|
||||
}
|
||||
// Fall back to database
|
||||
if t.apiConfig != nil {
|
||||
resolved, err := t.apiConfig.Resolve(ctx, "tmdb")
|
||||
if err == nil && resolved.APIKey != "" {
|
||||
return resolved.APIKey
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// resolveBaseURL returns the TMDb base URL, checking config first, then database.
|
||||
func (t *TMDbProvider) resolveBaseURL(ctx context.Context) string {
|
||||
// Check config first
|
||||
base := t.cfg.Secrets.TMDbAPIProxy
|
||||
if base == "" {
|
||||
base = "https://api.themoviedb.org/3"
|
||||
}
|
||||
// Override from database if available
|
||||
if t.apiConfig != nil {
|
||||
resolved, err := t.apiConfig.Resolve(ctx, "tmdb")
|
||||
if err == nil && resolved.BaseURL != "" {
|
||||
base = resolved.BaseURL
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// Match describes a successful metadata match. The same struct is reused
|
||||
// across providers; provider-specific IDs sit side-by-side so the scraper
|
||||
@@ -78,22 +126,26 @@ type Match struct {
|
||||
// SearchMovie issues `/search/movie` and returns the best match, or nil
|
||||
// when no result is found. The `year` argument is optional (0 = any).
|
||||
func (t *TMDbProvider) SearchMovie(ctx context.Context, query string, year int) (*Match, error) {
|
||||
if !t.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
if query == "" {
|
||||
return nil, errors.New("empty query")
|
||||
}
|
||||
|
||||
// Resolve API key from config or database
|
||||
apiKey := t.resolveAPIKey(ctx)
|
||||
if apiKey == "" {
|
||||
return nil, nil
|
||||
}
|
||||
base := t.resolveBaseURL(ctx)
|
||||
|
||||
q := url.Values{}
|
||||
q.Set("api_key", t.cfg.Secrets.TMDbAPIKey)
|
||||
q.Set("api_key", apiKey)
|
||||
q.Set("query", query)
|
||||
q.Set("language", "zh-CN")
|
||||
q.Set("include_adult", "false")
|
||||
if year > 0 {
|
||||
q.Set("year", fmt.Sprintf("%d", year))
|
||||
}
|
||||
u := t.base + "/search/movie?" + q.Encode()
|
||||
u := base + "/search/movie?" + q.Encode()
|
||||
|
||||
type result struct {
|
||||
ID int `json:"id"`
|
||||
|
||||
Reference in New Issue
Block a user