refactor(playback): simplify handlers, http utils, and ranking

This commit is contained in:
2026-04-20 01:42:09 +02:00
parent fab0b8a6fa
commit ffd67338c3
11 changed files with 71 additions and 110 deletions

View File

@@ -203,6 +203,15 @@ func (c *allAnimeClient) GetEpisodes(ctx context.Context, showID string, mode st
return episodes, nil
}
func buildStreamSource(url, sourceType, provider string) StreamSource {
return StreamSource{
URL: url,
Provider: provider,
Type: sourceType,
Referer: allAnimeReferer,
}
}
func (c *allAnimeClient) GetEpisodeSources(ctx context.Context, showID string, episode string, mode string) ([]StreamSource, error) {
graphqlQuery := `query($showId: String!, $translationType: VaildTranslationTypeEnumType!, $episodeString: String!) {
episode(showId: $showId, translationType: $translationType, episodeString: $episodeString) {
@@ -254,12 +263,7 @@ func (c *allAnimeClient) GetEpisodeSources(ctx context.Context, showID string, e
sourceType = detectEmbedType(target)
}
out = append(out, StreamSource{
URL: target,
Provider: ref.Name,
Type: sourceType,
Referer: allAnimeReferer,
})
out = append(out, buildStreamSource(target, sourceType, ref.Name))
continue
}
@@ -274,12 +278,7 @@ func (c *allAnimeClient) GetEpisodeSources(ctx context.Context, showID string, e
sourceType = detectEmbedType(decoded)
}
out = append(out, StreamSource{
URL: decoded,
Provider: ref.Name,
Type: sourceType,
Referer: allAnimeReferer,
})
out = append(out, buildStreamSource(decoded, sourceType, ref.Name))
continue
}

View File

@@ -217,18 +217,6 @@ func (h *Handler) HandleProxy(w http.ResponseWriter, r *http.Request) {
h.proxyUpstream(w, r, targetURL, referer)
}
func (h *Handler) HandleProxyStream(w http.ResponseWriter, r *http.Request) {
h.HandleProxy(w, r)
}
func (h *Handler) HandleProxySegment(w http.ResponseWriter, r *http.Request) {
h.HandleProxy(w, r)
}
func (h *Handler) HandleProxySubtitle(w http.ResponseWriter, r *http.Request) {
h.HandleProxy(w, r)
}
func (h *Handler) HandleSaveProgress(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)

View File

@@ -0,0 +1,25 @@
package playback
import (
"context"
"net/http"
)
func doProxiedRequest(ctx context.Context, client *http.Client, url string, referer string) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", defaultUserAgent)
if referer != "" {
req.Header.Set("Referer", referer)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}

View File

@@ -17,7 +17,7 @@ func (s *Service) SaveProgress(ctx context.Context, userID string, animeID int64
return errors.New("invalid save progress input")
}
txQueries, tx, err := s.beginTx(ctx)
txQueries, tx, err := database.BeginTx(ctx, s.sqlDB)
if err != nil {
return err
}
@@ -84,7 +84,7 @@ func (s *Service) CompleteAnime(ctx context.Context, userID string, animeID int6
return errors.New("invalid complete anime input")
}
txQueries, tx, err := s.beginTx(ctx)
txQueries, tx, err := database.BeginTx(ctx, s.sqlDB)
if err != nil {
return err
}
@@ -142,16 +142,3 @@ func (s *Service) CompleteAnime(ctx context.Context, userID string, animeID int6
return nil
}
func (s *Service) beginTx(ctx context.Context) (*database.Queries, *sql.Tx, error) {
if s.sqlDB == nil {
return nil, nil, errors.New("database unavailable")
}
tx, err := s.sqlDB.BeginTx(ctx, nil)
if err != nil {
return nil, nil, fmt.Errorf("failed to begin transaction: %w", err)
}
return database.New(tx), tx, nil
}

View File

@@ -27,15 +27,7 @@ func newProviderExtractor() *providerExtractor {
func (e *providerExtractor) ExtractVideoLinks(ctx context.Context, providerPath string) ([]StreamSource, error) {
endpoint := e.baseURL + providerPath
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, fmt.Errorf("create provider request: %w", err)
}
req.Header.Set("Referer", e.referer)
req.Header.Set("User-Agent", defaultUserAgent)
resp, err := e.httpClient.Do(req)
resp, err := doProxiedRequest(ctx, e.httpClient, endpoint, e.referer)
if err != nil {
return nil, fmt.Errorf("fetch provider response: %w", err)
}
@@ -133,17 +125,7 @@ func (e *providerExtractor) parseProviderResponse(ctx context.Context, response
}
func (e *providerExtractor) parseM3U8(ctx context.Context, masterURL string, referer string) ([]StreamSource, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, masterURL, nil)
if err != nil {
return nil, err
}
if referer != "" {
req.Header.Set("Referer", referer)
}
req.Header.Set("User-Agent", defaultUserAgent)
resp, err := e.httpClient.Do(req)
resp, err := doProxiedRequest(ctx, e.httpClient, masterURL, referer)
if err != nil {
return nil, err
}

View File

@@ -162,17 +162,17 @@ func (s *Service) issueProxyToken(targetURL string, referer string, scope proxyS
})
}
var proxyTokenTTLs = map[proxyScope]time.Duration{
proxyScopeStream: proxyStreamTokenTTL,
proxyScopeSegment: proxySegmentTokenTTL,
proxyScopeSubtitle: proxySubtitleTokenTTL,
}
func proxyTokenTTL(scope proxyScope) time.Duration {
switch scope {
case proxyScopeStream:
return proxyStreamTokenTTL
case proxyScopeSegment:
return proxySegmentTokenTTL
case proxyScopeSubtitle:
return proxySubtitleTokenTTL
default:
return proxyStreamTokenTTL
if ttl, ok := proxyTokenTTLs[scope]; ok {
return ttl
}
return proxyStreamTokenTTL
}
func (s *Service) resolveProxyToken(ctx context.Context, token string, scope proxyScope) (string, string, error) {

View File

@@ -182,7 +182,7 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, titleCandid
InitialMode: initialMode,
AvailableModes: cloneSlice(baseData.AvailableModes),
ModeSources: clientModeSources,
Segments: cloneSegments(baseData.Segments),
Segments: cloneSlice(baseData.Segments),
}, nil
}
@@ -360,16 +360,10 @@ func cloneModeSources(modeSources map[string]ModeSource) map[string]ModeSource {
cloned[mode] = ModeSource{
URL: source.URL,
Referer: source.Referer,
Subtitles: cloneSubtitleItems(source.Subtitles),
Subtitles: cloneSlice(source.Subtitles),
}
}
return cloned
}
func cloneSubtitleItems(items []SubtitleItem) []SubtitleItem {
return cloneSlice(items)
}
func cloneSegments(segments []SkipSegment) []SkipSegment {
return cloneSlice(segments)
}

View File

@@ -17,13 +17,7 @@ func (s *Service) fetchSkipSegments(ctx context.Context, malID int, episode stri
}
endpoint := fmt.Sprintf("https://api.aniskip.com/v1/skip-times/%s/%s?types=op&types=ed", url.PathEscape(strconv.Itoa(malID)), url.PathEscape(episode))
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil
}
req.Header.Set("User-Agent", defaultUserAgent)
resp, err := s.httpClient.Do(req)
resp, err := doProxiedRequest(ctx, s.httpClient, endpoint, "")
if err != nil {
return nil
}

View File

@@ -30,8 +30,8 @@ func rankSources(sources []StreamSource, quality string) ([]sourceScore, error)
targetQuality := normalizeQuality(quality)
scored := make([]sourceScore, 0, len(filtered))
for _, source := range filtered {
typeScore := sourceTypePriorityFn(source.Type)
providerScore := providerPriorityFn(source.Provider)
typeScore := lookupPriority(sourceTypePriority, source.Type, 200)
providerScore := lookupPriority(providerPriority, source.Provider, 60)
qualityScore := sourceQualityPriority(source.Quality, targetQuality)
refererScore := 0
if source.Referer != "" {
@@ -90,18 +90,11 @@ var sourceQualityDefaults = map[string]int{
"auto": 240,
}
func sourceTypePriorityFn(sourceType string) int {
if p, ok := sourceTypePriority[strings.ToLower(sourceType)]; ok {
func lookupPriority(m map[string]int, key string, fallback int) int {
if p, ok := m[strings.ToLower(key)]; ok {
return p
}
return 200
}
func providerPriorityFn(provider string) int {
if p, ok := providerPriority[strings.ToLower(provider)]; ok {
return p
}
return 60
return fallback
}
func sourceQualityPriority(sourceQuality string, targetQuality string) int {
@@ -155,15 +148,15 @@ func parseQualityValue(rawQuality string) int {
}
func extractDigits(value string) string {
var digits strings.Builder
var digits []byte
for _, char := range value {
if char >= '0' && char <= '9' {
digits.WriteRune(char)
} else if digits.Len() > 0 {
digits = append(digits, byte(char))
} else if len(digits) > 0 {
break
}
}
return digits.String()
return string(digits)
}
func normalizeSourceTypeFromProbe(source StreamSource, contentType string) StreamSource {

View File

@@ -126,12 +126,12 @@ func normalizeMode(raw string) string {
}
func availableModes(modeSources map[string]ModeSource) []string {
preferred := []string{"dub", "sub"}
ordered := make([]string, 0, len(modeSources))
if _, ok := modeSources["dub"]; ok {
ordered = append(ordered, "dub")
}
if _, ok := modeSources["sub"]; ok {
ordered = append(ordered, "sub")
for _, mode := range preferred {
if _, ok := modeSources[mode]; ok {
ordered = append(ordered, mode)
}
}
extra := make([]string, 0)