125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
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
|
|
}
|