228 lines
4.5 KiB
Go
228 lines
4.5 KiB
Go
package torrent
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha1"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type StubClient struct {
|
|
logPath string
|
|
savePath string
|
|
mu sync.RWMutex
|
|
logMu sync.Mutex
|
|
torrents map[string]*TorrentInfo
|
|
}
|
|
|
|
func NewStubClient(logPath, savePath string) *StubClient {
|
|
return &StubClient{
|
|
logPath: logPath,
|
|
savePath: savePath,
|
|
torrents: make(map[string]*TorrentInfo),
|
|
}
|
|
}
|
|
|
|
func (c *StubClient) log(format string, args ...any) {
|
|
if c.logPath == "" {
|
|
return
|
|
}
|
|
c.logMu.Lock()
|
|
defer c.logMu.Unlock()
|
|
|
|
f, err := os.OpenFile(c.logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
timestamp := time.Now().Format(time.RFC3339)
|
|
msg := fmt.Sprintf(format, args...)
|
|
fmt.Fprintf(f, "[%s] %s\n", timestamp, msg)
|
|
}
|
|
|
|
func (c *StubClient) Connect(ctx context.Context) error {
|
|
c.log("CONNECT")
|
|
return nil
|
|
}
|
|
|
|
func (c *StubClient) Disconnect(ctx context.Context) error {
|
|
c.log("DISCONNECT")
|
|
return nil
|
|
}
|
|
|
|
func (c *StubClient) ListTorrents(ctx context.Context) ([]TorrentInfo, error) {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
c.log("LIST_TORRENTS count=%d", len(c.torrents))
|
|
|
|
result := make([]TorrentInfo, 0, len(c.torrents))
|
|
for _, t := range c.torrents {
|
|
result = append(result, *t)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (c *StubClient) GetTorrent(ctx context.Context, hash string) (*TorrentInfo, error) {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
c.log("GET_TORRENT hash=%s", hash)
|
|
|
|
t, ok := c.torrents[hash]
|
|
if !ok {
|
|
return nil, ErrTorrentNotFound
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
func (c *StubClient) AddTorrentURL(ctx context.Context, url string, savePath *string) error {
|
|
path := c.savePath
|
|
if savePath != nil {
|
|
path = *savePath
|
|
}
|
|
|
|
hash := generateHashFromURL(url)
|
|
name := "Torrent-" + hash[:8]
|
|
|
|
c.mu.Lock()
|
|
c.torrents[hash] = &TorrentInfo{
|
|
Hash: hash,
|
|
Name: name,
|
|
Size: 500 * 1024 * 1024,
|
|
Progress: 0,
|
|
DownloadSpeed: 0,
|
|
UploadSpeed: 0,
|
|
State: StateQueued,
|
|
SavePath: path,
|
|
}
|
|
c.mu.Unlock()
|
|
|
|
c.log("ADD_TORRENT_URL url=%s hash=%s save_path=%s", url, hash, path)
|
|
return nil
|
|
}
|
|
|
|
func (c *StubClient) AddTorrentFile(ctx context.Context, data []byte, savePath *string) error {
|
|
path := c.savePath
|
|
if savePath != nil {
|
|
path = *savePath
|
|
}
|
|
|
|
hash := generateHashFromData(data)
|
|
name := "Torrent-" + hash[:8]
|
|
|
|
c.mu.Lock()
|
|
c.torrents[hash] = &TorrentInfo{
|
|
Hash: hash,
|
|
Name: name,
|
|
Size: uint64(len(data) * 100),
|
|
Progress: 0,
|
|
DownloadSpeed: 0,
|
|
UploadSpeed: 0,
|
|
State: StateQueued,
|
|
SavePath: path,
|
|
}
|
|
c.mu.Unlock()
|
|
|
|
c.log("ADD_TORRENT_FILE size=%d hash=%s save_path=%s", len(data), hash, path)
|
|
return nil
|
|
}
|
|
|
|
func (c *StubClient) RemoveTorrent(ctx context.Context, hash string, deleteFiles bool) error {
|
|
c.mu.Lock()
|
|
delete(c.torrents, hash)
|
|
c.mu.Unlock()
|
|
|
|
c.log("REMOVE_TORRENT hash=%s delete_files=%t", hash, deleteFiles)
|
|
return nil
|
|
}
|
|
|
|
func (c *StubClient) PauseTorrent(ctx context.Context, hash string) error {
|
|
c.mu.Lock()
|
|
if t, ok := c.torrents[hash]; ok {
|
|
t.State = StatePaused
|
|
t.DownloadSpeed = 0
|
|
}
|
|
c.mu.Unlock()
|
|
|
|
c.log("PAUSE_TORRENT hash=%s", hash)
|
|
return nil
|
|
}
|
|
|
|
func (c *StubClient) ResumeTorrent(ctx context.Context, hash string) error {
|
|
c.mu.Lock()
|
|
if t, ok := c.torrents[hash]; ok {
|
|
if t.Progress < 1.0 {
|
|
t.State = StateDownloading
|
|
} else {
|
|
t.State = StateSeeding
|
|
}
|
|
}
|
|
c.mu.Unlock()
|
|
|
|
c.log("RESUME_TORRENT hash=%s", hash)
|
|
return nil
|
|
}
|
|
|
|
func (c *StubClient) SetTorrentState(hash string, state TorrentState, progress float64) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if t, ok := c.torrents[hash]; ok {
|
|
t.State = state
|
|
t.Progress = progress
|
|
if state == StateSeeding {
|
|
t.Progress = 1.0
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *StubClient) SetTorrentName(hash, name string) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if t, ok := c.torrents[hash]; ok {
|
|
t.Name = name
|
|
}
|
|
}
|
|
|
|
func (c *StubClient) AddTorrentDirect(info TorrentInfo) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
c.torrents[info.Hash] = &info
|
|
}
|
|
|
|
func (c *StubClient) Clear() {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
c.torrents = make(map[string]*TorrentInfo)
|
|
}
|
|
|
|
func (c *StubClient) GetAllTorrents() map[string]*TorrentInfo {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
result := make(map[string]*TorrentInfo, len(c.torrents))
|
|
for k, v := range c.torrents {
|
|
copy := *v
|
|
result[k] = ©
|
|
}
|
|
return result
|
|
}
|
|
|
|
func generateHashFromURL(url string) string {
|
|
h := sha1.New()
|
|
h.Write([]byte(url))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
func generateHashFromData(data []byte) string {
|
|
h := sha1.New()
|
|
h.Write(data)
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|