mirror of
https://github.com/fish2018/pansou.git
synced 2025-11-25 03:14:59 +08:00
新增插件gying
This commit is contained in:
@@ -30,6 +30,9 @@ PanSou 还提供了一个基于 [Model Context Protocol (MCP)](https://modelcont
|
||||
[常见问题总结](https://github.com/fish2018/pansou/issues/46)
|
||||
|
||||
[TG资源频道列表](https://github.com/fish2018/pansou/issues/4)
|
||||
|
||||
[gying插件文档](https://github.com/fish2018/pansou/blob/main/plugin/gying/README.md)
|
||||
|
||||
<details>
|
||||
<summary>插件列表(请务必按需加载)</summary>
|
||||
<pre>
|
||||
@@ -38,7 +41,7 @@ susu,thepiratebay,wanou,xuexizhinan,panyq,zhizhen,labi,muou,ouge,shandian,
|
||||
duoduo,huban,cyg,erxiao,miaoso,fox4k,pianku,clmao,wuji,cldi,xiaozhang,
|
||||
libvio,leijing,xb6v,xys,ddys,hdmoli,yuhuage,u3c3,javdb,clxiong,jutoushe,
|
||||
sdso,xiaoji,xdyh,haisou,bixin,djgou,nyaa,xinjuc,aikanzy,qupanshe,xdpan,
|
||||
discourse,yunsou,ahhhhfs,nsgame
|
||||
discourse,yunsou,ahhhhfs,nsgame,gying
|
||||
</pre>
|
||||
</details>
|
||||
|
||||
|
||||
1
main.go
1
main.go
@@ -26,6 +26,7 @@ import (
|
||||
// 以下是插件的空导入,用于触发各插件的init函数,实现自动注册
|
||||
// 添加新插件时,只需在此处添加对应的导入语句即可
|
||||
_ "pansou/plugin/hdr4k"
|
||||
_ "pansou/plugin/gying"
|
||||
_ "pansou/plugin/pan666"
|
||||
_ "pansou/plugin/hunhepan"
|
||||
_ "pansou/plugin/jikepan"
|
||||
|
||||
304
plugin/gying/README.md
Normal file
304
plugin/gying/README.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# Gying 搜索插件
|
||||
|
||||
## 📖 简介
|
||||
|
||||
Gying是PanSou的搜索插件,用于从 www.gying.net 网站搜索影视资源。支持多用户登录并配置账户,在搜索时自动聚合所有用户的搜索结果。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
- ✅ **多用户支持** - 每个用户独立配置,互不干扰
|
||||
- ✅ **用户名密码登录** - 支持使用用户名和密码登录
|
||||
- ✅ **智能去重** - 多用户搜索时自动去重
|
||||
- ✅ **负载均衡** - 任务均匀分配,避免单用户限流
|
||||
- ✅ **内存缓存** - 用户数据缓存到内存,搜索性能极高
|
||||
- ✅ **持久化存储** - Cookie和用户配置自动保存,重启不丢失
|
||||
- ✅ **Web管理界面** - 一站式配置,简单易用
|
||||
- ✅ **RESTful API** - 支持程序化调用
|
||||
- ✅ **默认账户自动登录** - 插件启动时自动使用默认账户登录
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 步骤1: 启动服务
|
||||
|
||||
```bash
|
||||
cd /Users/macbookpro/Desktop/fish2018/pansou
|
||||
go run main.go
|
||||
|
||||
# 或者编译后运行
|
||||
go build -o pansou main.go
|
||||
./pansou
|
||||
```
|
||||
|
||||
### 步骤2: 访问管理页面
|
||||
|
||||
如果需要添加更多账户或管理现有账户,可以访问管理页面:
|
||||
|
||||
```
|
||||
http://localhost:8888/gying/你的用户名
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
http://localhost:8888/gying/myusername
|
||||
```
|
||||
|
||||
系统会自动:
|
||||
1. 根据用户名生成专属64位hash(不可逆)
|
||||
2. 重定向到专属管理页面:`http://localhost:8888/gying/{hash}`
|
||||
3. 显示登录表单供手动登录
|
||||
|
||||
**📌 提示**:请收藏hash后的URL(包含你的专属hash),方便下次访问。
|
||||
|
||||
### 步骤3: 手动登录
|
||||
|
||||
在"登录状态"区域输入:
|
||||
- 用户名
|
||||
- 密码
|
||||
|
||||
点击"**登录**"按钮。
|
||||
|
||||
### 步骤4: 开始搜索
|
||||
|
||||
在PanSou主页搜索框输入关键词,系统会**自动聚合所有用户**的Gying搜索结果!
|
||||
|
||||
```bash
|
||||
# 通过API搜索
|
||||
curl "http://localhost:8888/api/search?kw=遮天"
|
||||
|
||||
# 只搜索插件(包括gying)
|
||||
curl "http://localhost:8888/api/search?kw=遮天&src=plugin"
|
||||
```
|
||||
|
||||
## 📡 API文档
|
||||
|
||||
### 统一接口
|
||||
|
||||
所有操作通过统一的POST接口:
|
||||
|
||||
```
|
||||
POST /gying/{hash}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"action": "操作类型",
|
||||
...其他参数
|
||||
}
|
||||
```
|
||||
|
||||
### API列表
|
||||
|
||||
| Action | 说明 | 需要登录 |
|
||||
|--------|------|---------|
|
||||
| `get_status` | 获取状态 | ❌ |
|
||||
| `login` | 登录 | ❌ |
|
||||
| `logout` | 退出登录 | ✅ |
|
||||
| `test_search` | 测试搜索 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
### 1️⃣ get_status - 获取用户状态
|
||||
|
||||
**请求**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:8888/gying/{hash}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"action": "get_status"}'
|
||||
```
|
||||
|
||||
**成功响应(已登录)**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "获取成功",
|
||||
"data": {
|
||||
"hash": "abc123...",
|
||||
"logged_in": true,
|
||||
"status": "active",
|
||||
"username_masked": "pa****ou",
|
||||
"login_time": "2025-10-28 12:00:00",
|
||||
"expire_time": "2026-02-26 12:00:00",
|
||||
"expires_in_days": 121
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**成功响应(未登录)**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "获取成功",
|
||||
"data": {
|
||||
"hash": "abc123...",
|
||||
"logged_in": false,
|
||||
"status": "pending"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ login - 登录
|
||||
|
||||
**请求**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:8888/gying/{hash}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"action": "login", "username": "xxx", "password": "xxx"}'
|
||||
```
|
||||
|
||||
**成功响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "登录成功",
|
||||
"data": {
|
||||
"status": "active",
|
||||
"username_masked": "pa****ou"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应**:
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "登录失败: 用户名或密码错误"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ logout - 退出登录
|
||||
|
||||
**请求**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:8888/gying/{hash}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"action": "logout"}'
|
||||
```
|
||||
|
||||
**成功响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "已退出登录",
|
||||
"data": {
|
||||
"status": "pending"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4️⃣ test_search - 测试搜索
|
||||
|
||||
**请求**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:8888/gying/{hash}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"action": "test_search", "keyword": "遮天"}'
|
||||
```
|
||||
|
||||
**成功响应**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "找到 5 条结果",
|
||||
"data": {
|
||||
"keyword": "遮天",
|
||||
"total_results": 5,
|
||||
"results": [
|
||||
{
|
||||
"title": "遮天:禁区",
|
||||
"links": [
|
||||
{
|
||||
"type": "quark",
|
||||
"url": "https://pan.quark.cn/s/89f7aeef9681",
|
||||
"password": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### 环境变量(可选)
|
||||
|
||||
```bash
|
||||
# Hash Salt(推荐自定义,增强安全性)
|
||||
export GYING_HASH_SALT="your-custom-salt-here"
|
||||
|
||||
# Cookie加密密钥(32字节,推荐自定义)
|
||||
export GYING_ENCRYPTION_KEY="your-32-byte-key-here!!!!!!!!!!"
|
||||
```
|
||||
|
||||
### 代码内配置
|
||||
|
||||
在 `gying.go` 第20-24行修改:
|
||||
|
||||
```go
|
||||
const (
|
||||
MaxConcurrentUsers = 10 // 最多使用的用户数(搜索时)
|
||||
MaxConcurrentDetails = 50 // 最大并发详情请求数
|
||||
DebugLog = false // 调试日志开关
|
||||
)
|
||||
```
|
||||
|
||||
### 默认账户配置
|
||||
|
||||
在 `gying.go` 第27-32行修改默认账户:
|
||||
|
||||
```go
|
||||
var DefaultAccounts = []struct {
|
||||
Username string
|
||||
Password string
|
||||
}{
|
||||
// 可以添加更多默认账户
|
||||
// {"user2", "password2"},
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**:
|
||||
|
||||
| 参数 | 默认值 | 说明 | 建议 |
|
||||
|------|--------|------|------|
|
||||
| `MaxConcurrentUsers` | 10 | 单次搜索最多使用的用户数 | 10-20足够 |
|
||||
| `MaxConcurrentDetails` | 50 | 最大并发详情请求数 | 50-100 |
|
||||
| `DebugLog` | false | 是否开启调试日志 | 生产环境false |
|
||||
|
||||
## 📂 数据存储
|
||||
|
||||
### 存储位置
|
||||
|
||||
```
|
||||
cache/gying_users/{hash}.json
|
||||
```
|
||||
|
||||
### 数据结构
|
||||
|
||||
```json
|
||||
{
|
||||
"hash": "abc123...",
|
||||
"username": "pansou",
|
||||
"username_masked": "pa****ou",
|
||||
"cookie": "BT_auth=xxx; BT_cookietime=xxx",
|
||||
"status": "active",
|
||||
"created_at": "2025-10-28T12:00:00+08:00",
|
||||
"login_at": "2025-10-28T12:00:00+08:00",
|
||||
"expire_at": "2026-02-26T12:00:00+08:00",
|
||||
"last_access_at": "2025-10-28T13:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**:
|
||||
- `hash`: 用户唯一标识(SHA256,不可逆推用户名)
|
||||
- `username`: 原始用户名(存储)
|
||||
- `username_masked`: 脱敏用户名(如`pa****ou`)
|
||||
- `cookie`: 登录Cookie(明文存储,建议配置加密)
|
||||
- `status`: 用户状态(`pending`/`active`/`expired`)
|
||||
- `expire_at`: Cookie过期时间(121天)
|
||||
1977
plugin/gying/gying.go
Normal file
1977
plugin/gying/gying.go
Normal file
File diff suppressed because it is too large
Load Diff
377
plugin/gying/html结构分析.md
Normal file
377
plugin/gying/html结构分析.md
Normal file
@@ -0,0 +1,377 @@
|
||||
# Gying 网站结构分析
|
||||
|
||||
## 基本信息
|
||||
- **网站URL**: https://www.gying.net
|
||||
- **数据源类型**: 混合型(HTML + JSON API)
|
||||
- **特殊架构**: 需要登录 + 搜索结果在HTML内嵌JSON + 详情接口返回JSON
|
||||
- **支持多账户**: 是(支持负载均衡搜索)
|
||||
|
||||
## 登录认证
|
||||
|
||||
### 登录接口
|
||||
- **URL**: `https://www.gying.net/user/login`
|
||||
- **方法**: POST
|
||||
- **Content-Type**: `application/x-www-form-urlencoded`
|
||||
|
||||
### 登录请求参数
|
||||
```
|
||||
code=&siteid=1&dosubmit=1&cookietime=10506240&username={用户名}&password={密码}
|
||||
```
|
||||
|
||||
| 参数 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `code` | 验证码(可为空) | `` |
|
||||
| `siteid` | 站点ID(固定) | `1` |
|
||||
| `dosubmit` | 提交标识(固定) | `1` |
|
||||
| `cookietime` | Cookie有效期(秒) | `10506240` (约121天) |
|
||||
| `username` | 用户名 | `xxx` |
|
||||
| `password` | 密码 | `xxx` |
|
||||
|
||||
### 登录响应
|
||||
```json
|
||||
{"code":200}
|
||||
```
|
||||
|
||||
### 登录Cookie
|
||||
- **BT_auth**: 认证Cookie(HttpOnly, Secure, 121天有效期)
|
||||
```
|
||||
BT_auth=433cnQGx2Obm5YAMWnGaG-ZCcuma9JvULO1CSvPz7JzBhj3-t4HhwhSXrxaEVO53lSVoFtT_0-Ilzglvh0vFvv7RLqFfPdE17Maen0B3sWPwnO5GSQszEW9ZyjOU4KLx8TuRvDj3mF7bVVX4rgtgOq9gP0ljq_X-APtIPf3tkliblls
|
||||
```
|
||||
- **BT_cookietime**: Cookie时间标识
|
||||
```
|
||||
BT_cookietime=a9f5uPN9hZE-fXuzGhTxM8Vh6K5BUIVqeg4ESRHGbcU3jM7ZuuIB
|
||||
```
|
||||
|
||||
### 重要请求头
|
||||
```
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
|
||||
Accept: */*
|
||||
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Origin: https://www.gying.net
|
||||
Referer: https://www.gying.net/user/login/
|
||||
```
|
||||
|
||||
## 搜索接口
|
||||
|
||||
### 搜索URL
|
||||
- **格式**: `https://www.gying.net/s/1---1/{关键词}`
|
||||
- **方法**: GET
|
||||
- **关键词**: 需要URL编码(如:`遮天` -> `%E9%81%AE%E5%A4%A9`)
|
||||
|
||||
### 搜索响应格式
|
||||
搜索结果返回HTML页面,但实际数据在JavaScript变量 `_obj.search` 中:
|
||||
|
||||
```javascript
|
||||
_obj.search = {
|
||||
"q": "遮天", // 搜索关键词
|
||||
"wd": ["天","遮"], // 分词结果
|
||||
"n": "14", // 结果数量(字符串)
|
||||
"ns": [14,6,4,4,35], // 各类型结果统计
|
||||
"ty": 0, // 类型标识
|
||||
"l": { // 详细信息列表
|
||||
"daoyan": [...], // 导演
|
||||
"bianju": [...], // 编剧
|
||||
"zhuyan": [...], // 主演
|
||||
"info": [...], // 信息(地区/类型等)
|
||||
"pf": {...}, // 平台评分(豆瓣/IMDb)
|
||||
"title": [...], // 标题
|
||||
"name": [...], // 名称(英文等)
|
||||
"ename": [...], // 别名
|
||||
"year": [...], // 年份
|
||||
"d": [...], // 类型(mv=电影,ac=动画,tv=电视剧)
|
||||
"i": [...] // 资源ID(用于详情页)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 搜索结果字段映射
|
||||
|
||||
| 字段 | 说明 | 示例 | 用途 |
|
||||
|------|------|------|------|
|
||||
| `l.i` | 资源ID数组 | `["xJe3", "rzoj", ...]` | 用于构建详情接口URL |
|
||||
| `l.title` | 标题数组 | `["遮天:禁区", ...]` | 显示标题 |
|
||||
| `l.year` | 年份数组 | `[2023, 2023, ...]` | 年份标签 |
|
||||
| `l.d` | 类型数组 | `["mv", "ac", "tv"]` | 资源类型 |
|
||||
| `l.info` | 信息数组 | `["大陆 / 动作 / 冒险 / 奇幻", ...]` | 描述信息 |
|
||||
| `l.daoyan` | 导演数组 | `["罗乐", ...]` | 导演信息 |
|
||||
| `l.zhuyan` | 主演数组 | `["冯荔军 / 彭高唱 / ...", ...]` | 主演信息 |
|
||||
|
||||
### 类型标识(d字段)
|
||||
- `mv`: 电影
|
||||
- `ac`: 动画
|
||||
- `tv`: 电视剧
|
||||
|
||||
## 详情接口
|
||||
|
||||
### 详情URL
|
||||
- **格式**: `https://www.gying.net/res/downurl/{类型}/{资源ID}`
|
||||
- **示例**: `https://www.gying.net/res/downurl/mv/xJe3`
|
||||
- **方法**: GET
|
||||
- **认证**: 需要登录Cookie
|
||||
|
||||
### 详情响应结构
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"wp": false, // 是否需要网盘
|
||||
"downlist": { // 下载列表
|
||||
"imdb": "", // IMDb ID
|
||||
"type": {
|
||||
"a": ["1080P", "中字1080P", "中字4K"], // 清晰度类型数组
|
||||
"b": ["i3", "i7", "i4"] // 类型标识数组
|
||||
},
|
||||
"hex": "a0a74991cb03e4d43bb6564018c46c4034edff3cf4e32f356f735744258cbe5e",
|
||||
"list": { // 下载文件列表
|
||||
"m": ["hash1", "hash2", ...], // 文件hash数组
|
||||
"t": ["文件名1.mkv", "文件名2.mkv", ...], // 文件名数组
|
||||
"s": ["999.46M", "4.87G", ...], // 文件大小数组
|
||||
"e": [3, 0, 2, ...], // 编码类型
|
||||
"p": ["i3", "i4", "i7", ...], // 类型标识
|
||||
"u": ["短链1", "短链2", ...], // 短链接数组
|
||||
"k": [0, 0, 0, ...], // 密码标识(0=无密码)
|
||||
"n": ["1年前", "2年前", ...] // 上传时间
|
||||
}
|
||||
},
|
||||
"playlist": [...], // 播放列表(在线播放)
|
||||
"panlist": { // 网盘链接列表
|
||||
"id": ["lYPNk", "oJ858", ...], // 网盘分享ID
|
||||
"name": ["标题1", "标题2", ...], // 分享标题
|
||||
"p": ["", "", "917d", ...], // 提取码数组
|
||||
"url": [ // 分享链接数组
|
||||
"https://pan.quark.cn/s/89f7aeef9681",
|
||||
"https://cloud.189.cn/t\/3aQbiynAzEVn(访问码:7dsf)",
|
||||
"https://pan.baidu.com/s/1B_BnI7IDtQexYiytiZXOwg?pwd=917d",
|
||||
...
|
||||
],
|
||||
"type": [2, 3, 0, ...], // 网盘类型标识
|
||||
"user": ["沸羊羊爱分享", "大狗熊A", ...], // 分享用户
|
||||
"gid": [5, 4, 4, ...], // 用户组ID
|
||||
"time": ["7天前", "12天前", ...], // 分享时间
|
||||
"e": [0, 0, 0, ...], // 过期标识
|
||||
"heart": [0, 0, 0, ...], // 点赞数
|
||||
"tname": ["百度网盘", "迅雷网盘", ...] // 网盘类型名称数组
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 网盘类型标识(panlist.type)
|
||||
| 标识 | 网盘类型 | 说明 |
|
||||
|-----|---------|------|
|
||||
| `0` | 百度网盘 | baidu |
|
||||
| `1` | 迅雷网盘 | xunlei |
|
||||
| `2` | 夸克网盘 | quark |
|
||||
| `3` | 天翼网盘 | tianyi |
|
||||
| `4` | UC网盘 | uc |
|
||||
| `5` | 阿里网盘 | aliyun |
|
||||
|
||||
### 提取码处理
|
||||
- 提取码在 `panlist.p` 数组中
|
||||
- 如果URL中包含 `?pwd=` 或 `访问码:`,优先从URL提取
|
||||
- 如果 `panlist.p` 为空字符串,则无提取码
|
||||
|
||||
## 插件所需字段映射
|
||||
|
||||
### SearchResult构建
|
||||
```go
|
||||
result := model.SearchResult{
|
||||
UniqueID: fmt.Sprintf("gying-%s-%s", resourceType, resourceID), // 如 gying-mv-xJe3
|
||||
Title: title, // 从 l.title
|
||||
Content: buildContent(info, director, actors), // 组合信息
|
||||
Links: extractPanLinks(panlist), // 从详情接口获取
|
||||
Tags: []string{year}, // 从 l.year
|
||||
Channel: "", // 插件搜索结果Channel为空
|
||||
Datetime: time.Now(), // 当前时间
|
||||
}
|
||||
```
|
||||
|
||||
### 链接提取逻辑
|
||||
从 `panlist` 中提取,需要处理:
|
||||
1. 识别网盘类型(通过type标识或URL域名)
|
||||
2. 提取提取码(优先从URL,其次从p数组)
|
||||
3. 过滤无效链接(空URL或过期)
|
||||
4. 去重(同一URL只保留一次)
|
||||
|
||||
## 支持的网盘类型
|
||||
|
||||
### 主流网盘
|
||||
- **quark (夸克网盘)**: `https://pan.quark.cn/s/{分享码}`
|
||||
- **baidu (百度网盘)**: `https://pan.baidu.com/s/{分享码}?pwd={密码}`
|
||||
- **aliyun (阿里云盘)**: `https://www.alipan.com/s/{分享码}`
|
||||
- **uc (UC网盘)**: `https://drive.uc.cn/s/{分享码}`
|
||||
- **xunlei (迅雷网盘)**: `https://pan.xunlei.com/s/{分享码}`
|
||||
- **tianyi (天翼云盘)**: `https://cloud.189.cn/t/{分享码}`
|
||||
|
||||
## 插件开发指导
|
||||
|
||||
### 登录管理策略
|
||||
参考QQPD插件实现:
|
||||
1. **初始化登录**: 插件启动时,从缓存加载已登录用户
|
||||
2. **Cookie持久化**: 将Cookie保存到 `cache/gying_users/{hash}.json`
|
||||
3. **多账户支持**: 支持配置多个账户,进行负载均衡
|
||||
4. **Web管理界面**: 提供 `/gying/:param` 路由管理账户
|
||||
|
||||
### 用户数据结构
|
||||
```json
|
||||
{
|
||||
"hash": "用户hash(SHA256)",
|
||||
"username": "用户名(脱敏)",
|
||||
"cookie": "BT_auth=xxx; BT_cookietime=xxx",
|
||||
"status": "active/pending/expired",
|
||||
"created_at": "2025-10-28T12:00:00+08:00",
|
||||
"login_at": "2025-10-28T12:00:00+08:00",
|
||||
"expire_at": "2026-02-26T12:00:00+08:00", // 121天后
|
||||
"last_access_at": "2025-10-28T13:00:00+08:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 搜索流程
|
||||
```
|
||||
1. 用户搜索 "遮天"
|
||||
↓
|
||||
2. 获取所有有效用户(status=active)
|
||||
↓
|
||||
3. 负载均衡分配任务(每个用户处理部分搜索)
|
||||
↓
|
||||
4. 并发执行搜索:
|
||||
a. 使用用户Cookie请求搜索页面
|
||||
b. 提取HTML中的 _obj.search JSON数据
|
||||
c. 遍历 l.i 数组,并发请求详情接口
|
||||
d. 解析网盘链接
|
||||
↓
|
||||
5. 合并所有用户的结果
|
||||
↓
|
||||
6. 去重并返回
|
||||
```
|
||||
|
||||
### 关键函数示例
|
||||
|
||||
#### 登录函数
|
||||
```go
|
||||
func (p *GyingPlugin) login(username, password string) (string, error) {
|
||||
data := url.Values{}
|
||||
data.Set("code", "")
|
||||
data.Set("siteid", "1")
|
||||
data.Set("dosubmit", "1")
|
||||
data.Set("cookietime", "10506240") // 121天
|
||||
data.Set("username", username)
|
||||
data.Set("password", password)
|
||||
|
||||
req, _ := http.NewRequest("POST", "https://www.gying.net/user/login",
|
||||
strings.NewReader(data.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0...")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
// ... 处理响应
|
||||
|
||||
// 提取Cookie
|
||||
cookies := resp.Cookies()
|
||||
var btAuth, btCookietime string
|
||||
for _, cookie := range cookies {
|
||||
if cookie.Name == "BT_auth" {
|
||||
btAuth = cookie.Value
|
||||
} else if cookie.Name == "BT_cookietime" {
|
||||
btCookietime = cookie.Value
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("BT_auth=%s; BT_cookietime=%s", btAuth, btCookietime), nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 搜索函数
|
||||
```go
|
||||
func (p *GyingPlugin) searchWithCookie(keyword, cookie string) ([]model.SearchResult, error) {
|
||||
// 1. 请求搜索页面
|
||||
searchURL := fmt.Sprintf("https://www.gying.net/s/1---1/%s", url.QueryEscape(keyword))
|
||||
req, _ := http.NewRequest("GET", searchURL, nil)
|
||||
req.Header.Set("Cookie", cookie)
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0...")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
// ... 处理响应
|
||||
|
||||
// 2. 提取 _obj.search JSON
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
re := regexp.MustCompile(`_obj\.search=(\{.*?\});`)
|
||||
matches := re.FindSubmatch(body)
|
||||
if len(matches) < 2 {
|
||||
return nil, fmt.Errorf("未找到搜索结果")
|
||||
}
|
||||
|
||||
var searchData SearchData
|
||||
json.Unmarshal(matches[1], &searchData)
|
||||
|
||||
// 3. 并发请求详情接口
|
||||
var results []model.SearchResult
|
||||
for i, resourceID := range searchData.L.I {
|
||||
// 并发获取详情
|
||||
detail := p.fetchDetail(resourceID, searchData.L.D[i], cookie)
|
||||
result := p.buildResult(detail, searchData, i)
|
||||
results = append(results, result)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
```
|
||||
|
||||
#### 详情获取函数
|
||||
```go
|
||||
func (p *GyingPlugin) fetchDetail(resourceID, resourceType, cookie string) (*DetailData, error) {
|
||||
detailURL := fmt.Sprintf("https://www.gying.net/res/downurl/%s/%s", resourceType, resourceID)
|
||||
req, _ := http.NewRequest("GET", detailURL, nil)
|
||||
req.Header.Set("Cookie", cookie)
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0...")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
// ... 处理响应
|
||||
|
||||
var detail DetailData
|
||||
json.NewDecoder(resp.Body).Decode(&detail)
|
||||
return &detail, nil
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **登录验证**: 每次请求前验证Cookie是否有效,失效则重新登录
|
||||
2. **并发控制**: 控制详情接口的并发数,避免触发反爬虫(建议50并发)
|
||||
3. **错误处理**: 处理网络超时、JSON解析失败等异常情况
|
||||
4. **提取码处理**: 优先从URL中提取提取码,兼容多种格式
|
||||
5. **去重逻辑**: 同一资源可能有多个网盘链接,需要去重
|
||||
6. **Cookie刷新**: Cookie有效期121天,接近过期时提前刷新
|
||||
7. **多账户负载**: 当用户数大于1时,均匀分配搜索任务
|
||||
|
||||
## 与其他插件的差异
|
||||
|
||||
| 特性 | gying | qqpd | huban |
|
||||
|------|-------|------|-------|
|
||||
| **认证方式** | 用户名密码 | QQ扫码 | 无需登录 |
|
||||
| **数据格式** | HTML内嵌JSON + 详情API | API | JSON API |
|
||||
| **多账户** | 支持 | 支持 | 不支持 |
|
||||
| **Cookie管理** | 需要 | 需要 | 不需要 |
|
||||
| **负载均衡** | 支持 | 支持 | 不支持 |
|
||||
|
||||
## 开发建议
|
||||
|
||||
1. **分步实现**:
|
||||
- 先实现单账户登录和搜索
|
||||
- 再扩展多账户支持
|
||||
- 最后添加Web管理界面
|
||||
|
||||
2. **测试重点**:
|
||||
- Cookie失效后的自动重新登录
|
||||
- 并发详情请求的稳定性
|
||||
- 多账户负载均衡的正确性
|
||||
|
||||
3. **性能优化**:
|
||||
- 缓存搜索结果(5分钟)
|
||||
- 批量并发请求详情接口
|
||||
- 复用HTTP连接
|
||||
|
||||
4. **容错机制**:
|
||||
- 单个详情请求失败不影响整体
|
||||
- Cookie失效时自动降级到其他账户
|
||||
- 网络异常时自动重试3次
|
||||
|
||||
Reference in New Issue
Block a user