109 lines
3.6 KiB
Go
109 lines
3.6 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func (downloader *Downloader) resolveHongguoAppMedia(ctx context.Context, videoID string) (providerMedia, error) {
|
|
if !hongguoNumericID.MatchString(videoID) {
|
|
return providerMedia{}, errors.New("红果视频 ID 无效")
|
|
}
|
|
payload := map[string]any{
|
|
"video_id": videoID, "content_type": 1,
|
|
"biz_param": map[string]any{"need_all_video_definition": true, "video_platform": 3},
|
|
}
|
|
result, err := downloader.hongguoAppRequest(ctx, http.MethodPost, "/novel/player/video_model/v1/", nil, payload)
|
|
if err != nil {
|
|
return providerMedia{}, err
|
|
}
|
|
data := nestedMap(result, "data")
|
|
model, _ := data["video_model"].(map[string]any)
|
|
if encoded, ok := data["video_model"].(string); ok {
|
|
decoder := json.NewDecoder(strings.NewReader(encoded))
|
|
decoder.UseNumber()
|
|
if err := decoder.Decode(&model); err != nil {
|
|
return providerMedia{}, errors.New("红果 App 播放信息格式异常")
|
|
}
|
|
}
|
|
return selectHongguoAppMedia(model)
|
|
}
|
|
|
|
func selectHongguoAppMedia(model map[string]any) (providerMedia, error) {
|
|
variants := anyList(model["video_list"])
|
|
if rows, ok := model["video_list"].(map[string]any); ok && len(variants) == 0 {
|
|
keys := make([]string, 0, len(rows))
|
|
for key := range rows {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, key := range keys {
|
|
variants = append(variants, rows[key])
|
|
}
|
|
}
|
|
duration, _ := strconv.ParseFloat(mapString(model, "video_duration", "duration"), 64)
|
|
var selected providerMedia
|
|
bestQuality := -1
|
|
var keyErr error
|
|
choices := make(map[int]providerMedia)
|
|
scores := make(map[int]int)
|
|
for _, row := range variants {
|
|
variant, _ := row.(map[string]any)
|
|
meta := nestedMap(variant, "video_meta")
|
|
codec := strings.ToLower(mapString(meta, "codec_type"))
|
|
if codec == "bytevc2" || strings.Contains(strings.ToLower(mapString(variant, "gear_des_key")), "bytevc2") {
|
|
continue
|
|
}
|
|
address := mapString(variant, "main_url")
|
|
if len(address) > 8192 || !isProviderHTTPMediaURL(address) {
|
|
continue
|
|
}
|
|
media := providerMedia{URL: address, Referer: "https://novel.snssdk.com/", Duration: time.Duration(duration * float64(time.Second))}
|
|
encryption := nestedMap(variant, "encrypt_info")
|
|
spade := mapString(encryption, "spade_a")
|
|
if spade != "" || encryption["encrypt"] == true || mapString(encryption, "encryption_method") == "cenc-aes-ctr" {
|
|
var err error
|
|
media.CENCKey, err = hongguoContentKey(spade)
|
|
if err != nil {
|
|
keyErr = err
|
|
continue
|
|
}
|
|
}
|
|
height, _ := strconv.Atoi(mapString(meta, "vheight"))
|
|
if definition, err := strconv.Atoi(hongguoQualityNumber.FindString(mapString(meta, "definition"))); err == nil && definition > 0 {
|
|
height = definition
|
|
} else if width, _ := strconv.Atoi(mapString(meta, "vwidth")); width > 0 && (height == 0 || width < height) {
|
|
height = width
|
|
}
|
|
media.Quality = height
|
|
quality := height * 10
|
|
if codec == "h264" || codec == "avc1" {
|
|
quality++
|
|
}
|
|
if previous, exists := scores[height]; !exists || quality > previous {
|
|
choices[height], scores[height] = media, quality
|
|
}
|
|
if selected.URL == "" || quality > bestQuality {
|
|
selected, bestQuality = media, quality
|
|
}
|
|
}
|
|
if selected.URL != "" {
|
|
for _, media := range choices {
|
|
selected.Variants = append(selected.Variants, media)
|
|
}
|
|
sort.Slice(selected.Variants, func(i, j int) bool { return selected.Variants[i].Quality > selected.Variants[j].Quality })
|
|
return selected, nil
|
|
}
|
|
if keyErr != nil {
|
|
return providerMedia{}, fmt.Errorf("红果 App 媒体密钥不可用: %w", keyErr)
|
|
}
|
|
return providerMedia{}, errors.New("红果 App 未返回兼容的媒体,已跳过不支持的编码")
|
|
}
|