Skip to content
This repository was archived by the owner on Nov 3, 2025. It is now read-only.

Commit 4af11c8

Browse files
committed
实现直播和搜索建议接口
1 parent 0703fbf commit 4af11c8

5 files changed

Lines changed: 202 additions & 10 deletions

File tree

config/subscription.go

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,28 @@ func FetchSubscription(url string) error {
3838

3939
// 先用 encoding/json 解析以保持键的顺序
4040
var rawConfig struct {
41-
APISites json.RawMessage `json:"api_site"`
41+
APISites json.RawMessage `json:"api_site"`
42+
Lives json.RawMessage `json:"lives"`
4243
}
43-
44+
4445
if err := json.Unmarshal(decodedData, &rawConfig); err != nil {
4546
return fmt.Errorf("failed to unmarshal config JSON: %v", err)
4647
}
47-
48+
4849
// 构建最终的 config
4950
var config models.Config
5051
config.APISites = make(map[string]models.APISite)
5152
config.SiteList = make([]models.APISite, 0)
52-
53+
config.Lives = make([]models.LiveSource, 0)
54+
5355
// 使用 json.Decoder 按顺序解析 api_site 对象
5456
dec := json.NewDecoder(bytes.NewReader(rawConfig.APISites))
55-
57+
5658
// 读取开始的 {
5759
if _, err := dec.Token(); err != nil {
5860
return fmt.Errorf("failed to parse api_site: %v", err)
5961
}
60-
62+
6163
// 按顺序读取每个键值对
6264
for dec.More() {
6365
// 读取键
@@ -66,23 +68,54 @@ func FetchSubscription(url string) error {
6668
return fmt.Errorf("failed to read key: %v", err)
6769
}
6870
key := token.(string)
69-
71+
7072
// 读取值
7173
var site models.APISite
7274
if err := dec.Decode(&site); err != nil {
7375
return fmt.Errorf("failed to decode site: %v", err)
7476
}
75-
77+
7678
site.Key = key
7779
config.APISites[key] = site
7880
config.SiteList = append(config.SiteList, site)
7981
}
80-
82+
8183
if len(config.APISites) == 0 {
8284
return fmt.Errorf("no API sites found in subscription")
8385
}
8486

87+
// 解析 lives 字段(如果存在)
88+
if len(rawConfig.Lives) > 0 {
89+
liveDec := json.NewDecoder(bytes.NewReader(rawConfig.Lives))
90+
91+
// 读取开始的 {
92+
if _, err := liveDec.Token(); err != nil {
93+
return fmt.Errorf("failed to parse lives: %v", err)
94+
}
95+
96+
// 按顺序读取每个键值对
97+
for liveDec.More() {
98+
// 读取键
99+
token, err := liveDec.Token()
100+
if err != nil {
101+
return fmt.Errorf("failed to read live key: %v", err)
102+
}
103+
key := token.(string)
104+
105+
// 读取值
106+
var live models.LiveSource
107+
if err := liveDec.Decode(&live); err != nil {
108+
return fmt.Errorf("failed to decode live source: %v", err)
109+
}
110+
111+
live.Key = key
112+
config.Lives = append(config.Lives, live)
113+
}
114+
115+
log.Printf("Loaded %d live sources", len(config.Lives))
116+
}
117+
85118
GlobalConfig = config
86-
log.Printf("Subscription config loaded successfully. API sites: %d", len(config.APISites))
119+
log.Printf("Subscription config loaded successfully. API sites: %d, Live sources: %d", len(config.APISites), len(config.Lives))
87120
return nil
88121
}

handlers/live.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package handlers
2+
3+
import (
4+
"helios/config"
5+
"net/http"
6+
7+
"github.com/bytedance/sonic"
8+
)
9+
10+
type LiveSourceResponse struct {
11+
Key string `json:"key"`
12+
Name string `json:"name"`
13+
URL string `json:"url"`
14+
EPG string `json:"epg"`
15+
UA string `json:"ua,omitempty"`
16+
From string `json:"from"`
17+
Disabled bool `json:"disabled"`
18+
}
19+
20+
type LiveSourcesAPIResponse struct {
21+
Success bool `json:"success"`
22+
Data []LiveSourceResponse `json:"data"`
23+
}
24+
25+
func LiveSourcesHandler(w http.ResponseWriter, r *http.Request) {
26+
if r.Method != http.MethodGet {
27+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
28+
return
29+
}
30+
31+
sources := make([]LiveSourceResponse, 0, len(config.GlobalConfig.Lives))
32+
for _, live := range config.GlobalConfig.Lives {
33+
sources = append(sources, LiveSourceResponse{
34+
Key: live.Key,
35+
Name: live.Name,
36+
URL: live.Url,
37+
EPG: live.Epg,
38+
UA: live.UA,
39+
From: "config", // 来源为配置文件
40+
Disabled: false, // 默认不禁用
41+
})
42+
}
43+
44+
response := LiveSourcesAPIResponse{
45+
Success: true,
46+
Data: sources,
47+
}
48+
49+
w.Header().Set("Content-Type", "application/json")
50+
sonic.ConfigDefault.NewEncoder(w).Encode(response)
51+
}

handlers/suggestions.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package handlers
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"helios/config"
7+
"helios/lib"
8+
"net/http"
9+
"time"
10+
11+
"github.com/bytedance/sonic"
12+
)
13+
14+
type Suggestion struct {
15+
Text string `json:"text"`
16+
}
17+
18+
type SuggestionsResponse struct {
19+
Suggestions []Suggestion `json:"suggestions"`
20+
}
21+
22+
func SuggestionsHandler(w http.ResponseWriter, r *http.Request) {
23+
if r.Method != http.MethodGet {
24+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
25+
return
26+
}
27+
28+
query := r.URL.Query().Get("q")
29+
30+
// 如果没有查询参数,返回空建议
31+
if query == "" {
32+
w.Header().Set("Content-Type", "application/json")
33+
response := SuggestionsResponse{
34+
Suggestions: []Suggestion{},
35+
}
36+
sonic.ConfigDefault.NewEncoder(w).Encode(response)
37+
return
38+
}
39+
40+
// 获取第一个 API 站点
41+
apiSites := config.GlobalConfig.SiteList
42+
if len(apiSites) == 0 {
43+
w.Header().Set("Content-Type", "application/json")
44+
response := SuggestionsResponse{
45+
Suggestions: []Suggestion{},
46+
}
47+
sonic.ConfigDefault.NewEncoder(w).Encode(response)
48+
return
49+
}
50+
51+
firstSite := apiSites[0]
52+
53+
// 创建带超时的上下文
54+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
55+
defer cancel()
56+
57+
// 使用 channel 来接收结果或超时
58+
resultChan := make(chan []Suggestion, 1)
59+
60+
go func() {
61+
// 从第一个源搜索
62+
results, err := lib.SearchFromAPI(firstSite, query, 1)
63+
if err != nil {
64+
resultChan <- []Suggestion{}
65+
return
66+
}
67+
68+
// 应用黄色过滤
69+
results = lib.FilterYellowContent(results)
70+
71+
// 转换为建议格式
72+
suggestions := make([]Suggestion, 0, len(results))
73+
for _, result := range results {
74+
suggestions = append(suggestions, Suggestion{
75+
Text: result.Title,
76+
})
77+
}
78+
79+
resultChan <- suggestions
80+
}()
81+
82+
var suggestions []Suggestion
83+
select {
84+
case suggestions = <-resultChan:
85+
// 成功获取结果
86+
case <-ctx.Done():
87+
// 超时,返回空结果
88+
fmt.Printf("搜索建议超时\n")
89+
suggestions = []Suggestion{}
90+
}
91+
92+
w.Header().Set("Content-Type", "application/json")
93+
response := SuggestionsResponse{
94+
Suggestions: suggestions,
95+
}
96+
sonic.ConfigDefault.NewEncoder(w).Encode(response)
97+
}

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ func main() {
5050
http.HandleFunc("/api/search", handlers.AuthMiddleware(handlers.SearchHandler))
5151
http.HandleFunc("/api/search/ws", handlers.AuthMiddleware(handlers.SSESearchHandler))
5252
http.HandleFunc("/api/search/resources", handlers.AuthMiddleware(handlers.ResourcesHandler))
53+
http.HandleFunc("/api/search/suggestions", handlers.AuthMiddleware(handlers.SuggestionsHandler))
5354
http.HandleFunc("/api/detail", handlers.AuthMiddleware(handlers.DetailHandler))
5455
http.HandleFunc("/api/favorites", handlers.AuthMiddleware(handlers.FavoritesHandler))
5556
http.HandleFunc("/api/searchhistory", handlers.AuthMiddleware(handlers.SearchHistoryHandler))
5657
http.HandleFunc("/api/playrecords", handlers.AuthMiddleware(handlers.PlayRecordsHandler))
58+
http.HandleFunc("/api/live/sources", handlers.AuthMiddleware(handlers.LiveSourcesHandler))
5759

5860
port := ":8080"
5961
fmt.Printf("Server starting on port %s\n", port)

models/config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package models
33
type Config struct {
44
APISites map[string]APISite `json:"api_site"`
55
SiteList []APISite `json:"site_list"`
6+
Lives []LiveSource `json:"lives"`
67
}
78

89
type APISite struct {
@@ -11,3 +12,11 @@ type APISite struct {
1112
Name string `json:"name"`
1213
Detail string `json:"detail"`
1314
}
15+
16+
type LiveSource struct {
17+
Key string `json:"key"`
18+
Url string `json:"url"`
19+
Name string `json:"name"`
20+
UA string `json:"ua"`
21+
Epg string `json:"epg"`
22+
}

0 commit comments

Comments
 (0)