This commit is contained in:
www.xueximeng.com
2025-10-28 21:40:32 +08:00
parent 50d3e5466b
commit 538abef99d

View File

@@ -23,11 +23,12 @@ import (
"time" "time"
"unsafe" "unsafe"
"github.com/gin-gonic/gin"
"pansou/model" "pansou/model"
"pansou/plugin" "pansou/plugin"
"pansou/util/json" "pansou/util/json"
"github.com/gin-gonic/gin"
cloudscraper "github.com/Advik-B/cloudscraper/lib" cloudscraper "github.com/Advik-B/cloudscraper/lib"
) )
@@ -35,7 +36,7 @@ import (
const ( const (
MaxConcurrentUsers = 10 // 最多使用的用户数 MaxConcurrentUsers = 10 // 最多使用的用户数
MaxConcurrentDetails = 50 // 最大并发详情请求数 MaxConcurrentDetails = 50 // 最大并发详情请求数
DebugLog = false // 调试日志开关排查问题时改为true DebugLog = true // 调试日志开关排查问题时改为true
) )
// 默认账户配置可通过Web界面添加更多账户 // 默认账户配置可通过Web界面添加更多账户
@@ -566,6 +567,8 @@ func (p *GyingPlugin) loadAllUsers() {
// initDefaultAccounts 初始化所有账户(异步执行,不阻塞启动) // initDefaultAccounts 初始化所有账户(异步执行,不阻塞启动)
// 包括1. DefaultAccounts代码配置 2. 从文件加载的用户(使用加密密码重新登录) // 包括1. DefaultAccounts代码配置 2. 从文件加载的用户(使用加密密码重新登录)
func (p *GyingPlugin) initDefaultAccounts() { func (p *GyingPlugin) initDefaultAccounts() {
fmt.Printf("[Gying] ========== 异步初始化所有账户 ==========\n")
// 步骤1处理DefaultAccounts代码中配置的默认账户 // 步骤1处理DefaultAccounts代码中配置的默认账户
for i, account := range DefaultAccounts { for i, account := range DefaultAccounts {
if DebugLog { if DebugLog {
@@ -604,6 +607,8 @@ func (p *GyingPlugin) initDefaultAccounts() {
p.initOrRestoreUser(user.Username, password, "restore") p.initOrRestoreUser(user.Username, password, "restore")
} }
} }
fmt.Printf("[Gying] ========== 所有账户初始化完成 ==========\n")
} }
// initOrRestoreUser 初始化或恢复单个用户(登录并保存) // initOrRestoreUser 初始化或恢复单个用户(登录并保存)
@@ -1126,7 +1131,14 @@ func (p *GyingPlugin) doLogin(username, password string) (*cloudscraper.Scraper,
} }
// 创建cloudscraper实例每个用户独立的实例 // 创建cloudscraper实例每个用户独立的实例
scraper, err := cloudscraper.New() // 关键配置禁用403自动刷新,防止cookie被清空
scraper, err := cloudscraper.New(
cloudscraper.WithSessionConfig(
false, // refreshOn403 = false禁用403时自动刷新重要
365*24*time.Hour, // interval = 1年基本不刷新
0, // maxRetries = 0
),
)
if err != nil { if err != nil {
if DebugLog { if DebugLog {
fmt.Printf("[Gying] 创建cloudscraper失败: %v\n", err) fmt.Printf("[Gying] 创建cloudscraper失败: %v\n", err)
@@ -1135,7 +1147,7 @@ func (p *GyingPlugin) doLogin(username, password string) (*cloudscraper.Scraper,
} }
if DebugLog { if DebugLog {
fmt.Printf("[Gying] cloudscraper创建成功\n") fmt.Printf("[Gying] cloudscraper创建成功已禁用403自动刷新\n")
} }
// 创建cookieMap用于收集所有cookies // 创建cookieMap用于收集所有cookies
@@ -1354,6 +1366,54 @@ func min(a, b int) int {
return b return b
} }
// ============ 重新登录逻辑 ============
// reloginUser 重新登录指定用户
func (p *GyingPlugin) reloginUser(user *User) error {
if DebugLog {
fmt.Printf("[Gying] 🔄 开始重新登录用户: %s\n", user.UsernameMasked)
}
// 解密密码
password, err := p.decryptPassword(user.EncryptedPassword)
if err != nil {
if DebugLog {
fmt.Printf("[Gying] ❌ 解密密码失败: %v\n", err)
}
return fmt.Errorf("解密密码失败: %w", err)
}
// 执行登录
scraper, cookie, err := p.doLogin(user.Username, password)
if err != nil {
if DebugLog {
fmt.Printf("[Gying] ❌ 重新登录失败: %v\n", err)
}
return fmt.Errorf("重新登录失败: %w", err)
}
// 更新scraper实例
p.scrapers.Store(user.Hash, scraper)
// 更新用户信息
user.Cookie = cookie
user.LoginAt = time.Now()
user.ExpireAt = time.Now().AddDate(0, 4, 0)
user.Status = "active"
if err := p.saveUser(user); err != nil {
if DebugLog {
fmt.Printf("[Gying] ⚠️ 保存用户失败: %v\n", err)
}
}
if DebugLog {
fmt.Printf("[Gying] ✅ 用户 %s 重新登录成功\n", user.UsernameMasked)
}
return nil
}
// ============ 搜索逻辑 ============ // ============ 搜索逻辑 ============
// executeSearchTasks 并发执行搜索任务 // executeSearchTasks 并发执行搜索任务
@@ -1376,8 +1436,14 @@ func (p *GyingPlugin) executeSearchTasks(users []*User, keyword string) []model.
fmt.Printf("[Gying] 用户 %s 没有scraper实例尝试使用已保存的cookie创建\n", u.UsernameMasked) fmt.Printf("[Gying] 用户 %s 没有scraper实例尝试使用已保存的cookie创建\n", u.UsernameMasked)
} }
// 为用户创建新的cloudscraper实例 // 为用户创建新的cloudscraper实例禁用403自动刷新
newScraper, err := cloudscraper.New() newScraper, err := cloudscraper.New(
cloudscraper.WithSessionConfig(
false, // refreshOn403 = false
365*24*time.Hour, // interval = 1年
0, // maxRetries = 0
),
)
if err != nil { if err != nil {
if DebugLog { if DebugLog {
fmt.Printf("[Gying] 为用户 %s 创建scraper失败: %v\n", u.UsernameMasked, err) fmt.Printf("[Gying] 为用户 %s 创建scraper失败: %v\n", u.UsernameMasked, err)
@@ -1390,7 +1456,7 @@ func (p *GyingPlugin) executeSearchTasks(users []*User, keyword string) []model.
scraper = newScraper scraper = newScraper
if DebugLog { if DebugLog {
fmt.Printf("[Gying] 已为用户 %s 创建新的scraper实例\n", u.UsernameMasked) fmt.Printf("[Gying] 已为用户 %s 创建新的scraper实例已禁用403刷新\n", u.UsernameMasked)
} }
} else { } else {
var ok bool var ok bool
@@ -1403,10 +1469,10 @@ func (p *GyingPlugin) executeSearchTasks(users []*User, keyword string) []model.
} }
} }
results, err := p.searchWithScraper(keyword, scraper) results, err := p.searchWithScraperWithRetry(keyword, scraper, u)
if err != nil { if err != nil {
if DebugLog { if DebugLog {
fmt.Printf("[Gying] 用户 %s 搜索失败: %v\n", u.UsernameMasked, err) fmt.Printf("[Gying] 用户 %s 搜索失败(已重试): %v\n", u.UsernameMasked, err)
} }
return return
} }
@@ -1423,7 +1489,49 @@ func (p *GyingPlugin) executeSearchTasks(users []*User, keyword string) []model.
return p.deduplicateResults(allResults) return p.deduplicateResults(allResults)
} }
// searchWithCookie 使用scraper搜索 // searchWithScraperWithRetry 使用scraper搜索带403自动重新登录重试
func (p *GyingPlugin) searchWithScraperWithRetry(keyword string, scraper *cloudscraper.Scraper, user *User) ([]model.SearchResult, error) {
results, err := p.searchWithScraper(keyword, scraper)
// 检测是否为403错误
if err != nil && strings.Contains(err.Error(), "403") {
if DebugLog {
fmt.Printf("[Gying] ⚠️ 检测到403错误尝试重新登录用户 %s\n", user.UsernameMasked)
}
// 尝试重新登录
if reloginErr := p.reloginUser(user); reloginErr != nil {
if DebugLog {
fmt.Printf("[Gying] ❌ 重新登录失败: %v\n", reloginErr)
}
return nil, fmt.Errorf("403错误且重新登录失败: %w", reloginErr)
}
// 获取新的scraper实例
scraperVal, exists := p.scrapers.Load(user.Hash)
if !exists {
return nil, fmt.Errorf("重新登录后未找到scraper实例")
}
newScraper, ok := scraperVal.(*cloudscraper.Scraper)
if !ok || newScraper == nil {
return nil, fmt.Errorf("重新登录后scraper实例无效")
}
// 使用新scraper重试搜索
if DebugLog {
fmt.Printf("[Gying] 🔄 使用新登录状态重试搜索\n")
}
results, err = p.searchWithScraper(keyword, newScraper)
if err != nil {
return nil, fmt.Errorf("重新登录后搜索仍然失败: %w", err)
}
}
return results, err
}
// searchWithScraper 使用scraper搜索
func (p *GyingPlugin) searchWithScraper(keyword string, scraper *cloudscraper.Scraper) ([]model.SearchResult, error) { func (p *GyingPlugin) searchWithScraper(keyword string, scraper *cloudscraper.Scraper) ([]model.SearchResult, error) {
if DebugLog { if DebugLog {
fmt.Printf("[Gying] ---------- searchWithScraper 开始 ----------\n") fmt.Printf("[Gying] ---------- searchWithScraper 开始 ----------\n")
@@ -1451,6 +1559,7 @@ func (p *GyingPlugin) searchWithScraper(keyword string, scraper *cloudscraper.Sc
fmt.Printf("[Gying] 搜索响应状态码: %d\n", resp.StatusCode) fmt.Printf("[Gying] 搜索响应状态码: %d\n", resp.StatusCode)
} }
// 读取响应body
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
if DebugLog { if DebugLog {
@@ -1471,6 +1580,21 @@ func (p *GyingPlugin) searchWithScraper(keyword string, scraper *cloudscraper.Sc
} }
} }
// 检查403错误
if resp.StatusCode == 403 {
if DebugLog {
fmt.Printf("[Gying] ❌ 收到403 Forbidden - Cookie可能已过期或被网站拒绝\n")
if len(body) > 0 {
preview := string(body)
if len(preview) > 300 {
preview = preview[:300] + "..."
}
fmt.Printf("[Gying] 403响应内容: %s\n", preview)
}
}
return nil, fmt.Errorf("HTTP 403 Forbidden - 可能需要重新登录")
}
// 2. 提取 _obj.search JSON // 2. 提取 _obj.search JSON
re := regexp.MustCompile(`_obj\.search=(\{.*?\});`) re := regexp.MustCompile(`_obj\.search=(\{.*?\});`)
matches := re.FindSubmatch(body) matches := re.FindSubmatch(body)
@@ -1624,7 +1748,18 @@ func (p *GyingPlugin) fetchDetail(resourceID, resourceType string, scraper *clou
fmt.Printf("[Gying] 响应状态码: %d\n", resp.StatusCode) fmt.Printf("[Gying] 响应状态码: %d\n", resp.StatusCode)
} }
// 检查403错误
if resp.StatusCode == 403 {
if DebugLog {
fmt.Printf("[Gying] ❌ 详情接口返回403 - Cookie可能已过期\n")
}
return nil, fmt.Errorf("HTTP 403 Forbidden")
}
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
if DebugLog {
fmt.Printf("[Gying] ❌ HTTP错误: %d\n", resp.StatusCode)
}
return nil, fmt.Errorf("HTTP %d", resp.StatusCode) return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
} }
@@ -1974,4 +2109,3 @@ func (p *GyingPlugin) markInactiveUsers() int {
return markedCount return markedCount
} }