feat(app): add internal components batch 2
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user