rebase
This commit is contained in:
168
search.go
Executable file
168
search.go
Executable 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
|
||||
}
|
||||
Reference in New Issue
Block a user