Search/music.go

179 lines
4.1 KiB
Go
Raw Normal View History

2025-04-18 11:22:42 +02:00
// 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("")
// }