pansou Documentation

PanSou是一款高性能的网盘资源搜索API服务,支持TG频道和插件搜索。系统设计以性能和可扩展性为核心,支持多频道多插件并发搜索、结果智能排序和网盘类型分类。docker集成前后端,一键启动,开箱即用。

12,983 stars Go Go 1 file · ~11,825 tokens
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy pansou on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name pansou -p 8888:8888 --restart always ghcr.io/fish2018/pansou:latest
Configured Services & Ports
⚙️ pansou
image: ghcr.io/fish2018/pansou:latest
🔌 Ports: 8888:8888
docker-compose.yml
yaml (docker-compose.yml)
version: '3.8'

services:
  pansou:
    image: ghcr.io/fish2018/pansou:latest
    container_name: pansou
    restart: unless-stopped
    ports:
      - "8888:8888"
    environment:
      - PORT=8888
      - CHANNELS=tgsearchers7,Aliyun_4K_Movies,yunpanx,yp123pan,yunpanxunlei,tianyifc,peccxinpd,gotopan,PanjClub,baicaoZY,MCPH02,MCPH03,bdwpzhpd,Q66Share,ucwpzy,shareAliyun,Quark_Movies,XiangxiuNBB,ucquark,xx123pan,yingshifenxiang123,zyfb123,Lsp115,taoxgzy,Channel_Shares_115,vip115hot,wp123zy,yunpan139,yunpan189,yunpanuc,yydf_hzl,leoziyuan,yoyokuakeduanju,TG654TG,QukanMovie,yeqingjie_GJG666,movielover8888_film3,Baidu_netdisk,D_wusun,FLMdongtianfudi,KaiPanshare,rjyxfx,PikPak_Share_Channel,newproductsourcing,QuarkFree,yunpanNB,kkdj001,xxzlzn,pxyunpanxunlei,jxwpzy,kuakedongman,xiangnikanj,solidsexydoll,guoman4K,zdqxm,kduanju,cilidianying,CBduanju,SharePanFilms,dzsgx,BooksRealm,douerpan,Netdisk_Movies,yunpanquark,ciliziyuanku,jzmm_123pan,wpan8,mqte5,regengguangya,regeng115,regeng123,yy80986098,pan_guangya,guangyapan_episode,guangya_hdhive,guangyapindao,quark_res,domgmingapk,dianying4k,tgbokee,ucshare,gokuapan,WFYSFX03,gimy100,gimy115iso,fcij5,xvth5,xuexiziliaobaibaoku,phzvip,jdbigdiscount,youxigs,zhoulanziyuan,seedhub_pro,jnjy_5,xxziliao,wpzyk,ruanjianfenxiang77,jpnd5,XunLeiPinDao,a123fxme,WPpindao,kuyupan,djya5,zh_vip,gdsharing,guangyaya2026,alyp_17362,baidyunpan,yunpans,rbzhwpzy,Panzi88com
      # 必须指定启用的插件,多个插件用逗号分隔
      - ENABLED_PLUGINS=dyyjpro,duoduo,djgou,feikuai,gaoqing888,hdmoli,haitunsou,hunhepan,ikantv,jutoushe,kkv,dy4k,libvio,lingjisp,lou1,melost,meitizy,miosou,nyaa,ouge,panlian,pansearch,qqpd,quark4k,quarksoo,quarktv,sousou,thepiratebay,ting77,wanou,weibo,xb6v,xiaokupan,xiaozhang,xiaoyu,yingso,yulinshufa,yunso,yunsou,zlxapp,zxzj,rrbt,quarkres,diduan,huban,labi,muou,shandian,zhizhen,clxiong,cyg,jsnoteclub,duanjuw,dyyj,nsgame,cldi,clmao,susu,u3c3,5266ys,dygang,leso,btbtlb,aipan,sopanya,hjzhencai,pan365,buerchen,erxiaopan,488run,woniu
      - CACHE_ENABLED=true
      - CACHE_PATH=/app/cache
      - CACHE_MAX_SIZE=100
      - CACHE_TTL=60
      - ASYNC_PLUGIN_ENABLED=true
      - ASYNC_RESPONSE_TIMEOUT=4
      - ASYNC_MAX_BACKGROUND_WORKERS=20
      - ASYNC_MAX_BACKGROUND_TASKS=100
      - ASYNC_CACHE_TTL_HOURS=1
      # 认证配置(可选)
      # - AUTH_ENABLED=true
      # - AUTH_USERS=admin:admin123,user:pass456
      # - AUTH_TOKEN_EXPIRY=24
      # - AUTH_JWT_SECRET=your-secret-key-here
      # 如果需要代理,取消下面的注释并设置代理地址
      # - PROXY=socks5://proxy:7897
    volumes:
      - pansou-cache:/app/cache
    networks:
      - pansou-network
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:8888/api/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

  # 如果需要代理,取消下面的注释
  # proxy:
  #   image: ghcr.io/snail007/goproxy:latest
  #   container_name: pansou-proxy
  #   restart: unless-stopped
  #   command: /proxy socks -p :7897
  #   networks:
  #     - pansou-network

volumes:
  pansou-cache:
    name: pansou-cache

networks:
  pansou-network:
    name: pansou-network

File: README.md

PanSou 网盘搜索API

PanSou是一个高性能的网盘资源搜索API服务,支持TG搜索和自定义插件搜索。系统设计以性能和可扩展性为核心,支持并发搜索、结果智能排序和网盘类型分类。

特性([详见系统设计文档](docs/%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91%E8%AE%BE%E8%AE%A1%E6%96%87%E6%A1%A3.md))

  • 高性能搜索:并发执行多个TG频道及异步插件搜索,显著提升搜索速度;工作池设计,高效管理并发任务
  • 网盘类型分类:自动识别多种网盘链接,按类型归类展示
  • 智能排序:基于插件等级、时间新鲜度和优先关键词的多维度综合排序算法
  • 异步插件系统:支持通过插件扩展搜索来源,支持"尽快响应,持续处理"的异步搜索模式,解决了某些搜索源响应时间长的问题。详情参考插件开发指南,AI 辅助开发可参考PanSou 插件开发 Skill 使用说明
  • 二级缓存:分片内存+分片磁盘缓存机制,大幅提升重复查询速度和并发性能

开发者文档

支持的网盘类型

百度网盘 (baidu)、阿里云盘 (aliyun)、夸克网盘 (quark)、光鸭云盘 (guangya)、天翼云盘 (tianyi)、UC网盘 (uc)、移动云盘 (mobile)、115网盘 (115)、PikPak (pikpak)、迅雷网盘 (xunlei)、123网盘 (123)、磁力链接 (magnet)、电驴链接 (ed2k)、其他 (others)

快速开始

在 Github 上先
本项目,并点上 Star !!!

使用Docker部署

qqpd搜索插件文档
gying搜索插件文档
weibo搜索插件文档
常见问题总结
TG/QQ频道/插件/微博

**1、前后端集成版**

直接使用Docker命令

一键启动,开箱即用

text
docker run -d --name pansou -p 80:80 ghcr.io/fish2018/pansou-web

使用Docker Compose(推荐)

text
# 下载配置文件
curl -o docker-compose.yml https://raw.githubusercontent.com/fish2018/pansou-web/refs/heads/main/docker-compose.yml

# 启动服务
docker-compose up -d

# 查看日志
docker-compose logs -f

**2、纯后端API版**

直接使用Docker命令

bash
docker run -d --name pansou -p 8888:8888 ghcr.io/fish2018/pansou:latest

使用Docker Compose(推荐)

bash
# 下载配置文件
curl -o docker-compose.yml  https://raw.githubusercontent.com/fish2018/pansou/refs/heads/main/docker-compose.yml

# 启动服务
docker-compose up -d

# 访问服务
http://localhost:8888

从源码安装

环境要求

  • Go 1.18+
  • 可选:SOCKS5代理(用于访问受限地区的Telegram站点)
  1. 克隆仓库
bash
git clone https://github.com/fish2018/pansou.git
cd pansou
  1. 配置环境变量(可选)

基础配置

环境变量 描述 默认值 说明
PORT 服务端口 8888 修改服务监听端口
PROXY SOCKS5代理 无 如:PROXY=socks5://127.0.0.1:1080
HTTPS_PROXY/HTTP_PROXY HTTPS/HTTP代理 无 如:HTTPS_PROXY=http://127.0.0.1:1080,HTTP_PROXY=http://127.0.0.1:1080
CHANNELS 默认搜索的TG频道 tgsearchers3 多个频道用逗号分隔
ENABLED_PLUGINS 指定启用插件,多个插件用逗号分隔 无 必须显式指定

认证配置(可选)

PanSou支持可选的安全认证功能,默认关闭。开启后,所有API接口(除登录接口外)都需要提供有效的JWT Token。详见认证系统设计文档。

环境变量 描述 默认值 说明
AUTH_ENABLED 是否启用认证 false 设置为true启用认证功能
AUTH_USERS 用户账号配置 无 格式:user1:pass1,user2:pass2
AUTH_TOKEN_EXPIRY Token有效期(小时) 24 JWT Token的有效时长
AUTH_JWT_SECRET JWT签名密钥 自动生成 用于签名Token,建议手动设置

认证配置示例:

bash
# 启用认证并配置单个用户
docker run -d --name pansou -p 8888:8888 \
  -e AUTH_ENABLED=true \
  -e AUTH_USERS=admin:admin123 \
  -e AUTH_TOKEN_EXPIRY=24 \
  ghcr.io/fish2018/pansou:latest

# 配置多个用户
docker run -d --name pansou -p 8888:8888 \
  -e AUTH_ENABLED=true \
  -e AUTH_USERS=admin:pass123,user1:pass456,user2:pass789 \
  ghcr.io/fish2018/pansou:latest

认证API接口:

  • POST /api/auth/login - 用户登录,获取Token
  • POST /api/auth/verify - 验证Token有效性
  • POST /api/auth/logout - 退出登录(客户端删除Token)

使用Token调用API:

bash
# 1. 登录获取Token
curl -X POST http://localhost:8888/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin123"}'

# 响应:{"token":"eyJhbGc...","expires_at":1234567890,"username":"admin"}

# 2. 使用Token调用搜索API
curl -X POST http://localhost:8888/api/search \
  -H "Authorization: Bearer eyJhbGc..." \
  -H "Content-Type: application/json" \
  -d '{"kw":"速度与激情"}'

高级配置(默认值即可)

点击展开高级配置选项(通常不需要修改)

环境变量 描述 默认值
CONCURRENCY 并发搜索数 自动计算
CACHE_TTL 缓存有效期(分钟) 60
CACHE_MAX_SIZE 最大缓存大小(MB) 100
PLUGIN_TIMEOUT 插件超时时间(秒) 30
ASYNC_RESPONSE_TIMEOUT 快速响应超时(秒) 4
ASYNC_LOG_ENABLED 异步插件详细日志 true
CACHE_PATH 缓存文件路径 ./cache
SHARD_COUNT 缓存分片数量 8
CACHE_WRITE_STRATEGY 缓存写入策略(immediate/hybrid) hybrid
ENABLE_COMPRESSION 是否启用压缩 false
MIN_SIZE_TO_COMPRESS 最小压缩阈值(字节) 1024
GC_PERCENT Go GC触发百分比 50
ASYNC_MAX_BACKGROUND_WORKERS 最大后台工作者数量 CPU核心数×5
ASYNC_MAX_BACKGROUND_TASKS 最大后台任务数量 工作者数×5
ASYNC_CACHE_TTL_HOURS 异步缓存有效期(小时) 1
ASYNC_PLUGIN_ENABLED 异步插件是否启用 true
HTTP_READ_TIMEOUT HTTP读取超时(秒) 自动计算
HTTP_WRITE_TIMEOUT HTTP写入超时(秒) 自动计算
HTTP_IDLE_TIMEOUT HTTP空闲超时(秒) 120
HTTP_MAX_CONNS HTTP最大连接数 自动计算
  1. 构建
linux
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -extldflags '-static'" -o pansou .
  1. 运行
bash
./pansou

其他配置参考

点击展开 supervisor 配置参考

text
[program:pansou]
environment=PORT=8888,CHANNELS="tgsearchers4,Aliyun_4K_Movies,bdbdndn11,yunpanx,bsbdbfjfjff,yp123pan,sbsbsnsqq,yunpanxunlei,tianyifc,BaiduCloudDisk,txtyzy,peccxinpd,gotopan,PanjClub,kkxlzy,baicaoZY,MCPH01,bdwpzhpd,ysxb48,jdjdn1111,yggpan,MCPH086,zaihuayun,Q66Share,Oscar_4Kmovies,ucwpzy,shareAliyun,alyp_1,dianyingshare,Quark_Movies,XiangxiuNBB,ydypzyfx,ucquark,xx123pan,yingshifenxiang123,zyfb123,tyypzhpd,tianyirigeng,cloudtianyi,hdhhd21,Lsp115,oneonefivewpfx,qixingzhenren,taoxgzy,Channel_Shares_115,tyysypzypd,vip115hot,wp123zy,yunpan139,yunpan189,yunpanuc,yydf_hzl,leoziyuan,pikpakpan,Q_dongman,yoyokuakeduanju",ENABLED_PLUGINS="labi,zhizhen,shandian,duoduo,muou"
command=/home/work/pansou/pansou
directory=/home/work/pansou
autostart=true
autorestart=true
startsecs=5
startretries=3
exitcodes=0
stopwaitsecs=10
stopasgroup=true
killasgroup=true

点击展开 nginx 配置参考

text
server {
    listen 80;
    server_name pansou.252035.xyz;

    # 将 HTTP 重定向到 HTTPS
    return 301 https://$host$request_uri;
}

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=60r/m;

server {
    listen 443 ssl http2;
    server_name pansou.252035.xyz;

    access_log /home/work/logs/pansou.log;

    # 证书和密钥路径
    ssl_certificate /etc/letsencrypt/live/252035.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/252035.xyz/privkey.pem;

    # 增强 SSL 安全性
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
    ssl_prefer_server_ciphers on;

    # 后端代理,应用限流
    location / {
        # 应用限流规则
        limit_req zone=api_limit burst=10 nodelay;
        # 当超过限制时返回 429 状态码
        limit_req_status 429;

        proxy_pass http://127.0.0.1:8888;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

API文档

认证说明

当启用认证功能(AUTH_ENABLED=true)时,除登录和健康检测接口外的所有API接口都需要提供有效的JWT Token。

请求头格式:

text
Authorization: Bearer <your-jwt-token>

获取Token:

  1. 调用登录接口获取Token(详见下方认证API)
  2. 在后续所有API请求的Header中添加Authorization: Bearer <token>
  3. Token过期后需要重新登录获取新Token

示例:

bash
# 未启用认证时
curl -X POST http://localhost:8888/api/search \
  -H "Content-Type: application/json" \
  -d '{"kw":"速度与激情"}'

# 启用认证时
curl -X POST http://localhost:8888/api/search \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGc..." \
  -d '{"kw":"速度与激情"}'

认证API

用户登录

获取JWT Token用于后续API调用。

接口地址:/api/auth/login
请求方法:POST
Content-Type:application/json
是否需要认证:否

请求参数:

参数名 类型 必填 描述
username string 是 用户名
password string 是 密码

请求示例:

bash
curl -X POST http://localhost:8888/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin123"}'

成功响应:

json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": 1234567890,
  "username": "admin"
}

错误响应:

json
{
  "error": "用户名或密码错误"
}

验证Token

验证当前Token是否有效。

接口地址:/api/auth/verify
请求方法:POST
是否需要认证:是

请求示例:

bash
curl -X POST http://localhost:8888/api/auth/verify \
  -H "Authorization: Bearer eyJhbGc..."

成功响应:

json
{
  "valid": true,
  "username": "admin"
}

退出登录

退出当前登录(客户端删除Token即可)。

接口地址:/api/auth/logout
请求方法:POST
是否需要认证:否

请求示例:

bash
curl -X POST http://localhost:8888/api/auth/logout

成功响应:

json
{
  "message": "退出成功"
}

搜索API

搜索网盘资源。

接口地址:/api/search
请求方法:POST 或 GET
Content-Type:application/json(POST方法)
是否需要认证:取决于AUTH_ENABLED配置

POST请求参数:

参数名 类型 必填 描述
kw string 是 搜索关键词
channels string[] 否 搜索的频道列表,不提供则使用默认配置
conc number 否 并发搜索数量,不提供则自动设置为频道数+插件数+10
refresh boolean 否 强制刷新,不使用缓存,便于调试和获取最新数据
res string 否 结果类型:all(返回所有结果)、results(仅返回results)、merge(仅返回merged_by_type),默认为merge
src string 否 数据来源类型:all(默认,全部来源)、tg(仅Telegram)、plugin(仅插件)
plugins string[] 否 指定搜索的插件列表,不指定则搜索全部插件
cloud_types string[] 否 指定返回的网盘类型列表,支持:baidu、aliyun、quark、guangya、tianyi、uc、mobile、115、pikpak、xunlei、123、magnet、ed2k,不指定则返回所有类型
ext object 否 扩展参数,用于传递给插件的自定义参数,如{"title_en":"English Title", "is_all":true}
filter object 否 过滤配置,用于过滤返回结果。格式:{"include":["关键词1","关键词2"],"exclude":["排除词1","排除词2"]}。include为包含关键词列表(OR关系),exclude为排除关键词列表(OR关系)

GET请求参数:

参数名 类型 必填 描述
kw string 是 搜索关键词
channels string 否 搜索的频道列表,使用英文逗号分隔多个频道,不提供则使用默认配置
conc number 否 并发搜索数量,不提供则自动设置为频道数+插件数+10
refresh boolean 否 强制刷新,设置为"true"表示不使用缓存
res string 否 结果类型:all(返回所有结果)、results(仅返回results)、merge(仅返回merged_by_type),默认为merge
src string 否 数据来源类型:all(默认,全部来源)、tg(仅Telegram)、plugin(仅插件)
plugins string 否 指定搜索的插件列表,使用英文逗号分隔多个插件名,不指定则搜索全部插件
cloud_types string 否 指定返回的网盘类型列表,使用英文逗号分隔多个类型,支持:baidu、aliyun、quark、guangya、tianyi、uc、mobile、115、pikpak、xunlei、123、magnet、ed2k,不指定则返回所有类型
ext string 否 JSON格式的扩展参数,用于传递给插件的自定义参数,如{"title_en":"English Title", "is_all":true}
filter string 否 JSON格式的过滤配置,用于过滤返回结果。格式:{"include":["关键词1","关键词2"],"exclude":["排除词1","排除词2"]}

POST请求示例:

bash
# 未启用认证
curl -X POST http://localhost:8888/api/search \
  -H "Content-Type: application/json" \
  -d '{
    "kw": "速度与激情",
    "channels": ["tgsearchers3", "xxx"],
    "conc": 2,
    "refresh": true,
    "res": "merge",
    "src": "all",
    "plugins": ["jikepan"],
    "cloud_types": ["baidu", "quark"],
    "ext": {
      "title_en": "Fast and Furious",
      "is_all": true
    }
  }'

# 启用认证时(需要添加Authorization头)
curl -X POST http://localhost:8888/api/search \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -d '{
    "kw": "速度与激情",
    "res": "merge"
  }'

# 使用过滤器(只返回包含“合集”或“全集”,且不包含“预告”或“花絮”的结果)
curl -X POST http://localhost:8888/api/search \
  -H "Content-Type: application/json" \
  -d '{
    "kw": "唐朝诡事录",
    "filter": {
      "include": ["合集", "全集"],
      "exclude": ["预告", "花絮"]
    }
  }'

GET请求示例:

bash
# 未启用认证
curl "http://localhost:8888/api/search?kw=速度与激情&res=merge&src=tg"

# 启用认证时(需要添加Authorization头)
curl "http://localhost:8888/api/search?kw=速度与激情&res=merge" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

# 使用过滤器(GET方式需要URL编码JSON)
curl "http://localhost:8888/api/search?kw=唐朝诡事录&filter=%7B%22include%22%3A%5B%22合集%22%2C%22全集%22%5D%2C%22exclude%22%3A%5B%22预告%22%5D%7D"

成功响应:

json
{
  "total": 15,
  "results": [
    {
      "message_id": "12345",
      "unique_id": "channel-12345",
      "channel": "tgsearchers3",
      "datetime": "2023-06-10T14:23:45Z",
      "title": "速度与激情全集1-10",
      "content": "速度与激情系列全集,1080P高清...",
      "links": [
        {
          "type": "baidu",
          "url": "https://pan.baidu.com/s/1abcdef",
          "password": "1234",
          "datetime": "2023-06-10T14:23:45Z",
          "work_title": "速度与激情全集1-10"
        }
      ],
      "tags": ["电影", "合集"],
      "images": [
        "https://cdn1.cdn-telegram.org/file/xxx.jpg"
      ]
    },
    // 更多结果...
  ],
  "merged_by_type": {
    "baidu": [
      {
        "url": "https://pan.baidu.com/s/1abcdef",
        "password": "1234",
        "note": "速度与激情全集1-10",
        "datetime": "2023-06-10T14:23:45Z",
        "source": "tg:频道名称",
        "images": [
          "https://cdn1.cdn-telegram.org/file/xxx.jpg"
        ]
      },
      // 更多百度网盘链接...
    ],
    "quark": [
      {
        "url": "https://pan.quark.cn/s/xxxx",
        "password": "",
        "note": "凡人修仙传",
        "datetime": "2023-06-10T15:30:22Z",
        "source": "plugin:插件名",
        "images": []
      }
    ],
    "aliyun": [
      // 阿里云盘链接...
    ]
    // 更多网盘类型...
  }
}

字段说明:

SearchResult对象:

  • message_id: 消息ID
  • unique_id: 全局唯一标识符
  • channel: 来源频道名称
  • datetime: 消息发布时间
  • title: 消息标题
  • content: 消息内容
  • links: 网盘链接数组
  • tags: 标签数组(可选)
  • images: TG消息中的图片链接数组(可选)

Link对象:

  • type: 网盘类型(baidu、quark、aliyun等)
  • url: 网盘链接地址
  • password: 提取码/密码
  • datetime: 链接更新时间(可选)
  • work_title: 作品标题(可选)
    • 用于区分同一消息中多个作品的链接
    • 当一条消息包含≤4个链接时,所有链接使用相同的work_title
    • 当一条消息包含>4个链接时,系统会智能识别每个链接对应的作品标题

MergedLink对象:

  • url: 网盘链接地址
  • password: 提取码/密码
  • note: 资源说明/标题
  • datetime: 链接更新时间
  • source: 数据来源标识
    • tg:频道名称: 来自Telegram频道
    • plugin:插件名: 来自指定插件
    • unknown: 未知来源
  • images: TG消息中的图片链接数组(可选)
    • 仅在来源为Telegram频道且消息包含图片时出现

错误响应:

json
// 参数错误
{
  "code": 400,
  "message": "关键词不能为空"
}

// 未授权(启用认证但未提供Token)
{
  "error": "未授权:缺少认证令牌",
  "code": "AUTH_TOKEN_MISSING"
}

// Token无效或过期
{
  "error": "未授权:令牌无效或已过期",
  "code": "AUTH_TOKEN_INVALID"
}

链接检测API

检测指定网盘分享链接当前是否有效,适合前端结果页按需做可见项检测,也支持批量调试和服务端缓存复用。

接口地址:/api/check/links
请求方法:POST
Content-Type:application/json
是否需要认证:取决于AUTH_ENABLED配置

请求参数:

参数名 类型 必填 描述
items object[] 是 待检测链接数组,至少提供一项
items[].disk_type string 是 网盘类型,支持:baidu、aliyun、quark、tianyi、uc、mobile、115、xunlei、123
items[].url string 是 完整分享链接
items[].password string 否 提取码/密码,未拼接在链接中时可传
proxy_url string 否 本次检测请求使用的代理地址,位于请求根节点,支持 http://、https://、socks5://、socks5h://
proxy string 否 proxy_url 的兼容别名,位于请求根节点;同时传入时以 proxy_url 为准
view_token string 否 视图标识,用于区分当前前端检测批次

代理行为说明:

  • proxy_url/proxy 只影响当前 /api/check/links 请求,不会修改服务进程的全局代理配置。
  • 单次批量检测共用同一个代理;如需轮换出口IP,可由调用方在不同请求中传入不同代理。
  • 未传 proxy_url/proxy 时,检测服务沿用启动时的全局HTTP客户端配置。
  • 使用代理时,检测缓存会按代理地址隔离,避免不同出口IP复用同一检测结果。
  • 代理地址格式非法或协议不支持时返回 400,不会静默降级为直连。

请求示例:

bash
# 未启用认证
curl -X POST http://localhost:8888/api/check/links \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "disk_type": "quark",
        "url": "https://pan.quark.cn/s/abcdefg",
        "password": "1234"
      },
      {
        "disk_type": "xunlei",
        "url": "https://pan.xunlei.com/s/abcdefg?pwd=1234"
      },
      {
        "disk_type": "115",
        "url": "https://115cdn.com/s/abcdefg?password=1234"
      }
    ],
    "view_token": "quark-1710000000000"
  }'

# 指定本次检测使用代理,避免固定出口IP频繁触发风控
curl -X POST http://localhost:8888/api/check/links \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "disk_type": "quark",
        "url": "https://pan.quark.cn/s/abcdefg"
      }
    ],
    "proxy_url": "socks5://127.0.0.1:1080"
  }'

# 启用认证时
curl -X POST http://localhost:8888/api/check/links \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGc..." \
  -d '{
    "items": [
      {
        "disk_type": "baidu",
        "url": "https://pan.baidu.com/s/1abcdef?pwd=1234"
      }
    ]
  }'

成功响应:

json
{
  "results": [
    {
      "disk_type": "quark",
      "url": "https://pan.quark.cn/s/abcdefg",
      "normalized_url": "https://pan.quark.cn/s/abcdefg?pwd=1234",
      "state": "ok",
      "cache_hit": false,
      "checked_at": 1710000000000,
      "expires_at": 1710086400000,
      "summary": "链接有效"
    },
    {
      "disk_type": "xunlei",
      "url": "https://pan.xunlei.com/s/abcdefg?pwd=1234",
      "normalized_url": "https://pan.xunlei.com/s/abcdefg?pwd=1234",
      "state": "bad",
      "cache_hit": true,
      "checked_at": 1710000100000,
      "expires_at": 1710021700000,
      "summary": "链接失效"
    }
  ]
}

状态说明:

  • ok:链接有效
  • bad:链接失效
  • locked:需要提取码或密码错误
  • unsupported:当前平台暂不支持检测
  • uncertain:检测失败或结果不确定

字段说明:

  • results: 检测结果数组
  • results[].disk_type: 网盘类型
  • results[].url: 原始传入链接
  • results[].normalized_url: 规范化后的链接
  • results[].state: 检测状态
  • results[].cache_hit: 是否命中服务端检测缓存
  • results[].checked_at: 最近一次检测时间戳(毫秒)
  • results[].expires_at: 当前缓存过期时间戳(毫秒)
  • results[].summary: 状态说明文本

错误响应:

json
// 请求参数无效
{
  "code": 400,
  "message": "无效的检测请求: Key: 'CheckRequest.Items' Error:Field validation for 'Items' failed on the 'required' tag"
}

// items 为空
{
  "code": 400,
  "message": "items不能为空"
}

// 代理参数无效
{
  "code": 400,
  "message": "无效的代理参数: 不支持的代理协议: ftp"
}

// 未授权(启用认证但未提供Token)
{
  "error": "未授权:缺少认证令牌",
  "code": "AUTH_TOKEN_MISSING"
}

健康检查

检查API服务是否正常运行。

接口地址:/api/health
请求方法:GET
是否需要认证:否(公开接口)

请求示例:

bash
curl http://localhost:8888/api/health

成功响应:

json
{
  "status": "ok",
  "auth_enabled": true,
  "plugins_enabled": true,
  "plugin_count": 16,
  "plugins": [
    "pansearch",
    "panta", 
    "qupansou",
    "hunhepan",
    "jikepan",
    "pan666",
    "panyq",
    "susu",
    "xuexizhinan",
    "hdr4k",
    "labi",
    "shandian",
    "duoduo",
    "muou",
    "wanou",
    "ouge",
    "zhizhen",
    "huban"
  ],
  "channels_count": 1,
  "channels": [
    "tgsearchers3"
  ]
}

字段说明:

  • status: 服务状态,"ok"表示正常
  • auth_enabled: 是否启用认证功能
  • plugins_enabled: 是否启用插件
  • plugin_count: 已启用的插件数量
  • plugins: 已启用的插件列表
  • channels_count: 配置的频道数量
  • channels: 配置的频道列表

📄 许可证

本项目采用 MIT 许可证。详情请见 LICENSE 文件。

⭐ Star 历史


File: plugin/gying/README.md

Gying 搜索插件

📖 简介

Gying 是 PanSou 的搜索插件,用于抓取 Gying 站点的影视资源。当前实现里,https://www.gying.net 只是默认站点地址,实际支持在管理页里配置自定义域名 / 站点地址;搜索时会聚合所有有效账户的结果。

✨ 核心特性

  • ✅ 多用户支持 - 每个用户独立配置,互不干扰
  • ✅ 自定义域名 - 支持在管理页配置站点地址,登录/搜索/详情请求都基于当前 base_url
  • ✅ 用户名密码登录 - 支持使用用户名和密码登录
  • ✅ 智能去重 - 多用户搜索时自动去重
  • ✅ 负载均衡 - 任务均匀分配,避免单用户限流
  • ✅ 内存缓存 - 用户数据缓存到内存,搜索性能极高
  • ✅ 持久化存储 - Cookie和用户配置自动保存,重启不丢失
  • ✅ Web管理界面 - 一站式配置,简单易用
  • ✅ RESTful API - 支持程序化调用
  • ✅ 默认账户自动登录 - 插件启动时自动使用默认账户登录
  • ✅ 反爬挑战处理 - 内置 cloudscraper 和挑战页求解逻辑,可自动处理新版“浏览器安全验证”页面,并在登录失效时自动重登重试

🚀 快速开始

步骤1: 启动服务

bash
cd /Users/macbookpro/Desktop/fish2018/pansou
go run main.go

# 或者编译后运行
go build -o pansou main.go
./pansou

步骤2: 访问管理页面

如果需要添加更多账户或管理现有账户,可以访问管理页面:

text
http://localhost:8888/gying/你的用户名

示例:

text
http://localhost:8888/gying/myusername

系统会自动:

  1. 根据用户名生成专属64位hash(不可逆)
  2. 重定向到专属管理页面:http://localhost:8888/gying/{hash}
  3. 显示登录表单供手动登录

📌 提示:请收藏hash后的URL(包含你的专属hash),方便下次访问。

步骤3: 先配置站点地址(重要)

进入管理页后,建议先在“站点地址”区域配置你的自定义域名 / 站点地址,再进行登录。

当前代码逻辑是:

  • 默认站点地址为 https://www.gying.net
  • 初始会话页:{base_url}
  • 登录接口:{base_url}/user/login
  • 搜索页:{base_url}/search?q={keyword}&type=0&mode=2
  • 详情接口:{base_url}/res/downurl/{type}/{id}

也就是说,插件不是固定写死抓某一个域名,而是所有核心请求都基于当前 base_url 动态拼接。

📌 提示:

  • 站点地址会保存到 cache/gying_users/gying_config.json
  • 修改站点地址后,插件会清空当前登录状态和搜索缓存,需要重新登录
  • 站点地址只允许填写纯域名,例如 https://your-gying-domain.com

步骤4: 手动登录

在"登录状态"区域输入:

  • 用户名
  • 密码

点击"登录"按钮。

步骤5: 开始搜索

在PanSou主页搜索框输入关键词,系统会自动聚合所有用户的Gying搜索结果!

bash
# 通过API搜索
curl "http://localhost:8888/api/search?kw=遮天"

# 只搜索插件(包括gying)
curl "http://localhost:8888/api/search?kw=遮天&src=plugin"

📡 API文档

统一接口

所有操作通过统一的POST接口:

text
POST /gying/{hash}
Content-Type: application/json

{
  "action": "操作类型",
  ...其他参数
}

API列表

Action 说明 需要登录
get_status 获取状态 ❌
get_config 获取当前站点地址 ❌
update_config 更新站点地址 ❌
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": "pansou",
    "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": "xxx"
  }
}

失败响应:

json
{
  "success": false,
  "message": "登录失败: 用户名或密码错误"
}

3️⃣ get_config - 获取站点地址

请求:

bash
curl -X POST "http://localhost:8888/gying/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "get_config"}'

成功响应:

json
{
  "success": true,
  "message": "获取成功",
  "data": {
    "base_url": "https://www.gying.net"
  }
}

4️⃣ update_config - 更新站点地址

请求:

bash
curl -X POST "http://localhost:8888/gying/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "update_config", "base_url": "https://your-gying-domain.com"}'

成功响应:

json
{
  "success": true,
  "message": "站点地址已保存,当前登录状态已清空,请重新登录",
  "data": {
    "base_url": "https://your-gying-domain.com"
  }
}

说明:

  • 保存前会自动做域名标准化
  • 不允许带路径、查询参数或锚点
  • 如果域名发生变化,所有用户当前 Cookie 会被清空并回到 pending

5️⃣ 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"
  }
}

请求:

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": ""
          }
        ]
      }
    ]
  }
}

🔍 当前实现补充

下面这些是当前 gying.go 已经实现、但原始 README 没写全的部分:

1. 反爬挑战处理

所有关键请求都会经过 requestWithChallengeRetry。如果检测到站点返回的验证页,例如:

  • 浏览器安全验证
  • 安全验证
  • 正在进行浏览器计算验证
  • 旧版 正在确认你是不是机器人

插件会进入 solveBotChallenge 自动求解后再重试原请求。

当前同时兼容三类验证结构:

  • 远程 PoW:验证页只加载 powSolve-*.js,并通过 Set-Cookie: browser_pow=... 标记挑战;插件请求 {base_url}/res/pow 获取 N/x/t,计算后 POST {base_url}/res/pow 提交 y。
  • 内嵌 PoW:const json={id,N,x,t};const jss=...,插件用 Go 的 math/big 直接计算 y = y*y % N 共 t 次,然后提交 action=verify&id={id}&y={y}。
  • 旧版哈希枚举:const json={id,challenge,diff,salt};const jss=...,插件枚举 nonce,匹配 sha256(strconv.Itoa(nonce)+salt) 后提交 nonce[]。

PoW 求解不依赖 Playwright、Puppeteer、Selenium 或真实浏览器环境;它是对站点 worker 脚本的轻量算法复现。为贴近前端行为,计算完成不足 3 秒时会补齐等待后再提交验证。

验证通过后,站点通常会下发 browser_verified 这类验证态 Cookie。当前插件会:

  • 在登录阶段导出并保存当前 Cookie 快照
  • 在搜索阶段如果拿到新的验证态 Cookie,会自动回写到用户文件
  • 在插件重启后优先使用已保存 Cookie 恢复会话,而不是默认先重登

这一步的目的是尽量复用浏览器验证结果,减少重复触发 challenge。

1.1 2026-05 登录验证循环问题

曾出现过一种登录失败场景:日志里先显示 Challenge验证成功,但随后重试原请求仍然返回 浏览器安全验证,最终报错 重试后仍然进入机器人验证页。

排查结论:新版验证态不只依赖 browser_verified Cookie,还会受同一次客户端请求指纹影响。此前 requestWithChallengeRetry 里 GET 请求直接使用 cloudscraper 内部的 http.Client.Do,而验证提交使用 scraper.Post;这会让“访问验证页 / 提交验证 / 重试原请求”没有完整经过 cloudscraper 的同一套浏览器化请求处理,导致站点认为重试请求仍然需要验证。

当前修复:

  • GET 请求统一使用 scraper.Get
  • POST 请求继续使用 scraper.Post
  • challenge 页、验证提交、重试原请求都保持在同一个 cloudscraper 会话与请求链路里
  • 调试日志中的登录 POST 数据已对密码脱敏,只输出密码长度

验证方式:

  1. 使用错误密码登录,应能通过 challenge 并返回站点的账号密码错误 JSON,而不是停在机器人验证页。
  2. 使用正确密码登录,应返回 登录成功,并在 Cookie 快照中包含 PHPSESSID、app_auth、browser_verified。
  3. 调用 test_search 搜索关键词,应能访问搜索页和详情接口并返回网盘链接。

1.2 2026-05 新版 PoW 参数变更

新版验证页不再只返回 challenge/diff/salt,而是返回类似:

javascript
const json={"id":"...","N":"...","x":"...","t":800000};const jss={...};

对应的 worker 逻辑等价于:

text
N = BigInt("0x" + json.N)
y = BigInt("0x" + json.x)
repeat json.t times:
    y = (y * y) % N

验证提交表单为:

text
action=verify&id={id}&y={hex(y)}

此前代码只识别旧版 challenge/diff/salt,会在登录或搜索进入新版验证页时报 验证数据无效。当前实现已在 solveBotChallenge 中自动分流新版 PoW 和旧版哈希枚举。

因为新版验证会增加登录耗时,配套前端 pansou-web 也把 Gying 登录 / 测试搜索请求超时放宽,并在登录请求超时后回查一次状态,避免后端已登录成功但前端误报“登录失败”。

1.3 2026-05 远程 PoW 流程

站点后来又调整为“页面不再内嵌 const json”的流程。验证页只包含:

javascript
const jss={"worker":"//static.filejin.ru/static/file/js/pow.worker-...js"};

同时响应头下发:

text
Set-Cookie: browser_pow={challenge_id}; Max-Age=300; HttpOnly; SameSite=Lax

前端脚本的真实流程是:

  1. GET /res/pow 获取 N/x/t
  2. 本地计算 y = y*y % N 共 t 次
  3. POST /res/pow,表单为 y={hex(y)}
  4. 成功后服务端返回 {"success":true,"challenge_id":"..."} 并下发 browser_verified
  5. 原请求重新加载

当前插件在识别到这类验证页时,会自动走 solveRemotePowChallenge,用同一个 cloudscraper 会话完成 /res/pow 获取、计算、提交,再重试搜索或详情请求。

2. 搜索链路不是单接口

当前搜索逻辑不是直接调一个公开 API,而是:

  1. 访问搜索页 /{base_url}/search?q={keyword}&type=0&mode=2
  2. 从 HTML 中提取 _obj.search = {...} 内嵌 JSON
  3. 再并发请求详情接口 /{base_url}/res/downurl/{type}/{id}
  4. 从详情里提取网盘链接和磁力链接

2.1 代理前提

当前目标站点在不同网络出口下行为差异很大。

实测中,如果直连当前 gying 域名,搜索入口有时不会进入 challenge,而是直接返回一个由 Angie 输出的 404 Not Found 假页;这类 404 不是插件参数错误,而是站点对当前网络出口的拒绝或伪装响应。

因此,gying 在需要海外网络时,必须确保插件自己的请求链也走代理。

当前实现会显式复用主程序的全局 PROXY 配置,并应用到 cloudscraper 内部 transport,包括:

  • 启动时使用已保存 Cookie 恢复会话
  • 用户名密码登录
  • 搜索页请求
  • 详情接口请求

例如:

bash
PROXY=socks5://127.0.0.1:7897 ENABLED_PLUGINS=gying go run .

3. 自动重新登录

如果搜索页或详情接口返回下列任一失效信号,插件会尝试使用已保存的加密密码重新登录,然后用新的会话重试搜索:

  • HTTP 403
  • _BT.PC.HTML('login')
  • _BT.PC.HTML('nologin')
  • 页面标题或正文出现“未登录,访问受限”
  • 详情 JSON 里的 code == 403

4. 当前管理页的实际定位

管理页是“一个 hash 对应一个用户槽位”,不是统一多账号后台;但插件搜索时,仍然会把所有 active 用户的结果汇总后去重。


🔧 配置说明

站点地址配置(重要)

站点地址相关逻辑对应 getBaseURL()、updateBaseURL()、getLoginPageURL()、getLoginAPIURL() 等方法。

配置规则:

  • 默认值:https://www.gying.net
  • 支持通过管理页动态修改
  • 自动补全 https://
  • 只允许 http:// 或 https://
  • 不允许包含路径、参数、锚点

建议实际使用顺序:

  1. 访问 /gying/你的用户名
  2. 进入 hash 页面后先配置站点地址
  3. 再登录账号
  4. 最后再使用 test_search 或正式搜索

当前 gying 的会话至少包含两类状态:

  • 登录态 Cookie:例如 PHPSESSID、app_auth
  • 验证态 Cookie:例如 browser_verified

两者作用不同:

  • 没有登录态,搜索可能直接进入 nologin
  • 没有验证态,即使已登录,也可能先进入“浏览器安全验证”

当前插件的处理链路是:

  1. 登录或重登时保存完整 Cookie 快照
  2. 搜索过程中如果站点补发新的验证态 Cookie,会同步回写到 cache/gying_users/{hash}.json
  3. 插件重启后优先恢复这份 Cookie 快照,以复用验证结果

环境变量(可选)

bash
# 缓存目录(默认 ./cache)
export CACHE_PATH="./cache"

# 目标站点如果需要海外网络,建议显式配置 PROXY;gying 会复用这条代理到自己的 cloudscraper 请求链
export PROXY="socks5://127.0.0.1:7897"

# Hash Salt(推荐自定义,增强安全性)
export GYING_HASH_SALT="your-custom-salt-here"

# 当前主流程的密码加解密未使用这个环境变量;它只影响可选的 Cookie 加密辅助函数
export GYING_ENCRYPTION_KEY="your-32-byte-key-here!!!!!!!!!!"

代码内配置

在 gying.go 中修改:

go
const (
    MaxConcurrentUsers   = 10    // 最多使用的用户数(搜索时)
    MaxConcurrentDetails = 50    // 最大并发详情请求数
    DebugLog             = false // 调试日志开关
)

默认账户配置

在 gying.go 中修改默认账户:

go
var DefaultAccounts = []struct {
    Username string
    Password string
}{
    // 可以添加更多默认账户
    // {"user2", "password2"},
}

参数说明:

参数 默认值 说明 建议
MaxConcurrentUsers 10 单次搜索最多使用的用户数 10-20足够
MaxConcurrentDetails 50 最大并发详情请求数 50-100
DebugLog false 是否开启调试日志 生产环境false

📂 数据存储

存储位置

text
cache/gying_users/gying_config.json
cache/gying_users/{hash}.json

数据结构

站点配置文件:

json
{
  "base_url": "https://your-gying-domain.com",
  "updated_at": "2026-04-03T12:00:00+08:00"
}

用户文件:

json
{
  "hash": "abc123...",
  "username": "pansou",
  "encrypted_password": "base64-aes-gcm",
  "cookie": "PHPSESSID=xxx; app_auth=xxx; browser_verified=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: 原始用户名(存储)
  • encrypted_password: 加密后的密码,用于搜索失效或 403 后自动重新登录
  • cookie: 登录 Cookie 快照,常见值包括 PHPSESSID、app_auth、browser_verified
  • status: 用户状态(pending/active/expired)
  • expire_at: Cookie过期时间(121天)

File: plugin/qqpd/README.md

QQ频道搜索插件 (QQPD)

📖 简介

QQPD是PanSou的QQ频道搜索插件,支持多用户登录QQ频道并配置频道列表,在搜索时自动聚合所有用户的频道资源。

✨ 核心特性

  • ✅ 多用户支持 - 每个用户独立配置,互不干扰
  • ✅ 扫码登录 - 手机QQ扫码,自动获取Cookie
  • ✅ Session保活 - 自动定期访问保持Cookie活跃,防止失效 🆕
  • ✅ 智能去重 - 多用户配置相同频道时自动去重
  • ✅ 负载均衡 - 任务均匀分配,避免单用户限流
  • ✅ 内存缓存 - 用户数据和guild_id缓存到内存,搜索性能极高
  • ✅ 持久化存储 - Cookie和频道配置自动保存,重启不丢失
  • ✅ Web管理界面 - 一站式配置,简单易用
  • ✅ RESTful API - 支持程序化调用

🚀 快速开始

步骤1: 启动服务

bash
cd /Users/macbookpro/Desktop/fish2018/pansou
ENABLED_PLUGINS=qqpd go run main.go

# 或者编译后运行
go build -o pansou main.go
ENABLED_PLUGINS=qqpd ./pansou

步骤2: 访问管理页面

浏览器打开:

text
http://localhost:8888/qqpd/你的QQ号

示例:

text
http://localhost:8888/qqpd/1234567

系统会自动:

  1. 根据QQ号生成专属64位hash(不可逆)
  2. 重定向到专属管理页面:http://localhost:8888/qqpd/{hash}
  3. 显示二维码供扫码登录

📌 提示:请收藏hash后的URL(包含你的专属hash),方便下次访问。

步骤3: 扫码登录

  1. 页面会自动显示QQ登录二维码
  2. 使用手机QQ扫描二维码
  3. 扫码后系统会自动检测登录状态(每2秒检查一次)
  4. 登录成功后自动显示用户信息

步骤4: 配置频道

在"频道管理"区域输入频道号,每行一个:

text
pd97631607
languan8K115
m250319e25

支持格式:

  • ✅ 纯频道号:pd97631607
  • ✅ 完整URL:https://pd.qq.com/g/pd97631607

点击"保存频道配置"按钮。

步骤5: 开始搜索

在PanSou主页搜索框输入关键词,系统会自动聚合所有用户的QQ频道结果!

bash
# 通过API搜索
curl "http://localhost:8888/api/search?kw=遮天"

# 只搜索插件(包括qqpd)
curl "http://localhost:8888/api/search?kw=遮天&src=plugin"

📡 API文档

统一接口

所有操作通过统一的POST接口:

text
POST /qqpd/{hash}
Content-Type: application/json

{
  "action": "操作类型",
  ...其他参数
}

API列表

Action 说明 需要登录 前端调用时机
get_status 获取状态 ❌ 每3秒自动调用
refresh_qrcode 刷新二维码 ❌ 用户点击刷新按钮
check_login 检查登录状态 ❌ 未登录时每2秒调用
logout 退出登录 ✅ 用户点击退出按钮
set_channels 设置频道列表 ✅ 用户点击保存按钮
test_search 测试搜索 ✅ 用户点击搜索按钮

1️⃣ get_status - 获取用户状态

作用:获取当前用户的登录状态、频道配置等信息

请求:

bash
curl -X POST "http://localhost:8888/qqpd/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "get_status"}'

成功响应(已登录):

json
{
  "success": true,
  "message": "获取成功",
  "data": {
    "hash": "1dd868cc...",
    "logged_in": true,
    "status": "active",
    "qq_masked": "1851****32",
    "login_time": "2025-10-24 12:00:00",
    "expire_time": "2035-10-24 12:00:00",
    "expires_in_days": 3650,
    "channels": ["pd97631607", "kuake12345"],
    "channel_count": 2,
    "qrcode_base64": ""
  }
}

成功响应(未登录):

json
{
  "success": true,
  "message": "获取成功",
  "data": {
    "hash": "1dd868cc...",
    "logged_in": false,
    "status": "pending",
    "qq_masked": "",
    "channels": [],
    "channel_count": 0,
    "qrcode_base64": "data:image/png;base64,iVBORw0KGgo..."  // Base64二维码
  }
}

2️⃣ refresh_qrcode - 刷新二维码

作用:强制生成新的二维码(当二维码过期时)

请求:

bash
curl -X POST "http://localhost:8888/qqpd/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "refresh_qrcode"}'

成功响应:

json
{
  "success": true,
  "message": "二维码已刷新",
  "data": {
    "qrcode_base64": "data:image/png;base64,iVBORw0KGgo..."
  }
}

说明:

  • 二维码有效期约2分钟
  • 系统会自动缓存30秒,避免频繁生成
  • 过期后需要点击刷新

3️⃣ check_login - 检查登录状态

作用:检查二维码是否被扫描,登录是否成功(扫码后轮询调用)

请求:

bash
curl -X POST "http://localhost:8888/qqpd/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "check_login"}'

响应(等待扫码):

json
{
  "success": true,
  "message": "等待扫码",
  "data": {
    "login_status": "waiting"
  }
}

响应(登录成功):

json
{
  "success": true,
  "message": "登录成功",
  "data": {
    "login_status": "success",
    "qq_masked": "1851****32"
  }
}

响应(二维码过期):

json
{
  "success": false,
  "message": "二维码已失效,请刷新"
}

说明:

  • 前端未登录时每2秒自动调用
  • 登录成功后前端会停止轮询
  • 后端会自动获取完整Cookie并保存

4️⃣ logout - 退出登录

作用:清除Cookie,退出登录状态

请求:

bash
curl -X POST "http://localhost:8888/qqpd/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "logout"}'

成功响应:

json
{
  "success": true,
  "message": "已退出登录",
  "data": {
    "status": "pending"
  }
}

5️⃣ set_channels - 设置频道列表

作用:配置或更新频道列表(覆盖式更新)

请求:

bash
curl -X POST "http://localhost:8888/qqpd/{hash}" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "set_channels",
    "channels": ["pd97631607", "kuake12345", "https://pd.qq.com/g/languan8K115"]
  }'

成功响应:

json
{
  "success": true,
  "message": "频道列表已更新",
  "data": {
    "channels": ["pd97631607", "kuake12345", "languan8K115"],
    "channel_count": 3,
    "invalid_channels": [],
    "guild_ids_cached": 3
  }
}

说明:

  • 自动提取频道号(支持URL格式)
  • 自动去重
  • 自动获取并缓存guild_id(首次添加频道时)
  • guild_id永久缓存,搜索时0网络请求

作用:在管理页面测试搜索功能

请求:

bash
curl -X POST "http://localhost:8888/qqpd/{hash}" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "test_search",
    "keyword": "遮天",
    "max_results": 10
  }'

参数:

  • keyword(必需):搜索关键词
  • max_results(可选):最大返回数量,默认10

成功响应:

json
{
  "success": true,
  "message": "找到 5 条结果",
  "data": {
    "keyword": "遮天",
    "total_results": 5,
    "channels_searched": ["pd97631607", "kuake12345", "languan8K115"],
    "results": [
      {
        "unique_id": "qqpd-pd97631607-0",
        "title": "遮天 (2023) 臻彩4K.更新至109集",
        "links": [
          {
            "type": "quark",
            "url": "https://pan.quark.cn/s/779d98f49e88",
            "password": ""
          }
        ]
      },
      ...
    ]
  }
}

🔧 配置说明

环境变量(可选)

bash
# Hash Salt(推荐自定义,增强安全性)
export QQPD_HASH_SALT="your-custom-salt-here"

# Cookie加密密钥(32字节,推荐自定义)
export QQPD_ENCRYPTION_KEY="your-32-byte-key-here!!!!!!!!!!"

代码内配置

在 qqpd.go 第33-37行修改:

go
const (
    MaxConcurrentUsers    = 10              // 最多使用的用户数(搜索时)
    MaxConcurrentChannels = 50              // 最大并发频道数
    KeepAliveInterval     = 3 * time.Minute // Session保活间隔
    DebugLog              = false           // 调试日志开关
)

参数说明:

参数 默认值 说明 建议
MaxConcurrentUsers 10 单次搜索最多使用的用户数 10-20足够
MaxConcurrentChannels 50 最大并发频道数 50-100
KeepAliveInterval 3分钟 Session保活间隔 2-5分钟
DebugLog false 是否开启调试日志 生产环境false

📂 数据存储

存储位置

text
cache/qqpd_users/{hash}.json

数据结构

json
{
  "hash": "1dd868cc97f5540db170bb3208a4ad737cd7aea3e8df85535178dcbacfa46300",
  "qq_masked": "123**67",
  "cookie": "p_skey=xxx; uin=xxx; ...",
  "status": "active",
  "channels": ["pd97631607", "kuake12345", "languan8K115"],
  "channel_guild_ids": {
    "pd97631607": "592843764045681811",
    "kuake12345": "987654321098765432",
    "languan8K115": "612109904026776189"
  },
  "created_at": "2025-10-24T12:00:00+08:00",
  "login_at": "2025-10-24T12:05:00+08:00",
  "expire_at": "2035-10-24T12:05:00+08:00",
  "last_access_at": "2025-10-24T13:00:00+08:00"
}

字段说明:

  • hash: 用户唯一标识(SHA256,不可逆推QQ号)
  • qq_masked: 脱敏QQ号(如1851****32)
  • cookie: QQ登录Cookie(明文存储,建议配置加密)
  • status: 用户状态(pending/active/expired)
  • channels: 频道号列表
  • channel_guild_ids: 频道号→guild_id映射(性能优化缓存)
  • expire_at: Cookie过期时间

🔒 安全特性

1. QQ号隐私保护

  • ✅ 不存储明文QQ号:只存储SHA256 hash(64位十六进制)
  • ✅ 不可逆:无法从hash反推QQ号
  • ✅ 加盐hash:支持自定义salt,进一步增强安全性
  • ⚠️ 当前:明文存储到JSON(方便调试)
  • ✅ 可选:通过环境变量配置加密密钥
  • ✅ 建议:生产环境配置QQPD_ENCRYPTION_KEY

3. 自动清理

定期清理任务(每24小时):

  • 删除:状态为expired且30天未访问的用户
  • 标记:90天未访问的用户标记为expired

4. 二维码安全

  • ✅ 每次生成新的qrsig
  • ✅ 30秒缓存,减少暴露
  • ✅ 2分钟自动过期

⚙️ 高级特性

1. Session保活机制 🆕

问题:QQ频道的Cookie会在2天左右失效,导致搜索失败。

解决方案:自动保活机制

text
插件启动后:
  ↓ 延迟3分钟
  ↓
定期执行保活 (每3分钟):
  ↓
遍历所有active用户:
  ↓ 异步执行
  访问 https://pd.qq.com/ (带Cookie)
  ↓
刷新服务器端session
  ↓
Cookie保持活跃状态 ✅

工作原理:

  • 🔄 每3分钟访问一次QQ频道首页
  • 🍪 携带用户Cookie发送请求
  • 💓 让QQ服务器知道session还活跃
  • ⚡ 异步执行,不阻塞搜索功能

日志示例(DebugLog=true时):

text
[QQPD] 💓 Session保活: 已为 2 个用户执行保活任务
[QQPD] 💓 Session保活成功: 1851****32 (状态码: 200)
[QQPD] 💓 Session保活成功: 1234****56 (状态码: 200)

配置建议:

  • 默认间隔:3分钟(推荐)
  • 可调整范围:2-5分钟
  • 太频繁:可能被视为异常
  • 太慢:可能无法防止超时

2. 多用户支持

场景:多个用户各自配置不同的频道

text
用户A (QQ: 111111111)
  ↓
配置频道: [频道1, 频道2, 频道3]

用户B (QQ: 222222222)
  ↓
配置频道: [频道2, 频道4, 频道5]

用户C (QQ: 333333333)
  ↓
配置频道: [频道3, 频道5, 频道6]

搜索时:
  ↓
去重后的频道: [频道1, 频道2, 频道3, 频道4, 频道5, 频道6]
  ↓
负载均衡分配:
  - 用户A: 搜索频道1, 频道4
  - 用户B: 搜索频道2, 频道5
  - 用户C: 搜索频道3, 频道6

3. guild_id缓存优化

性能提升:

text
首次保存频道:
  pd97631607 → 访问 https://pd.qq.com/g/pd97631607
              → 提取 guild_id: 592843764045681811
              → 缓存到JSON

搜索时:
  pd97631607 → 从内存读取 guild_id: 592843764045681811
              → 0网络请求 ⚡
              → 直接调用搜索API

效果:

  • 首次配置:稍慢(需要获取guild_id)
  • 后续搜索:极快(从内存读取)
  • 性能提升:每个频道节省100-200ms

4. 智能去重

text
用户A配置: [频道1, 频道2, 频道3]
用户B配置: [频道2, 频道3, 频道4]

去重后:
  频道1 → 分配给用户A
  频道2 → 分配给用户A(负载均衡)
  频道3 → 分配给用户B(负载均衡)
  频道4 → 分配给用户B

5. 负载均衡

text
任务分配算法:
  for each 频道:
    选择当前任务数最少的用户来执行
  
效果:
  - 避免单用户请求过多被限流
  - 任务均匀分配
  - 提高成功率

File: plugin/weibo/README.md

微博搜索插件 (Weibo)

📖 简介

Weibo是PanSou的微博搜索插件,支持多用户登录微博并配置要搜索的微博用户,在搜索时自动聚合所有配置的微博用户发布的资源链接(从微博正文和评论中提取)。

✨ 核心特性

  • ✅ 多账户支持 - 每个微博账户独立配置,互不干扰
  • ✅ 扫码登录 - 手机微博扫码,自动获取Cookie
  • ✅ 多微博用户 - 每个账户可配置多个要搜索的微博用户
  • ✅ 评论提取 - 自动提取微博正文和评论中的网盘链接
  • ✅ 智能去重 - 多账户配置相同微博用户时自动去重
  • ✅ 负载均衡 - 任务均匀分配,避免单账户限流
  • ✅ 内存缓存 - 用户数据缓存到内存,搜索性能极高
  • ✅ 持久化存储 - Cookie和配置自动保存,重启不丢失
  • ✅ Web管理界面 - 一站式配置,简单易用
  • ✅ RESTful API - 支持程序化调用

🚀 快速开始

步骤1: 启动服务

bash
cd /path/to/pansou
ENABLED_PLUGINS=weibo go run main.go

# 或者编译后运行
go build -o pansou main.go
ENABLED_PLUGINS=weibo ./pansou

步骤2: 访问管理页面

浏览器打开:

text
http://localhost:8888/weibo/你的微博用户名

示例:

text
http://localhost:8888/weibo/pansou123

系统会自动:

  1. 根据用户名生成专属64位hash(不可逆)
  2. 重定向到专属管理页面:http://localhost:8888/weibo/{hash}
  3. 显示二维码供扫码登录

📌 提示:请收藏hash后的URL(包含你的专属hash),方便下次访问。

步骤3: 扫码登录

  1. 页面会自动显示微博登录二维码
  2. 使用手机微博APP扫描二维码
  3. 扫码后系统会自动检测登录状态(每2秒检查一次)
  4. 登录成功后自动显示用户信息

步骤4: 配置微博用户

在"微博用户管理"区域输入要搜索的微博用户ID,每行一个:

text
1234567890
2345678901
3456789012

支持格式:

  • ✅ 纯用户ID:1234567890
  • ✅ 完整URL:https://weibo.com/u/1234567890

点击"保存配置"按钮。

📌 如何获取微博用户ID?

  1. 访问目标微博用户主页
  2. 查看URL:https://weibo.com/u/1234567890
  3. 其中 1234567890 就是用户ID

步骤5: 开始搜索

在PanSou主页搜索框输入关键词,系统会自动搜索所有配置的微博用户的微博内容!

bash
# 通过API搜索
curl "http://localhost:8888/api/search?kw=唐朝诡事录"

# 只搜索插件(包括weibo)
curl "http://localhost:8888/api/search?kw=唐朝诡事录&src=plugin"

📡 API文档

统一接口

所有操作通过统一的POST接口:

text
POST /weibo/{hash}
Content-Type: application/json

{
  "action": "操作类型",
  ...其他参数
}

API列表

Action 说明 需要登录 前端调用时机
get_status 获取状态 ❌ 每3秒自动调用
refresh_qrcode 刷新二维码 ❌ 用户点击刷新按钮
check_login 检查登录状态 ❌ 未登录时每2秒调用
logout 退出登录 ✅ 用户点击退出按钮
set_users 设置微博用户列表 ✅ 用户点击保存按钮
test_search 测试搜索 ✅ 用户点击搜索按钮

1️⃣ get_status - 获取账户状态

作用:获取当前账户的登录状态、配置的微博用户等信息

请求:

bash
curl -X POST "http://localhost:8888/weibo/{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-10-28 12:00:00",
    "expires_in_days": 365,
    "weibo_users": ["1234567890", "2345678901"],
    "user_count": 2,
    "qrcode_base64": ""
  }
}

成功响应(未登录):

json
{
  "success": true,
  "message": "获取成功",
  "data": {
    "hash": "abc123...",
    "logged_in": false,
    "status": "pending",
    "username_masked": "",
    "weibo_users": [],
    "user_count": 0,
    "qrcode_base64": "data:image/png;base64,iVBORw0KGgo..."
  }
}

2️⃣ refresh_qrcode - 刷新二维码

作用:强制生成新的二维码(当二维码过期时)

请求:

bash
curl -X POST "http://localhost:8888/weibo/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "refresh_qrcode"}'

成功响应:

json
{
  "success": true,
  "message": "二维码已刷新",
  "data": {
    "qrcode_base64": "data:image/png;base64,iVBORw0KGgo..."
  }
}

说明:

  • 二维码有效期约2-3分钟
  • 过期后需要点击刷新

3️⃣ check_login - 检查登录状态

作用:检查二维码是否被扫描,登录是否成功(扫码后轮询调用)

请求:

bash
curl -X POST "http://localhost:8888/weibo/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "check_login"}'

响应(等待扫码):

json
{
  "success": true,
  "message": "等待扫码",
  "data": {
    "login_status": "waiting"
  }
}

响应(登录成功):

json
{
  "success": true,
  "message": "登录成功",
  "data": {
    "login_status": "success",
    "username_masked": "pa****ou"
  }
}

响应(二维码过期):

json
{
  "success": false,
  "message": "二维码已失效,请刷新"
}

说明:

  • 前端未登录时每2秒自动调用
  • 登录成功后前端会停止轮询
  • 后端会自动获取完整Cookie并保存

4️⃣ logout - 退出登录

作用:清除Cookie,退出登录状态

请求:

bash
curl -X POST "http://localhost:8888/weibo/{hash}" \
  -H "Content-Type: application/json" \
  -d '{"action": "logout"}'

成功响应:

json
{
  "success": true,
  "message": "已退出登录",
  "data": {
    "status": "pending"
  }
}

5️⃣ set_users - 设置微博用户列表

作用:配置或更新要搜索的微博用户列表(覆盖式更新)

请求:

bash
curl -X POST "http://localhost:8888/weibo/{hash}" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "set_users",
    "users": ["1234567890", "2345678901", "https://weibo.com/u/3456789012"]
  }'

成功响应:

json
{
  "success": true,
  "message": "微博用户列表已更新",
  "data": {
    "weibo_users": ["1234567890", "2345678901", "3456789012"],
    "user_count": 3,
    "invalid_users": []
  }
}

说明:

  • 自动提取用户ID(支持URL格式)
  • 自动去重
  • 只保存数字格式的用户ID

作用:在管理页面测试搜索功能

请求:

bash
curl -X POST "http://localhost:8888/weibo/{hash}" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "test_search",
    "keyword": "唐朝诡事录"
  }'

参数:

  • keyword(必需):搜索关键词

成功响应:

json
{
  "success": true,
  "message": "找到 3 条结果",
  "data": {
    "keyword": "唐朝诡事录",
    "total_results": 3,
    "users_searched": ["1234567890", "2345678901"],
    "results": [
      {
        "unique_id": "weibo-1234567890-M_Pqs5eOb",
        "title": "唐朝诡事录 全集",
        "content": "唐朝诡事录更新至40集...",
        "links": [
          {
            "type": "quark",
            "url": "https://pan.quark.cn/s/xxxxx",
            "password": ""
          }
        ]
      }
    ]
  }
}

🔧 配置说明

环境变量(可选)

bash
# Hash Salt(推荐自定义,增强安全性)
export WEIBO_HASH_SALT="your-custom-salt-here"

# Cookie加密密钥(32字节,推荐自定义)
export WEIBO_ENCRYPTION_KEY="your-32-byte-key-here!!!!!!!!!!"

代码内配置

在 weibo.go 第28-33行修改:

go
const (
    MaxConcurrentUsers = 10  // 最多同时搜索多少个微博账户
    MaxConcurrentWeibo = 30  // 最多同时处理多少条微博(获取评论)
    MaxComments        = 1   // 每条微博最多获取多少条评论
    DebugLog           = false
)

参数说明:

参数 默认值 说明 建议
MaxConcurrentUsers 10 单次搜索最多使用的微博账户数 5-10足够
MaxConcurrentWeibo 30 最大并发处理微博数(获取评论) 20-50
MaxComments 1 每条微博最多获取多少条评论 1-3条
DebugLog false 是否开启调试日志 生产环境false

📂 数据存储

存储位置

text
cache/weibo_users/{hash}.json

数据结构

json
{
  "hash": "abc123...",
  "username_masked": "pa****ou",
  "cookie": "SUB=xxx; SUBP=xxx; ...",
  "status": "active",
  "weibo_users": ["1234567890", "2345678901", "3456789012"],
  "created_at": "2025-11-19T12:00:00+08:00",
  "login_at": "2025-11-19T12:00:00+08:00",
  "expire_at": "2026-11-19T12:00:00+08:00",
  "last_access_at": "2025-11-19T13:00:00+08:00"
}

字段说明:

  • hash: 账户唯一标识(SHA256,不可逆)
  • username_masked: 脱敏用户名(如pa****ou)
  • cookie: 微博登录Cookie(明文存储,建议配置加密)
  • status: 账户状态(pending/active/expired)
  • weibo_users: 要搜索的微博用户ID列表
  • expire_at: Cookie过期时间

🔒 安全特性

1. 用户名隐私保护

  • ✅ 不存储明文用户名:只存储SHA256 hash(64位十六进制)
  • ✅ 不可逆:无法从hash反推用户名
  • ✅ 加盐hash:支持自定义salt,进一步增强安全性
  • ⚠️ 当前:明文存储到JSON(方便调试)
  • ✅ 可选:通过环境变量配置加密密钥
  • ✅ 建议:生产环境配置WEIBO_ENCRYPTION_KEY

3. 自动清理

定期清理任务(每24小时):

  • 删除:状态为expired且30天未访问的账户
  • 标记:90天未访问的账户标记为expired

⚙️ 工作原理

搜索流程

text
用户搜索关键词 "唐朝诡事录"
  ↓
加载所有active状态的微博账户
  ↓
取最近访问的前10个账户(负载均衡)
  ↓
为每个账户分配要搜索的微博用户
  ↓
并发执行:
  账户A → 搜索微博用户 1234567890
  账户B → 搜索微博用户 2345678901
  账户C → 搜索微博用户 3456789012
  ↓
  对每个微博用户:
    1. 获取前3页微博列表
    2. 过滤包含关键词的微博
    3. 提取微博正文中的网盘链接
    4. 获取第1条评论(可配置)
    5. 提取评论中的网盘链接
  ↓
合并所有账户的搜索结果
  ↓
去重(基于微博ID)
  ↓
返回最终结果

链接提取

支持的网盘类型:

  • 夸克网盘:https://pan.quark.cn/s/xxxxx
  • 阿里云盘:https://www.alipan.com/s/xxxxx
  • 百度网盘:https://pan.baidu.com/s/xxxxx
  • 其他常见网盘

提取位置:

  1. 微博正文
  2. 微博评论(默认取第1条)

负载均衡

text
账户A配置: [用户1, 用户2, 用户3, 用户4]
账户B配置: [用户2, 用户3, 用户5, 用户6]
账户C配置: [用户1, 用户5, 用户7]

去重后要搜索的微博用户:
  [用户1, 用户2, 用户3, 用户4, 用户5, 用户6, 用户7]

任务分配(轮询):
  用户1 → 账户A
  用户2 → 账户B
  用户3 → 账户C
  用户4 → 账户A
  用户5 → 账户B
  用户6 → 账户C
  用户7 → 账户A

🎯 使用场景

场景1: 追剧更新

配置几个经常分享资源的微博用户,自动获取最新更新的剧集链接。

场景2: 资源聚合

配置多个不同领域的资源分享博主,一次搜索聚合所有相关资源。

场景3: 团队协作

团队成员各自配置自己的微博账户和关注的资源博主,共享搜索结果。

📝 注意事项

  1. Cookie有效期:微博Cookie约1年有效,过期需要重新登录
  2. 请求限制:单个账户请求过快可能被限流,建议配置多个账户
  3. 评论获取:默认只获取每条微博的第1条评论,可通过MaxComments调整
  4. 用户ID格式:必须是纯数字格式(如1234567890),不支持个性化域名

🔍 故障排查

问题1: 搜索无结果

可能原因:

  • 配置的微博用户没有发布包含关键词的内容
  • Cookie已过期,需要重新登录
  • 微博用户ID配置错误

解决方法:

  1. 检查管理页面的登录状态
  2. 使用"测试搜索"功能验证
  3. 确认微博用户ID格式正确

问题2: 登录失败

可能原因:

  • 二维码已过期
  • 网络问题
  • 微博安全策略限制

解决方法:

  1. 点击"刷新二维码"重试
  2. 检查网络连接
  3. 尝试更换网络环境

可能原因:

  • 账户在其他设备登录
  • 账户安全策略
  • 请求频率过高

解决方法:

  1. 减少请求频率
  2. 配置多个账户分散请求
  3. 检查账户安全设置

📚 更多信息