mirror of
https://github.com/fish2018/pansou.git
synced 2025-11-25 03:14:59 +08:00
update
This commit is contained in:
@@ -33,9 +33,9 @@ import (
|
||||
|
||||
// 插件配置参数
|
||||
const (
|
||||
MaxConcurrentUsers = 10 // 最多使用的用户数
|
||||
MaxConcurrentDetails = 50 // 最大并发详情请求数
|
||||
DebugLog = false // 调试日志开关(排查问题时改为true)
|
||||
MaxConcurrentUsers = 10 // 最多使用的用户数
|
||||
MaxConcurrentDetails = 50 // 最大并发详情请求数
|
||||
DebugLog = false // 调试日志开关(排查问题时改为true)
|
||||
)
|
||||
|
||||
// 默认账户配置(可通过Web界面添加更多账户)
|
||||
@@ -382,9 +382,10 @@ const HTMLTemplate = `<!DOCTYPE html>
|
||||
// GyingPlugin 插件结构
|
||||
type GyingPlugin struct {
|
||||
*plugin.BaseAsyncPlugin
|
||||
users sync.Map // 内存缓存:hash -> *User
|
||||
scrapers sync.Map // cloudscraper实例缓存:hash -> *cloudscraper.Scraper
|
||||
mu sync.RWMutex
|
||||
users sync.Map // 内存缓存:hash -> *User
|
||||
scrapers sync.Map // cloudscraper实例缓存:hash -> *cloudscraper.Scraper
|
||||
mu sync.RWMutex
|
||||
searchCache sync.Map // 插件级缓存:关键词->model.PluginSearchResult
|
||||
}
|
||||
|
||||
// User 用户数据结构
|
||||
@@ -419,8 +420,8 @@ type SearchData struct {
|
||||
|
||||
// DetailData 详情接口JSON数据结构
|
||||
type DetailData struct {
|
||||
Code int `json:"code"`
|
||||
WP bool `json:"wp"`
|
||||
Code int `json:"code"`
|
||||
WP bool `json:"wp"`
|
||||
Panlist struct {
|
||||
ID []string `json:"id"`
|
||||
Name []string `json:"name"`
|
||||
@@ -489,41 +490,63 @@ func (p *GyingPlugin) Search(keyword string, ext map[string]interface{}) ([]mode
|
||||
// 2. 有自己的用户会话管理
|
||||
// 3. Service层已经有缓存,无需插件层再次缓存
|
||||
func (p *GyingPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] ========== 开始搜索: %s ==========\n", keyword)
|
||||
}
|
||||
// 解析 ext["refresh"]
|
||||
forceRefresh := false
|
||||
if ext != nil {
|
||||
if v, ok := ext["refresh"]; ok {
|
||||
if b, ok := v.(bool); ok && b {
|
||||
forceRefresh = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 获取所有有效用户
|
||||
users := p.getActiveUsers()
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 找到 %d 个有效用户\n", len(users))
|
||||
}
|
||||
if !forceRefresh {
|
||||
if cacheItem, ok := p.searchCache.Load(keyword); ok {
|
||||
cached := cacheItem.(model.PluginSearchResult)
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 命中插件缓存: %s\n", keyword)
|
||||
}
|
||||
return cached, nil
|
||||
}
|
||||
} else {
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 强制刷新,此次跳过插件缓存,关键词: %s\n", keyword)
|
||||
}
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 没有有效用户,返回空结果\n")
|
||||
}
|
||||
return model.PluginSearchResult{Results: []model.SearchResult{}, IsFinal: true}, nil
|
||||
}
|
||||
|
||||
// 2. 限制用户数量
|
||||
if len(users) > MaxConcurrentUsers {
|
||||
sort.Slice(users, func(i, j int) bool {
|
||||
return users[i].LastAccessAt.After(users[j].LastAccessAt)
|
||||
})
|
||||
users = users[:MaxConcurrentUsers]
|
||||
}
|
||||
|
||||
// 3. 并发执行搜索
|
||||
results := p.executeSearchTasks(users, keyword)
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 搜索完成,获得 %d 条结果\n", len(results))
|
||||
}
|
||||
|
||||
return model.PluginSearchResult{
|
||||
Results: results,
|
||||
IsFinal: true,
|
||||
}, nil
|
||||
// 原有真实抓取逻辑
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] searchWithScraper REAL 执行: %s\n", keyword)
|
||||
}
|
||||
users := p.getActiveUsers()
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 找到 %d 个有效用户\n", len(users))
|
||||
}
|
||||
if len(users) == 0 {
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 没有有效用户,返回空结果\n")
|
||||
}
|
||||
return model.PluginSearchResult{Results: []model.SearchResult{}, IsFinal: true}, nil
|
||||
}
|
||||
if len(users) > MaxConcurrentUsers {
|
||||
sort.Slice(users, func(i, j int) bool {
|
||||
return users[i].LastAccessAt.After(users[j].LastAccessAt)
|
||||
})
|
||||
users = users[:MaxConcurrentUsers]
|
||||
}
|
||||
results := p.executeSearchTasks(users, keyword)
|
||||
if DebugLog {
|
||||
fmt.Printf("[Gying] 搜索完成,获得 %d 条结果\n", len(results))
|
||||
}
|
||||
realResult := model.PluginSearchResult{
|
||||
Results: results,
|
||||
IsFinal: true,
|
||||
}
|
||||
// 写入缓存
|
||||
if len(results) > 0 {
|
||||
p.searchCache.Store(keyword, realResult)
|
||||
}
|
||||
return realResult, nil
|
||||
}
|
||||
|
||||
// ============ 用户管理 ============
|
||||
@@ -800,13 +823,13 @@ func (p *GyingPlugin) handleGetStatus(c *gin.Context, hash string) {
|
||||
}
|
||||
|
||||
respondSuccess(c, "获取成功", gin.H{
|
||||
"hash": hash,
|
||||
"logged_in": loggedIn,
|
||||
"status": user.Status,
|
||||
"username_masked": user.UsernameMasked,
|
||||
"login_time": user.LoginAt.Format("2006-01-02 15:04:05"),
|
||||
"expire_time": user.ExpireAt.Format("2006-01-02 15:04:05"),
|
||||
"expires_in_days": expiresInDays,
|
||||
"hash": hash,
|
||||
"logged_in": loggedIn,
|
||||
"status": user.Status,
|
||||
"username_masked": user.UsernameMasked,
|
||||
"login_time": user.LoginAt.Format("2006-01-02 15:04:05"),
|
||||
"expire_time": user.ExpireAt.Format("2006-01-02 15:04:05"),
|
||||
"expires_in_days": expiresInDays,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1026,9 +1049,9 @@ func (p *GyingPlugin) createScraperWithCookies(cookieStr string) (*cloudscraper.
|
||||
// 创建cloudscraper实例,配置以保护cookies不被刷新
|
||||
scraper, err := cloudscraper.New(
|
||||
cloudscraper.WithSessionConfig(
|
||||
false, // refreshOn403 = false,禁用403时自动刷新
|
||||
365*24*time.Hour, // interval = 1年,基本不刷新
|
||||
0, // maxRetries = 0
|
||||
false, // refreshOn403 = false,禁用403时自动刷新
|
||||
365*24*time.Hour, // interval = 1年,基本不刷新
|
||||
0, // maxRetries = 0
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1060,8 +1083,8 @@ func (p *GyingPlugin) createScraperWithCookies(cookieStr string) (*cloudscraper.
|
||||
|
||||
for name, value := range cookies {
|
||||
cookie := &http.Cookie{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Name: name,
|
||||
Value: value,
|
||||
// 不设置Domain和Path,让cookiejar根据URL自动推导
|
||||
// cookiejar.SetCookies会根据提供的URL自动设置正确的Domain和Path
|
||||
}
|
||||
@@ -1124,9 +1147,9 @@ func parseCookieString(cookieStr string) map[string]string {
|
||||
// doLogin 执行登录,返回scraper实例和cookie字符串
|
||||
//
|
||||
// 登录流程(3步):
|
||||
// 1. GET登录页 (https://www.gying.net/user/login/) → 获取PHPSESSID
|
||||
// 2. POST登录 (https://www.gying.net/user/login) → 获取BT_auth、BT_cookietime等认证cookies
|
||||
// 3. GET详情页 (https://www.gying.net/mv/wkMn) → 触发防爬cookies (vrg_sc、vrg_go等)
|
||||
// 1. GET登录页 (https://www.gying.net/user/login/) → 获取PHPSESSID
|
||||
// 2. POST登录 (https://www.gying.net/user/login) → 获取BT_auth、BT_cookietime等认证cookies
|
||||
// 3. GET详情页 (https://www.gying.net/mv/wkMn) → 触发防爬cookies (vrg_sc、vrg_go等)
|
||||
//
|
||||
// 返回: (*cloudscraper.Scraper, cookie字符串, error)
|
||||
func (p *GyingPlugin) doLogin(username, password string) (*cloudscraper.Scraper, string, error) {
|
||||
@@ -1140,9 +1163,9 @@ func (p *GyingPlugin) doLogin(username, password string) (*cloudscraper.Scraper,
|
||||
// 关键配置:禁用403自动刷新,防止cookie被清空
|
||||
scraper, err := cloudscraper.New(
|
||||
cloudscraper.WithSessionConfig(
|
||||
false, // refreshOn403 = false,禁用403时自动刷新(重要!)
|
||||
365*24*time.Hour, // interval = 1年,基本不刷新
|
||||
0, // maxRetries = 0
|
||||
false, // refreshOn403 = false,禁用403时自动刷新(重要!)
|
||||
365*24*time.Hour, // interval = 1年,基本不刷新
|
||||
0, // maxRetries = 0
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -2232,4 +2255,3 @@ func (p *GyingPlugin) markInactiveUsers() int {
|
||||
|
||||
return markedCount
|
||||
}
|
||||
|
||||
|
||||
@@ -1227,6 +1227,11 @@ func (s *SearchService) searchPlugins(keyword string, plugins []string, forceRef
|
||||
ext = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// 关键:将forceRefresh同步到插件ext["refresh"]
|
||||
if forceRefresh {
|
||||
ext["refresh"] = true
|
||||
}
|
||||
|
||||
// 生成缓存键
|
||||
cacheKey := cache.GeneratePluginCacheKey(keyword, plugins)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user