mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-13 08:36:38 +08:00
@@ -7,6 +7,7 @@ package service
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
@@ -61,6 +62,12 @@ func (a *QBitAdapter) Ping(ctx context.Context) error {
|
||||
|
||||
// AddTorrent 通过 URL 添加种子。
|
||||
func (a *QBitAdapter) AddTorrent(ctx context.Context, torrentURL, savePath string) (string, error) {
|
||||
return a.AddTorrentWithCategory(ctx, torrentURL, savePath, "")
|
||||
}
|
||||
|
||||
// AddTorrentWithCategory submits a URL or magnet while preserving the
|
||||
// qBittorrent category selected by MediaStationGo's classification rules.
|
||||
func (a *QBitAdapter) AddTorrentWithCategory(ctx context.Context, torrentURL, savePath, category string) (string, error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := a.ensureAuthLocked(ctx); err != nil {
|
||||
@@ -70,10 +77,49 @@ func (a *QBitAdapter) AddTorrent(ctx context.Context, torrentURL, savePath strin
|
||||
body := &bytes.Buffer{}
|
||||
w := multipart.NewWriter(body)
|
||||
_ = w.WriteField("urls", torrentURL)
|
||||
return a.addTorrentMultipartLocked(ctx, body, w, savePath, category, torrentURLInfoHash(torrentURL))
|
||||
}
|
||||
|
||||
// AddMagnet 通过磁力链接添加种子。
|
||||
func (a *QBitAdapter) AddMagnet(ctx context.Context, magnet, savePath string) (string, error) {
|
||||
return a.AddTorrent(ctx, magnet, savePath)
|
||||
}
|
||||
|
||||
// AddTorrentFile uploads .torrent bytes as multipart/form-data.
|
||||
func (a *QBitAdapter) AddTorrentFile(ctx context.Context, data []byte, name, savePath string) (string, error) {
|
||||
return a.AddTorrentFileWithCategory(ctx, data, name, savePath, "")
|
||||
}
|
||||
|
||||
// AddTorrentFileWithCategory uploads .torrent bytes and preserves the
|
||||
// qBittorrent category selected by the caller.
|
||||
func (a *QBitAdapter) AddTorrentFileWithCategory(ctx context.Context, data []byte, name, savePath, category string) (string, error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := a.ensureAuthLocked(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
body := &bytes.Buffer{}
|
||||
w := multipart.NewWriter(body)
|
||||
part, err := w.CreateFormFile("torrents", name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := part.Write(data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return a.addTorrentMultipartLocked(ctx, body, w, savePath, category, torrentInfoHash(data))
|
||||
}
|
||||
|
||||
func (a *QBitAdapter) addTorrentMultipartLocked(ctx context.Context, body *bytes.Buffer, w *multipart.Writer, savePath, category, externalID string) (string, error) {
|
||||
if savePath != "" {
|
||||
_ = w.WriteField("savepath", savePath)
|
||||
}
|
||||
_ = w.Close()
|
||||
if category != "" {
|
||||
_ = w.WriteField("category", category)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
baseURL := strings.TrimRight(a.cfg.Host, "/")
|
||||
req, err := newDownloadClientHTTPRequest(ctx, http.MethodPost,
|
||||
@@ -95,14 +141,9 @@ func (a *QBitAdapter) AddTorrent(ctx context.Context, torrentURL, savePath strin
|
||||
return "", fmt.Errorf("qbittorrent add torrent: %d: %s", resp.StatusCode, strings.TrimSpace(string(raw)))
|
||||
}
|
||||
if strings.EqualFold(strings.TrimSpace(string(raw)), "Fails.") {
|
||||
return "", fmt.Errorf("qbittorrent add torrent: rejected by downloader")
|
||||
return externalID, errors.New("qbittorrent add torrent: rejected by downloader")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// AddMagnet 通过磁力链接添加种子。
|
||||
func (a *QBitAdapter) AddMagnet(ctx context.Context, magnet, savePath string) (string, error) {
|
||||
return a.AddTorrent(ctx, magnet, savePath)
|
||||
return externalID, nil
|
||||
}
|
||||
|
||||
// Pause 暂停种子。
|
||||
@@ -193,6 +234,36 @@ func (a *QBitAdapter) Remove(ctx context.Context, hash string, deleteFiles bool)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *QBitAdapter) Relocate(ctx context.Context, hash, location string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := a.ensureAuthLocked(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
baseURL := strings.TrimRight(a.cfg.Host, "/")
|
||||
form := url.Values{}
|
||||
form.Set("hashes", hash)
|
||||
form.Set("location", location)
|
||||
req, err := newDownloadClientHTTPRequest(ctx, http.MethodPost,
|
||||
baseURL+"/api/v2/torrents/setLocation", strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Referer", baseURL)
|
||||
req.Header.Set("Origin", baseURL)
|
||||
resp, err := a.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode >= 400 {
|
||||
return fmt.Errorf("qbittorrent setLocation: %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loginLocked 执行登录(调用者必须持有锁)。
|
||||
func (a *QBitAdapter) loginLocked(ctx context.Context) error {
|
||||
if a.cfg.Host == "" {
|
||||
|
||||
Reference in New Issue
Block a user