179 lines
4.1 KiB
Go
179 lines
4.1 KiB
Go
|
// music.go - Central music search handler
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type MusicSearchEngine struct {
|
||
|
Name string
|
||
|
Func func(query string, page int) ([]MusicResult, error)
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
musicSearchEngines []MusicSearchEngine
|
||
|
cacheMutex = &sync.Mutex{}
|
||
|
)
|
||
|
|
||
|
var allMusicSearchEngines = []MusicSearchEngine{
|
||
|
{Name: "SoundCloud", Func: SearchSoundCloud},
|
||
|
{Name: "YouTube", Func: SearchMusicViaPiped},
|
||
|
{Name: "Bandcamp", Func: SearchBandcamp},
|
||
|
//{Name: "Spotify", Func: SearchSpotify},
|
||
|
}
|
||
|
|
||
|
func initMusicEngines() {
|
||
|
// Initialize with all engines if no specific config
|
||
|
musicSearchEngines = allMusicSearchEngines
|
||
|
}
|
||
|
|
||
|
func handleMusicSearch(w http.ResponseWriter, settings UserSettings, query string, page int) {
|
||
|
start := time.Now()
|
||
|
|
||
|
cacheKey := CacheKey{
|
||
|
Query: query,
|
||
|
Page: page,
|
||
|
Type: "music",
|
||
|
Lang: settings.SearchLanguage,
|
||
|
Safe: settings.SafeSearch == "active",
|
||
|
}
|
||
|
|
||
|
var results []MusicResult
|
||
|
|
||
|
if cached, found := resultsCache.Get(cacheKey); found {
|
||
|
if musicResults, ok := convertCacheToMusicResults(cached); ok {
|
||
|
results = musicResults
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if len(results) == 0 {
|
||
|
results = fetchMusicResults(query, page)
|
||
|
if len(results) > 0 {
|
||
|
resultsCache.Set(cacheKey, convertMusicResultsToCache(results))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
go prefetchMusicPages(query, page)
|
||
|
|
||
|
elapsed := time.Since(start) // Calculate duration
|
||
|
fetched := fmt.Sprintf("%.2f %s", elapsed.Seconds(), Translate("seconds"))
|
||
|
|
||
|
data := map[string]interface{}{
|
||
|
"Results": results,
|
||
|
"Query": query,
|
||
|
"Page": page,
|
||
|
"HasPrevPage": page > 1,
|
||
|
"HasNextPage": len(results) >= 10, // Default page size
|
||
|
"MusicServices": getMusicServiceNames(),
|
||
|
"CurrentService": "all", // Default service
|
||
|
"Theme": settings.Theme,
|
||
|
"IsThemeDark": settings.IsThemeDark,
|
||
|
"Trans": Translate,
|
||
|
"Fetched": fetched,
|
||
|
}
|
||
|
|
||
|
renderTemplate(w, "music.html", data)
|
||
|
}
|
||
|
|
||
|
// Helper to get music service names
|
||
|
func getMusicServiceNames() []string {
|
||
|
names := make([]string, len(allMusicSearchEngines))
|
||
|
for i, engine := range allMusicSearchEngines {
|
||
|
names[i] = engine.Name
|
||
|
}
|
||
|
return names
|
||
|
}
|
||
|
|
||
|
func convertMusicResultsToCache(results []MusicResult) []SearchResult {
|
||
|
cacheResults := make([]SearchResult, len(results))
|
||
|
for i, r := range results {
|
||
|
cacheResults[i] = r
|
||
|
}
|
||
|
return cacheResults
|
||
|
}
|
||
|
|
||
|
func convertCacheToMusicResults(cached []SearchResult) ([]MusicResult, bool) {
|
||
|
results := make([]MusicResult, 0, len(cached))
|
||
|
for _, item := range cached {
|
||
|
if musicResult, ok := item.(MusicResult); ok {
|
||
|
results = append(results, musicResult)
|
||
|
} else {
|
||
|
return nil, false
|
||
|
}
|
||
|
}
|
||
|
return results, true
|
||
|
}
|
||
|
|
||
|
func fetchMusicResults(query string, page int) []MusicResult {
|
||
|
var results []MusicResult
|
||
|
resultsChan := make(chan []MusicResult, len(musicSearchEngines))
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
for _, engine := range musicSearchEngines {
|
||
|
wg.Add(1)
|
||
|
go func(e MusicSearchEngine) {
|
||
|
defer wg.Done()
|
||
|
res, err := e.Func(query, page)
|
||
|
if err == nil && len(res) > 0 {
|
||
|
resultsChan <- res
|
||
|
}
|
||
|
}(engine)
|
||
|
}
|
||
|
|
||
|
go func() {
|
||
|
wg.Wait()
|
||
|
close(resultsChan)
|
||
|
}()
|
||
|
|
||
|
for res := range resultsChan {
|
||
|
results = append(results, res...)
|
||
|
if len(results) >= 50 { // Default max results
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return deduplicateResults(results)
|
||
|
}
|
||
|
|
||
|
func prefetchMusicPages(query string, currentPage int) {
|
||
|
for _, page := range []int{currentPage - 1, currentPage + 1} {
|
||
|
if page < 1 {
|
||
|
continue
|
||
|
}
|
||
|
cacheKey := CacheKey{
|
||
|
Query: query,
|
||
|
Page: page,
|
||
|
Type: "music",
|
||
|
}
|
||
|
if _, found := resultsCache.Get(cacheKey); !found {
|
||
|
go fetchMusicResults(query, page)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func deduplicateResults(results []MusicResult) []MusicResult {
|
||
|
seen := make(map[string]bool)
|
||
|
var unique []MusicResult
|
||
|
|
||
|
for _, res := range results {
|
||
|
if !seen[res.URL] {
|
||
|
seen[res.URL] = true
|
||
|
unique = append(unique, res)
|
||
|
}
|
||
|
}
|
||
|
return unique
|
||
|
}
|
||
|
|
||
|
// func generatePlayerHTML(result MusicResult) template.HTML {
|
||
|
// if result.IframeSrc != "" {
|
||
|
// return template.HTML(fmt.Sprintf(
|
||
|
// `<iframe width="100%%" height="166" scrolling="no" frameborder="no" src="%s"></iframe>`,
|
||
|
// result.IframeSrc,
|
||
|
// ))
|
||
|
// }
|
||
|
// return template.HTML("")
|
||
|
// }
|