Cache preferred Piped instance to furde reduce start delay
Some checks failed
Run Integration Tests / test (push) Has been cancelled
Some checks failed
Run Integration Tests / test (push) Has been cancelled
This commit is contained in:
parent
8f31f0b2eb
commit
be973266c6
2 changed files with 120 additions and 16 deletions
|
@ -19,14 +19,13 @@ type MusicAPIResponse struct {
|
|||
|
||||
func SearchMusicViaPiped(query string, page int) ([]MusicResult, error) {
|
||||
var lastError error
|
||||
|
||||
// We will try to use preferred instance
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
for _, instance := range pipedInstances {
|
||||
if disabledInstances[instance] {
|
||||
continue
|
||||
}
|
||||
instance := preferredInstance
|
||||
mu.Unlock()
|
||||
|
||||
if instance != "" && !disabledInstances[instance] {
|
||||
url := fmt.Sprintf(
|
||||
"https://%s/search?q=%s&filter=music_songs&page=%d",
|
||||
instance,
|
||||
|
@ -34,22 +33,51 @@ func SearchMusicViaPiped(query string, page int) ([]MusicResult, error) {
|
|||
page,
|
||||
)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
defer resp.Body.Close()
|
||||
var apiResp MusicAPIResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&apiResp); err == nil {
|
||||
return convertPipedToMusicResults(instance, apiResp), nil
|
||||
}
|
||||
}
|
||||
|
||||
printWarn("Preferred instance %s failed for music, falling back", instance)
|
||||
disableInstance(instance)
|
||||
}
|
||||
|
||||
// 2. Fallback using others
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
for _, inst := range pipedInstances {
|
||||
if disabledInstances[inst] {
|
||||
continue
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(
|
||||
"https://%s/search?q=%s&filter=music_songs&page=%d",
|
||||
inst,
|
||||
url.QueryEscape(query),
|
||||
page,
|
||||
)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
printInfo("Disabling instance %s due to error: %v", instance, err)
|
||||
disabledInstances[instance] = true
|
||||
lastError = fmt.Errorf("request to %s failed: %w", instance, err)
|
||||
printInfo("Disabling instance %s due to error: %v", inst, err)
|
||||
disabledInstances[inst] = true
|
||||
lastError = fmt.Errorf("request to %s failed: %w", inst, err)
|
||||
continue
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
var apiResp MusicAPIResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil {
|
||||
lastError = fmt.Errorf("failed to decode response from %s: %w", instance, err)
|
||||
lastError = fmt.Errorf("failed to decode response from %s: %w", inst, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return convertPipedToMusicResults(instance, apiResp), nil
|
||||
preferredInstance = inst
|
||||
return convertPipedToMusicResults(inst, apiResp), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("all Piped instances failed, last error: %v", lastError)
|
||||
|
|
86
video.go
86
video.go
|
@ -5,6 +5,9 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -12,18 +15,90 @@ import (
|
|||
const retryDuration = 12 * time.Hour // Retry duration for unresponding piped instances
|
||||
|
||||
var (
|
||||
preferredInstance string
|
||||
pipedInstances = []string{}
|
||||
disabledInstances = make(map[string]bool)
|
||||
mu sync.Mutex
|
||||
preferredInstance string
|
||||
pipedInstanceCacheFile string
|
||||
pipedInstances = []string{}
|
||||
disabledInstances = make(map[string]bool)
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
func initPipedInstances() {
|
||||
pipedInstances = config.MetaSearch.Video
|
||||
if config.DriveCacheEnabled {
|
||||
pipedInstanceCacheFile = filepath.Join(config.DriveCache.Path, "piped_instances.txt")
|
||||
|
||||
cached := loadCachedPipedInstances()
|
||||
if len(cached) > 0 {
|
||||
pipedInstances = cached
|
||||
printInfo("Loaded %d cached Piped instances from disk.", len(cached))
|
||||
} else {
|
||||
pipedInstances = config.MetaSearch.Video
|
||||
savePipedInstances(pipedInstances)
|
||||
}
|
||||
|
||||
// load preferred
|
||||
if pref := loadPreferredInstance(); pref != "" {
|
||||
preferredInstance = pref
|
||||
printInfo("Using cached preferred Piped instance: %s", pref)
|
||||
}
|
||||
} else {
|
||||
pipedInstances = config.MetaSearch.Video
|
||||
}
|
||||
|
||||
go checkDisabledInstancesPeriodically()
|
||||
go selectInitialPipedInstance()
|
||||
}
|
||||
|
||||
func savePreferredInstance(instance string) {
|
||||
if pipedInstanceCacheFile == "" {
|
||||
return
|
||||
}
|
||||
path := filepath.Join(filepath.Dir(pipedInstanceCacheFile), "piped_preferred.txt")
|
||||
if err := os.WriteFile(path, []byte(instance), 0644); err != nil {
|
||||
printWarn("Failed to write preferred Piped instance: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadPreferredInstance() string {
|
||||
if pipedInstanceCacheFile == "" {
|
||||
return ""
|
||||
}
|
||||
path := filepath.Join(filepath.Dir(pipedInstanceCacheFile), "piped_preferred.txt")
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
func loadCachedPipedInstances() []string {
|
||||
if pipedInstanceCacheFile == "" {
|
||||
return nil
|
||||
}
|
||||
data, err := os.ReadFile(pipedInstanceCacheFile)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
var out []string
|
||||
for _, l := range lines {
|
||||
trimmed := strings.TrimSpace(l)
|
||||
if trimmed != "" {
|
||||
out = append(out, trimmed)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func savePipedInstances(instances []string) {
|
||||
if pipedInstanceCacheFile == "" {
|
||||
return
|
||||
}
|
||||
content := strings.Join(instances, "\n")
|
||||
if err := os.WriteFile(pipedInstanceCacheFile, []byte(content), 0644); err != nil {
|
||||
printWarn("Failed to write piped instance list to cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// VideoAPIResponse matches the structure of the JSON response from the Piped API
|
||||
type VideoAPIResponse struct {
|
||||
Items []struct {
|
||||
|
@ -174,6 +249,7 @@ func makeHTMLRequest(query, safe, lang string, page int) (*VideoAPIResponse, err
|
|||
|
||||
// Store new preferred instance
|
||||
preferredInstance = inst
|
||||
savePreferredInstance(inst)
|
||||
return &apiResp, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue