mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-26 05:51:18 +02:00
270496b3d5
Replace the substring signal lists in isLyricsProviderUnavailableError with typed errors (errLyricsNotFound / errLyricsServiceUnavailable in the new lyrics_errors.go). Providers now classify failures at the point of origin: HTTP statuses via lyricsHTTPStatusError (429/5xx cooldown), netease API codes and paxsenix proxy failures as explicit unavailable, and every "no lyrics/songs found" path as typed not-found. Substring matching survives only for error payloads from third-party proxies (rate limit, missing parameters), whose messages genuinely arrive as free text. Two deliberate behavior changes: LyricsPlus 429/5xx responses now cool the provider down (previously unclassified), and proxy payloads saying "not found" are treated as not-found instead of disabling the provider.
101 lines
3.5 KiB
Go
101 lines
3.5 KiB
Go
package gobackend
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// Sentinel classifications for lyrics provider failures. Providers wrap their
|
|
// errors with the typed constructors below at the point where the failure kind
|
|
// is known, so isLyricsProviderUnavailableError can classify with errors.Is
|
|
// instead of parsing error text.
|
|
var (
|
|
errLyricsNotFound = errors.New("lyrics not found")
|
|
errLyricsServiceUnavailable = errors.New("lyrics service unavailable")
|
|
)
|
|
|
|
// lyricsNotFoundError: the provider responded but has nothing for this track
|
|
// (including "nothing to search for" inputs). Never disables the provider.
|
|
type lyricsNotFoundError struct{ err error }
|
|
|
|
func (e *lyricsNotFoundError) Error() string { return e.err.Error() }
|
|
func (e *lyricsNotFoundError) Unwrap() error { return e.err }
|
|
func (e *lyricsNotFoundError) Is(target error) bool { return target == errLyricsNotFound }
|
|
|
|
func lyricsNotFoundErrorf(format string, args ...any) error {
|
|
return &lyricsNotFoundError{err: fmt.Errorf(format, args...)}
|
|
}
|
|
|
|
// lyricsServiceUnavailableError: a provider/API-level failure that should
|
|
// temporarily disable the lyrics source (see markLyricsProviderUnavailable).
|
|
type lyricsServiceUnavailableError struct{ err error }
|
|
|
|
func (e *lyricsServiceUnavailableError) Error() string { return e.err.Error() }
|
|
func (e *lyricsServiceUnavailableError) Unwrap() error { return e.err }
|
|
func (e *lyricsServiceUnavailableError) Is(target error) bool {
|
|
return target == errLyricsServiceUnavailable
|
|
}
|
|
|
|
func lyricsServiceUnavailableErrorf(format string, args ...any) error {
|
|
return &lyricsServiceUnavailableError{err: fmt.Errorf(format, args...)}
|
|
}
|
|
|
|
// lyricsHTTPStatusError classifies an upstream HTTP status at the response
|
|
// site: 429 and 5xx mean the service is temporarily unusable; anything else
|
|
// stays a plain error.
|
|
func lyricsHTTPStatusError(statusCode int, format string, args ...any) error {
|
|
if statusCode == http.StatusTooManyRequests || statusCode >= 500 {
|
|
return lyricsServiceUnavailableErrorf(format, args...)
|
|
}
|
|
return fmt.Errorf(format, args...)
|
|
}
|
|
|
|
// Error payloads from third-party lyrics proxies arrive as free text, often
|
|
// with HTTP 200 (see detectLyricsErrorPayload). Their meaning can only be
|
|
// recognized from the message, so this is the one place lyrics failures are
|
|
// still classified by substring. Errors generated by our own code must use
|
|
// the typed constructors above instead.
|
|
var (
|
|
lyricsPayloadNotFoundSignals = []string{
|
|
"lyrics not found",
|
|
"no lyrics found",
|
|
"no songs found",
|
|
"not found",
|
|
}
|
|
lyricsPayloadUnavailableSignals = []string{
|
|
"rate limit",
|
|
"too many requests",
|
|
"operation too frequent",
|
|
"操作频繁",
|
|
"missing required parameters",
|
|
}
|
|
)
|
|
|
|
func lyricsPayloadIndicatesNotFound(payloadMsg string) bool {
|
|
msg := strings.ToLower(payloadMsg)
|
|
for _, signal := range lyricsPayloadNotFoundSignals {
|
|
if strings.Contains(msg, signal) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// classifyLyricsPayloadError builds a provider error from an upstream error
|
|
// payload, typed by what the payload message describes. statusCode is the
|
|
// HTTP status of the response (0 when it arrived with HTTP 200).
|
|
func classifyLyricsPayloadError(statusCode int, payloadMsg string, format string, args ...any) error {
|
|
if lyricsPayloadIndicatesNotFound(payloadMsg) {
|
|
return lyricsNotFoundErrorf(format, args...)
|
|
}
|
|
msg := strings.ToLower(payloadMsg)
|
|
for _, signal := range lyricsPayloadUnavailableSignals {
|
|
if strings.Contains(msg, signal) {
|
|
return lyricsServiceUnavailableErrorf(format, args...)
|
|
}
|
|
}
|
|
return lyricsHTTPStatusError(statusCode, format, args...)
|
|
}
|