diff --git a/main.go b/main.go
index b04ccb9..1f51e25 100644
--- a/main.go
+++ b/main.go
@@ -63,6 +63,7 @@ import (
_ "pansou/plugin/yuhuage"
_ "pansou/plugin/u3c3"
_ "pansou/plugin/clxiong"
+ _ "pansou/plugin/jutoushe"
)
// 全局缓存写入管理器
diff --git a/plugin/jutoushe/html结构分析.md b/plugin/jutoushe/html结构分析.md
new file mode 100644
index 0000000..5e27096
--- /dev/null
+++ b/plugin/jutoushe/html结构分析.md
@@ -0,0 +1,199 @@
+# JUTOUSHE(剧透社)HTML结构分析
+
+## 网站信息
+- **网站名称**: 剧透社
+- **域名**: https://1.star2.cn/
+- **类型**: 网盘资源分享站,主要提供电视剧、电影、短剧、综艺等影视资源
+- **特点**: 提供多种网盘下载链接(夸克、百度、阿里等)
+
+## 搜索页面结构
+
+### 1. 搜索URL模式
+```
+https://1.star2.cn/search/?keyword={关键词}
+
+示例:
+https://1.star2.cn/search/?keyword=%E7%91%9E%E5%85%8B%E5%92%8C%E8%8E%AB%E8%92%82
+https://1.star2.cn/search/?keyword=%E4%B8%8E%E6%99%8B%E9%95%BF%E5%AE%89
+
+参数说明:
+- keyword: URL编码的搜索关键词
+```
+
+### 2. 搜索结果容器
+- **父容器**: `
`
+- **结果项**: `- ` (每个搜索结果)
+
+### 3. 单个搜索结果结构
+
+#### 标题和链接区域 (.a)
+```html
+
+
+提取要素:
+- 详情页链接: a.main 的 href 属性 (需要拼接完整域名)
+- 标题: a.main 的文本内容 (包含分类标签)
+```
+
+#### 时间信息区域 (.i)
+```html
+
+ 2025-05-26
+
+
+提取要素:
+- 发布时间: span.time 的文本内容 (格式: YYYY-MM-DD)
+```
+
+## 详情页面结构
+
+### 1. 详情页URL模式
+```
+https://1.star2.cn/{分类}/{ID}.html
+
+示例:
+https://1.star2.cn/dm/8100.html (动漫类别)
+https://1.star2.cn/ju/8737.html (国剧类别)
+
+分类说明:
+- /dm/ : 动漫
+- /ju/ : 国剧
+- /dj/ : 短剧
+- /zy/ : 综艺
+- /mv/ : 电影
+- /rh/ : 韩日剧
+- /ym/ : 英美剧
+- /wj/ : 外剧
+- /qt/ : 其他
+```
+
+### 2. 详情页面关键区域
+
+#### 标题区域
+```html
+【动漫】瑞克和莫蒂8.全集
+
+提取要素:
+- 标题: h1 的文本内容
+```
+
+#### 元信息区域
+```html
+
+
+提取要素:
+- 发布时间: span.time 的文本内容
+- 浏览次数: span.view 的文本内容 (可选)
+```
+
+#### 下载链接区域
+```html
+
+
+
+

+
影片地址
+
+
+
+
+
+提取要素:
+- 网盘链接: .dlipp-cont-bd a.dlipp-dl-btn 的 href 属性
+- 网盘类型: 从链接URL自动识别 (quark.cn, baidu.com 等)
+- 提取码: 从URL参数中提取 (如 ?pwd=8888)
+```
+
+## CSS选择器总结
+
+| 数据项 | 页面类型 | CSS选择器 | 提取方式 |
+|--------|----------|-----------|----------|
+| 搜索结果列表 | 搜索页 | `ul.erx-list li.item` | 遍历所有结果项 |
+| 标题 | 搜索页 | `.a a.main` | 文本内容 |
+| 详情页链接 | 搜索页 | `.a a.main` | href 属性 |
+| 发布时间 | 搜索页 | `.i span.time` | 文本内容 |
+| 详情页标题 | 详情页 | `h1` | 文本内容 |
+| 详情页时间 | 详情页 | `section.i span.time` | 文本内容 |
+| 浏览次数 | 详情页 | `section.i span.view` | 文本内容 |
+| 下载链接 | 详情页 | `.dlipp-cont-bd a.dlipp-dl-btn` | href 属性 |
+
+## 实现要点
+
+### 1. 网盘类型自动识别
+根据链接URL自动识别网盘类型:
+```
+pan.quark.cn → quark (夸克网盘)
+pan.baidu.com → baidu (百度网盘)
+aliyundrive.com → aliyun (阿里云盘)
+alipan.com → aliyun (阿里云盘新域名)
+cloud.189.cn → tianyi (天翼云盘)
+pan.xunlei.com → xunlei (迅雷网盘)
+115.com → 115 (115网盘)
+123pan.com → 123 (123网盘)
+caiyun.139.com → mobile (移动云盘)
+```
+
+### 2. 提取码处理
+- 百度网盘: `?pwd=1234` 格式
+- 其他网盘: 一般无需提取码或在URL中已包含
+
+### 3. 标题清理
+- 保留分类标签如 `【动漫】`、`【国剧】` 等
+- 去除多余空格和特殊字符
+
+### 4. 时间格式处理
+- 原格式: `2025-05-26`
+- 需转换为标准时间对象
+
+### 5. 内容描述
+- 可以从标题中提取分类信息作为描述
+- 或使用固定描述如 "剧透社影视资源"
+
+## 支持的分类
+
+| 分类代码 | 中文名称 | 路径 | 说明 |
+|----------|----------|------|------|
+| dm | 动漫 | /dm/ | 动画、动漫作品 |
+| ju | 国剧 | /ju/ | 国产电视剧 |
+| dj | 短剧 | /dj/ | 短视频剧集 |
+| zy | 综艺 | /zy/ | 综艺节目 |
+| mv | 电影 | /mv/ | 电影作品 |
+| rh | 韩日 | /rh/ | 韩国、日本影视剧 |
+| ym | 英美 | /ym/ | 英美影视剧 |
+| wj | 外剧 | /wj/ | 其他外国影视剧 |
+| qt | 其他 | /qt/ | 其他类型内容 |
+
+## 错误处理
+
+1. **网络超时**: 设置合理的超时时间,实现重试机制
+2. **解析失败**: 对于解析失败的页面,记录日志但不中断流程
+3. **空结果**: 搜索无结果时返回空数组
+4. **链接失效**: 验证链接格式,过滤掉明显无效的链接
+
+## 反爬虫处理
+
+1. **请求头设置**: 使用标准浏览器User-Agent
+2. **请求频率**: 控制请求间隔,避免被封IP
+3. **错误重试**: 遇到403/429等状态码时适当延迟重试
+
+## 特殊说明
+
+1. **域名**: 网站可能使用多个域名或动态域名,需要灵活处理
+2. **编码**: 确保中文关键词正确URL编码
+3. **链接拼接**: 详情页链接为相对路径,需要拼接完整URL
+4. **缓存**: 建议缓存搜索结果,避免重复请求
diff --git a/plugin/jutoushe/jutoushe.go b/plugin/jutoushe/jutoushe.go
new file mode 100644
index 0000000..61663f5
--- /dev/null
+++ b/plugin/jutoushe/jutoushe.go
@@ -0,0 +1,348 @@
+package jutoushe
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/PuerkitoBio/goquery"
+
+ "pansou/model"
+ "pansou/plugin"
+)
+
+type JutoushePlugin struct {
+ *plugin.BaseAsyncPlugin
+}
+
+func init() {
+ p := &JutoushePlugin{
+ BaseAsyncPlugin: plugin.NewBaseAsyncPlugin("jutoushe", 1),
+ }
+ plugin.RegisterGlobalPlugin(p)
+}
+
+// Search 执行搜索并返回结果(兼容性方法)
+func (p *JutoushePlugin) Search(keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {
+ result, err := p.SearchWithResult(keyword, ext)
+ if err != nil {
+ return nil, err
+ }
+ return result.Results, nil
+}
+
+// SearchWithResult 执行搜索并返回包含IsFinal标记的结果(推荐方法)
+func (p *JutoushePlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {
+ return p.AsyncSearchWithResult(keyword, p.searchImpl, p.MainCacheKey, ext)
+}
+
+// searchImpl 实现搜索逻辑
+func (p *JutoushePlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {
+ // 1. 构建搜索URL
+ baseURL := "https://1.star2.cn"
+ searchURL := fmt.Sprintf("%s/search/?keyword=%s", baseURL, url.QueryEscape(keyword))
+
+ // 2. 创建带超时的上下文
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer cancel()
+
+ // 3. 创建请求对象
+ req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
+ if err != nil {
+ return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
+ }
+
+ // 4. 设置请求头,避免反爬虫检测
+ req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
+ req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
+ req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
+ req.Header.Set("Connection", "keep-alive")
+ req.Header.Set("Referer", baseURL+"/")
+
+ // 5. 发送HTTP请求(带重试机制)
+ resp, err := p.doRequestWithRetry(req, client)
+ if err != nil {
+ return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
+ }
+ defer resp.Body.Close()
+
+ // 6. 检查状态码
+ if resp.StatusCode != 200 {
+ return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
+ }
+
+ // 7. 解析搜索结果页面
+ doc, err := goquery.NewDocumentFromReader(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("[%s] HTML解析失败: %w", p.Name(), err)
+ }
+
+ // 8. 提取搜索结果
+ var results []model.SearchResult
+ doc.Find("ul.erx-list li.item").Each(func(i int, s *goquery.Selection) {
+ // 提取标题和链接
+ linkElem := s.Find(".a a.main")
+ title := strings.TrimSpace(linkElem.Text())
+ detailPath, exists := linkElem.Attr("href")
+
+ if !exists || title == "" {
+ return // 跳过无效项
+ }
+
+ // 构建完整的详情页URL
+ detailURL := baseURL + detailPath
+
+ // 提取发布时间
+ timeStr := strings.TrimSpace(s.Find(".i span.time").Text())
+ publishTime := p.parseDate(timeStr)
+
+ // 构建唯一ID
+ uniqueID := fmt.Sprintf("%s-%s", p.Name(), p.extractIDFromURL(detailPath))
+
+ // 创建搜索结果(先不获取下载链接)
+ result := model.SearchResult{
+ UniqueID: uniqueID,
+ Title: title,
+ Content: fmt.Sprintf("剧透社影视资源:%s", title),
+ Datetime: publishTime,
+ Tags: p.extractTags(title),
+ Links: []model.Link{}, // 稍后从详情页获取
+ Channel: "", // 插件搜索结果必须为空字符串
+ }
+
+ // 异步获取详情页的下载链接
+ if links := p.getDetailLinks(client, detailURL); len(links) > 0 {
+ result.Links = links
+ results = append(results, result)
+ }
+ })
+
+ // 9. 关键词过滤
+ filteredResults := plugin.FilterResultsByKeyword(results, keyword)
+
+ return filteredResults, nil
+}
+
+// doRequestWithRetry 带重试机制的HTTP请求
+func (p *JutoushePlugin) doRequestWithRetry(req *http.Request, client *http.Client) (*http.Response, error) {
+ maxRetries := 3
+ var lastErr error
+
+ for i := 0; i < maxRetries; i++ {
+ if i > 0 {
+ // 指数退避重试
+ backoff := time.Duration(1< 1 {
+ return matches[1]
+ }
+ }
+
+ // 其他网盘暂不处理提取码
+ return ""
+}
+
+// isValidNetworkDriveURL 验证是否为有效的网盘链接
+func (p *JutoushePlugin) isValidNetworkDriveURL(url string) bool {
+ if url == "" {
+ return false
+ }
+
+ // 检查是否为HTTP/HTTPS链接
+ if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
+ return false
+ }
+
+ // 检查是否包含已知网盘域名
+ knownDomains := []string{
+ "pan.quark.cn", "drive.uc.cn", "pan.baidu.com",
+ "aliyundrive.com", "alipan.com", "pan.xunlei.com",
+ "cloud.189.cn", "115.com", "123pan.com",
+ "caiyun.139.com", "mypikpak.com",
+ }
+
+ for _, domain := range knownDomains {
+ if strings.Contains(url, domain) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// extractIDFromURL 从URL路径中提取ID
+func (p *JutoushePlugin) extractIDFromURL(urlPath string) string {
+ // 从 /dm/8100.html 提取 8100
+ re := regexp.MustCompile(`/([^/]+)/(\d+)\.html`)
+ matches := re.FindStringSubmatch(urlPath)
+ if len(matches) > 2 {
+ return matches[2]
+ }
+
+ // 如果无法提取,使用完整路径作为ID
+ return strings.ReplaceAll(urlPath, "/", "_")
+}
+
+// extractTags 从标题中提取标签
+func (p *JutoushePlugin) extractTags(title string) []string {
+ var tags []string
+
+ // 提取分类标签
+ categoryPattern := regexp.MustCompile(`【([^】]+)】`)
+ matches := categoryPattern.FindAllStringSubmatch(title, -1)
+ for _, match := range matches {
+ if len(match) > 1 {
+ tags = append(tags, match[1])
+ }
+ }
+
+ // 如果没有提取到分类,添加默认标签
+ if len(tags) == 0 {
+ tags = append(tags, "影视资源")
+ }
+
+ return tags
+}
+
+// parseDate 解析日期字符串
+func (p *JutoushePlugin) parseDate(dateStr string) time.Time {
+ if dateStr == "" {
+ return time.Now()
+ }
+
+ // 尝试解析 YYYY-MM-DD 格式
+ if t, err := time.Parse("2006-01-02", dateStr); err == nil {
+ return t
+ }
+
+ // 尝试解析 YYYY年MM月DD日 格式
+ re := regexp.MustCompile(`(\d{4})年(\d{1,2})月(\d{1,2})日`)
+ matches := re.FindStringSubmatch(dateStr)
+ if len(matches) == 4 {
+ year, _ := strconv.Atoi(matches[1])
+ month, _ := strconv.Atoi(matches[2])
+ day, _ := strconv.Atoi(matches[3])
+ return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)
+ }
+
+ // 解析失败,返回当前时间
+ return time.Now()
+}