初始提交:短剧库源码(含转码优化 ultrafast)
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const legacyFrontendURL = "https://d2pypzndaqisk.cloudfront.net"
|
||||
|
||||
var apiFallbackHosts = []string{
|
||||
defaultAPIBase,
|
||||
"https://d18ka9rqpfd3lo.cloudfront.net",
|
||||
"https://d37n0wjehmw08f.cloudfront.net",
|
||||
}
|
||||
|
||||
func (d *Downloader) apiEndpoint(ctx context.Context) (string, error) {
|
||||
d.apiMu.Lock()
|
||||
defer d.apiMu.Unlock()
|
||||
if d.apiBase != "" {
|
||||
return d.apiBase, nil
|
||||
}
|
||||
configured := strings.TrimRight(firstNonEmpty(d.cfg.APIBase, defaultAPIBase), "/")
|
||||
parsed, err := url.Parse(configured)
|
||||
if err != nil || !isProviderHTTPMediaURL(configured) {
|
||||
return "", errors.New("API 地址无效")
|
||||
}
|
||||
if !strings.HasSuffix(strings.ToLower(parsed.Hostname()), ".cloudfront.net") {
|
||||
d.apiBase = configured
|
||||
return configured, nil
|
||||
}
|
||||
candidates := append([]string{configured}, apiFallbackHosts...)
|
||||
seen := map[string]bool{}
|
||||
var lastErr error
|
||||
for pass := 0; pass < 2; pass++ {
|
||||
if pass == 1 {
|
||||
candidates, err = d.discoverAPIHosts(ctx)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
if seen[candidate] {
|
||||
continue
|
||||
}
|
||||
seen[candidate] = true
|
||||
if err := ctx.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := d.probeAPIHost(ctx, candidate); err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
d.apiBase = candidate
|
||||
return candidate, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("黄果原 API 的候选域名均不可用: %w", publicError(lastErr))
|
||||
}
|
||||
|
||||
func (d *Downloader) resetAPIEndpoint(failed string) {
|
||||
d.apiMu.Lock()
|
||||
if d.apiBase == failed {
|
||||
d.apiBase = ""
|
||||
}
|
||||
d.apiMu.Unlock()
|
||||
}
|
||||
|
||||
func (d *Downloader) probeAPIHost(ctx context.Context, host string) error {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, host+"/api/app/ping/check", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Header.Set("User-Agent", userAgent)
|
||||
response, err := d.doCatalogRequestWithTimeout(request, 4*time.Second)
|
||||
if err != nil {
|
||||
return publicError(err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
var envelope struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
if response.StatusCode != http.StatusOK || json.NewDecoder(io.LimitReader(response.Body, 64*1024)).Decode(&envelope) != nil || envelope.Code != 200 {
|
||||
return fmt.Errorf("%s API 健康检查失败 (HTTP %d)", request.URL.Hostname(), response.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Downloader) discoverAPIHosts(ctx context.Context) ([]string, error) {
|
||||
body, err := d.fetchLegacyFrontendScript(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hosts := parseAPIHosts(body); len(hosts) > 0 {
|
||||
return hosts, nil
|
||||
}
|
||||
return nil, errors.New("官网前端未提供可识别的 API 地址,请更新 apiBase 配置")
|
||||
}
|
||||
|
||||
func (d *Downloader) fetchLegacyFrontendScript(ctx context.Context) (string, error) {
|
||||
page, err := d.fetchProviderText(ctx, legacyFrontendURL+"/", legacyFrontendURL+"/")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
scripts := regexp.MustCompile(`(?i)<script\b[^>]*\bsrc=["']([^"']+)["']`).FindAllStringSubmatch(page, -1)
|
||||
for _, script := range scripts {
|
||||
if !strings.Contains(script[1], "/main-") || !strings.HasSuffix(script[1], ".js") {
|
||||
continue
|
||||
}
|
||||
assetURL := resolveProviderURL(legacyFrontendURL+"/", script[1])
|
||||
asset, err := url.Parse(assetURL)
|
||||
if err != nil || asset.Hostname() != mustHost(legacyFrontendURL) {
|
||||
continue
|
||||
}
|
||||
body, err := d.fetchProviderText(ctx, assetURL, legacyFrontendURL+"/")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
return "", errors.New("黄果官网未提供可识别的接口脚本")
|
||||
}
|
||||
|
||||
func parseAPIHosts(script string) []string {
|
||||
matches := regexp.MustCompile(`https://[a-z0-9]+\.cloudfront\.net`).FindAllString(script, -1)
|
||||
seen := map[string]bool{}
|
||||
var hosts []string
|
||||
for _, host := range matches {
|
||||
if host != legacyFrontendURL && !seen[host] {
|
||||
seen[host] = true
|
||||
hosts = append(hosts, host)
|
||||
if len(hosts) == 8 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
Reference in New Issue
Block a user