mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 03:26:37 +08:00
123 lines
3.9 KiB
Go
123 lines
3.9 KiB
Go
// Package service — NexusPHP site adapter.
|
||
package service
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"net/http"
|
||
"net/url"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// ─── NexusPHP 适配器 ─────────────────────────────────────────────────────────
|
||
|
||
// NexusPHPAdapter NexusPHP 框架适配器(馒头、HDHome、CHDBits 等)。
|
||
type NexusPHPAdapter struct {
|
||
client *http.Client
|
||
}
|
||
|
||
// NewNexusPHPAdapter 创建 NexusPHP 适配器。
|
||
func NewNexusPHPAdapter() *NexusPHPAdapter {
|
||
return &NexusPHPAdapter{
|
||
client: &http.Client{Timeout: 30 * time.Second},
|
||
}
|
||
}
|
||
|
||
func (a *NexusPHPAdapter) Authenticate(ctx context.Context, cfg SiteConfig) error {
|
||
// 走 doRequest 以便复用代理 / FlareSolverr / 浏览器头。
|
||
data, status, err := doRequest(ctx, a.client, "GET", cfg.URL+"/index.php", cfg, nil)
|
||
if err != nil {
|
||
return fmt.Errorf("request failed: %w", err)
|
||
}
|
||
|
||
if status == http.StatusFound {
|
||
return fmt.Errorf("authentication failed: redirected to login page")
|
||
}
|
||
if status == http.StatusUnauthorized || status == http.StatusForbidden {
|
||
return fmt.Errorf("authentication failed: status %d", status)
|
||
}
|
||
if status >= 400 {
|
||
return fmt.Errorf("authentication failed: status %d", status)
|
||
}
|
||
|
||
body := string(data)
|
||
// NexusPHP 登录后页面通常包含 logout 或 userdetails;
|
||
// 仅当二者都不存在且明确显示登录表单时才判失败。
|
||
if strings.Contains(body, "userdetails") || strings.Contains(body, "logout") || strings.Contains(body, "退出") {
|
||
return nil
|
||
}
|
||
if strings.Contains(body, "takelogin.php") || strings.Contains(body, "id=\"loginform\"") {
|
||
return fmt.Errorf("authentication failed: not logged in")
|
||
}
|
||
// 状态码 OK 但页面不含明显标记时不再武断判失败。
|
||
return nil
|
||
}
|
||
|
||
func (a *NexusPHPAdapter) Search(ctx context.Context, cfg SiteConfig, keyword string, page int) (*SiteSearchResult, error) {
|
||
params := url.Values{}
|
||
params.Set("searchstr", keyword)
|
||
params.Set("search", keyword)
|
||
params.Set("search_area", "0")
|
||
params.Set("search_mode", "0")
|
||
params.Set("page", strconv.Itoa(page))
|
||
params.Set("inclbookmarked", "0")
|
||
params.Set("incldead", "0")
|
||
|
||
u := cfg.URL + "/torrents.php?" + params.Encode()
|
||
data, status, err := doRequest(ctx, a.client, "GET", u, cfg, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("search request: %w", err)
|
||
}
|
||
if status != http.StatusOK {
|
||
return nil, fmt.Errorf("search failed: status %d", status)
|
||
}
|
||
|
||
body := string(data)
|
||
if nexusPHPPageLooksLogin(body) {
|
||
return nil, fmt.Errorf("search failed: not logged in or cookie expired")
|
||
}
|
||
return parseNexusPHPHTML(body, cfg.Name, cfg.URL)
|
||
}
|
||
|
||
func (a *NexusPHPAdapter) Browse(ctx context.Context, cfg SiteConfig, category string, page int) (*SiteSearchResult, error) {
|
||
params := url.Values{}
|
||
if category != "" {
|
||
params.Set("cat", category)
|
||
}
|
||
params.Set("page", strconv.Itoa(page))
|
||
|
||
u := cfg.URL + "/torrents.php?" + params.Encode()
|
||
data, status, err := doRequest(ctx, a.client, "GET", u, cfg, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("browse request: %w", err)
|
||
}
|
||
if status != http.StatusOK {
|
||
return nil, fmt.Errorf("browse failed: status %d", status)
|
||
}
|
||
|
||
body := string(data)
|
||
if nexusPHPPageLooksLogin(body) {
|
||
return nil, fmt.Errorf("browse failed: not logged in or cookie expired")
|
||
}
|
||
return parseNexusPHPHTML(body, cfg.Name, cfg.URL)
|
||
}
|
||
|
||
func (a *NexusPHPAdapter) GetDetail(ctx context.Context, cfg SiteConfig, id string) (*TorrentDetail, error) {
|
||
u := cfg.URL + "/details.php?id=" + id
|
||
data, status, err := doRequest(ctx, a.client, "GET", u, cfg, nil)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("detail request: %w", err)
|
||
}
|
||
if status != http.StatusOK {
|
||
return nil, fmt.Errorf("detail failed: status %d", status)
|
||
}
|
||
|
||
return parseNexusPHPDetailHTML(string(data), id, cfg.URL)
|
||
}
|
||
|
||
func (a *NexusPHPAdapter) GetDownloadURL(ctx context.Context, cfg SiteConfig, id string) (string, error) {
|
||
return cfg.URL + "/download.php?id=" + id, nil
|
||
}
|