mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-12 00:06:37 +08:00
split transmission adapter helpers
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// transmissionRPCRequest 是 Transmission RPC 请求的通用结构。
|
||||
type transmissionRPCRequest struct {
|
||||
Method string `json:"method"`
|
||||
Arguments map[string]interface{} `json:"arguments"`
|
||||
Tag int `json:"tag,omitempty"`
|
||||
}
|
||||
|
||||
// transmissionRPCResponse 是 Transmission RPC 响应的通用结构。
|
||||
type transmissionRPCResponse struct {
|
||||
Result string `json:"result"`
|
||||
Arguments map[string]interface{} `json:"arguments"`
|
||||
Tag int `json:"tag"`
|
||||
}
|
||||
|
||||
// Initialize 配置并初始化 Transmission RPC 连接。
|
||||
func (a *TransmissionAdapter) Initialize(ctx context.Context, cfg DownloadClientConfig) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
endpoint, err := normalizeDownloadClientEndpoint("transmission", cfg.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.Host = endpoint
|
||||
a.cfg = cfg
|
||||
a.sessionID = ""
|
||||
a.tag = 0
|
||||
return a.pingLocked(ctx)
|
||||
}
|
||||
|
||||
// Ping 测试连接。
|
||||
func (a *TransmissionAdapter) Ping(ctx context.Context) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.pingLocked(ctx)
|
||||
}
|
||||
|
||||
// pingLocked 内部 ping 实现(调用者必须持有锁)。
|
||||
func (a *TransmissionAdapter) pingLocked(ctx context.Context) error {
|
||||
rpcURL, err := downloadClientRPCURL("transmission", a.cfg.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := newDownloadClientHTTPRequest(ctx, http.MethodGet, rpcURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.cfg.Username != "" {
|
||||
req.SetBasicAuth(a.cfg.Username, a.cfg.Password)
|
||||
}
|
||||
resp, err := a.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
if resp.StatusCode == 409 {
|
||||
// 正常:需要 CSRF token
|
||||
a.sessionID = resp.Header.Get("X-Transmission-Session-Id")
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
return fmt.Errorf("transmission rpc: %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rpcLocked 发送 RPC 请求(调用者必须持有锁)。
|
||||
func (a *TransmissionAdapter) rpcLocked(ctx context.Context, method string, args map[string]interface{}) (*transmissionRPCResponse, error) {
|
||||
rpcURL, err := downloadClientRPCURL("transmission", a.cfg.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.tag++
|
||||
body, err := json.Marshal(transmissionRPCRequest{
|
||||
Method: method,
|
||||
Arguments: args,
|
||||
Tag: a.tag,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for attempt := 0; attempt < 2; attempt++ {
|
||||
req, err := newDownloadClientHTTPRequest(ctx, http.MethodPost, rpcURL, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if a.sessionID != "" {
|
||||
req.Header.Set("X-Transmission-Session-Id", a.sessionID)
|
||||
}
|
||||
if a.cfg.Username != "" {
|
||||
req.SetBasicAuth(a.cfg.Username, a.cfg.Password)
|
||||
}
|
||||
|
||||
resp, err := a.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == 409 {
|
||||
a.sessionID = resp.Header.Get("X-Transmission-Session-Id")
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("transmission rpc error: %d: %s", resp.StatusCode, string(raw))
|
||||
}
|
||||
|
||||
var result transmissionRPCResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Result != "success" {
|
||||
return nil, fmt.Errorf("transmission rpc result: %s", result.Result)
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
return nil, fmt.Errorf("transmission: failed after CSRF retry")
|
||||
}
|
||||
Reference in New Issue
Block a user