feat(app): add internal components batch 2

This commit is contained in:
吴璨
2026-09-30 10:19:54 +08:00
parent a9d79bbe23
commit fce3d91aa0
10 changed files with 2149 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
package app
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/http"
"path/filepath"
"sync"
"sync/atomic"
"time"
)
type Downloader struct {
cfg Config
client *http.Client
providerMu sync.Mutex
providerHosts map[string]string
hongguoOnce sync.Once
hongguo *hongguoAppClient
rankings rankingCache
limiter *requestLimiter
proxyRouter *proxyRouter
ffmpegMu sync.Mutex
ffmpegInstaller *ffmpegInstaller
diagnostics *diagnosticLog
directoryMu sync.Mutex
downloadDirectories map[string]string
downloadGrouping *bool
}
func NewDownloader(cfg Config) *Downloader {
if cfg.OutputDir == "" {
cfg.OutputDir = defaultOutputDir()
}
if cfg.FFmpeg == "" {
cfg.FFmpeg = "ffmpeg"
}
if cfg.Concurrency <= 0 {
cfg.Concurrency = 2
}
if cfg.RequestConcurrency <= 0 {
cfg.RequestConcurrency = 2
}
if cfg.RequestIntervalMS <= 0 {
cfg.RequestIntervalMS = 500
}
loadRuntimeSettings(&cfg)
if absolute, err := filepath.Abs(cfg.dataDirectory()); err == nil {
cfg.dataDir = absolute
}
cfg.outputDirSetting = cfg.OutputDir
if absolute, err := filepath.Abs(cfg.OutputDir); err == nil {
cfg.OutputDir = absolute
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: cfg.InsecureTLS}
transport.MaxIdleConnsPerHost = 8
transport.ResponseHeaderTimeout = 20 * time.Second
router := &proxyRouter{}
router.configure(cfg.ProxyURL)
transport.Proxy = router.proxy
return &Downloader{cfg: cfg, client: &http.Client{Transport: newImageTransport(transport, transport), Timeout: 45 * time.Second}, providerHosts: map[string]string{}, limiter: newRequestLimiter(cfg.RequestConcurrency, time.Duration(cfg.RequestIntervalMS)*time.Millisecond), proxyRouter: router, diagnostics: newDiagnosticLog(cfg.dataDirectory())}
}
func (d *Downloader) DownloadEpisode(ctx context.Context, task Task) error {
return d.DownloadEpisodeWithProgress(ctx, task, nil)
}
func (d *Downloader) DownloadEpisodeWithProgress(ctx context.Context, task Task, callback func(DownloadProgress)) (resultErr error) {
defer func() {
if ctx.Err() == nil {
d.recordTaskFailure("download.failed", task, 0, resultErr)
}
}()
if !isHongguoTask(task) {
return errors.New("此版本仅支持红果剧集")
}
return d.downloadMediaWithProgress(ctx, task, callback)
}
func DownloadTasks(ctx context.Context, d *Downloader, tasks []Task) []Result {
if len(tasks) == 0 {
return nil
}
jobs := make(chan Task)
results := make(chan Result)
var done int64
workers := d.cfg.Concurrency
if workers > len(tasks) {
workers = len(tasks)
}
var wg sync.WaitGroup
for i := 1; i <= workers; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for task := range jobs {
cur := atomic.AddInt64(&done, 1)
fmt.Printf(" [线程%d] [%d/%d] 《%s》 第%d/%d 集\n", id, cur, len(tasks), task.DramaTitle, task.Index, task.Total)
err := d.DownloadEpisode(ctx, task)
if err != nil {
results <- Result{Task: task, OK: false, Err: err.Error()}
} else {
results <- Result{Task: task, OK: true}
}
}
}(i)
}
go func() {
for _, task := range tasks {
jobs <- task
}
close(jobs)
wg.Wait()
close(results)
}()
var out []Result
for r := range results {
out = append(out, r)
}
return out
}