This commit is contained in:
2025-12-09 16:33:44 +08:00
parent 345af5e2a3
commit 1d42f5ea50
49 changed files with 12015 additions and 1 deletions

168
search.go Executable file
View File

@@ -0,0 +1,168 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
)
// SearchResult represents a search result with frontend-compatible fields
type SearchResult struct {
Title string `json:"title"`
Artist string `json:"artist"`
URL string `json:"url"` // For frontend compatibility
Link string `json:"link"` // Alternative field
LyricURL string `json:"lyric_url"` // Lyrics URL
CoverURL string `json:"cover_url"`
Duration int `json:"duration"` // Duration in seconds
}
// HandleSearch handles music search requests
func HandleSearch(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
query := r.URL.Query().Get("query")
fmt.Printf("[API] Search request: query=%s\n", query)
if query == "" {
json.NewEncoder(w).Encode([]SearchResult{})
return
}
// Search from multiple sources
items := searchFromAllSources(query)
// Convert to frontend-compatible format
results := make([]SearchResult, len(items))
for i, item := range items {
// Use AudioFullURL as primary, fallback to AudioURL
audioURL := item.AudioFullURL
if audioURL == "" {
audioURL = item.AudioURL
}
results[i] = SearchResult{
Title: item.Title,
Artist: item.Artist,
URL: audioURL,
Link: audioURL,
LyricURL: item.LyricURL,
CoverURL: item.CoverURL,
Duration: item.Duration,
}
fmt.Printf("[API] Result %d: title=%s, artist=%s, url=%s, lyric=%s\n", i+1, item.Title, item.Artist, audioURL, item.LyricURL)
}
fmt.Printf("[API] Search completed: %d results found\n", len(results))
json.NewEncoder(w).Encode(results)
}
// searchFromAllSources searches music from all available sources
func searchFromAllSources(query string) []MusicItem {
var results []MusicItem
// Source 1: Search from sources.json (local config)
sources := readSources()
for _, source := range sources {
if containsIgnoreCase(source.Title, query) || containsIgnoreCase(source.Artist, query) {
item := MusicItem{
Title: source.Title,
Artist: source.Artist,
AudioURL: source.AudioURL,
AudioFullURL: source.AudioFullURL,
M3U8URL: source.M3U8URL,
LyricURL: source.LyricURL,
CoverURL: source.CoverURL,
Duration: source.Duration,
FromCache: false,
}
results = append(results, item)
// Limit results to prevent too many items
if len(results) >= 20 {
break
}
}
}
// Source 2: Search from local files
localItems := searchLocalMusic(query)
results = append(results, localItems...)
// If no local results, try API search
if len(results) == 0 {
apiResults := searchFromAPI(query)
results = append(results, apiResults...)
}
// Limit total results
if len(results) > 20 {
results = results[:20]
}
return results
}
// searchLocalMusic searches music from local files
func searchLocalMusic(query string) []MusicItem {
// This function would search from local music files
// For now, return empty array
// TODO: Implement local file search
return []MusicItem{}
}
// searchFromAPI searches music from external APIs
func searchFromAPI(query string) []MusicItem {
var results []MusicItem
// Try different sources in priority order: 酷我 > 网易云 > 咪咕 > 百度
// 酷我的搜索结果更准确,原唱版本更容易排在前面
sources := []string{"kuwo", "netease", "migu", "baidu"}
for _, source := range sources {
item := YuafengAPIResponseHandler(source, query, "")
if item.Title != "" {
results = append(results, item)
break // Return first successful result
}
}
return results
}
// containsIgnoreCase checks if a string contains substring (case insensitive)
func containsIgnoreCase(s, substr string) bool {
s = toLower(s)
substr = toLower(substr)
return contains(s, substr)
}
func toLower(s string) string {
var result []rune
for _, r := range s {
if r >= 'A' && r <= 'Z' {
r += 32
}
result = append(result, r)
}
return string(result)
}
func contains(s, substr string) bool {
return len(substr) == 0 || indexString(s, substr) >= 0
}
func indexString(s, substr string) int {
n := len(substr)
if n == 0 {
return 0
}
for i := 0; i+n <= len(s); i++ {
if s[i:i+n] == substr {
return i
}
}
return -1
}