Ztorrent/internal/torrent/engine.go
itexpert228 636fd5baa8
huy
2026-03-06 12:02:52 +03:00

158 lines
2.7 KiB
Go

package torrent
import (
"context"
"crypto/rand"
"sync"
"time"
"github.com/veggiedefender/torrent-client/internal/torrentfile"
"github.com/veggiedefender/torrent-client/internal/tracker"
)
type Engine struct {
mu sync.RWMutex
torrent *torrentfile.TorrentFile
peers []tracker.Peer
peerID [20]byte
cancel context.CancelFunc
lastErr error
}
type Status struct {
Loaded bool
Name string
Length int
PieceLength int
PieceCount int
Files []torrentfile.File
Announce string
PeerCount int
PeerID string
Progress float64
LastError string
}
func NewEngine() *Engine {
return &Engine{
peerID: generatePeerID(),
}
}
func (e *Engine) LoadTorrent(path string) error {
tf, err := torrentfile.Open(path)
if err != nil {
e.setError(err)
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
e.swapCancel(cancel)
defer e.clearCancel()
peers, err := tracker.GetPeers(ctx, tf, tracker.AnnounceOptions{
PeerID: e.peerID,
Port: 6881,
})
if err != nil {
e.mu.Lock()
e.torrent = tf
e.peers = nil
e.lastErr = err
e.mu.Unlock()
return err
}
e.mu.Lock()
e.torrent = tf
e.peers = peers
e.lastErr = nil
e.mu.Unlock()
return nil
}
func (e *Engine) Stop() {
e.mu.Lock()
defer e.mu.Unlock()
if e.cancel != nil {
e.cancel()
e.cancel = nil
}
}
func (e *Engine) Progress() float64 {
return 0
}
func (e *Engine) Status() Status {
e.mu.RLock()
defer e.mu.RUnlock()
status := Status{
Loaded: e.torrent != nil,
PeerCount: len(e.peers),
PeerID: string(e.peerID[:]),
Progress: e.Progress(),
}
if e.torrent != nil {
status.Name = e.torrent.Name
status.Length = e.torrent.Length
status.PieceLength = e.torrent.PieceLength
status.PieceCount = len(e.torrent.PieceHashes)
status.Files = append(status.Files, e.torrent.Files...)
status.Announce = e.torrent.Announce
}
if e.lastErr != nil {
status.LastError = e.lastErr.Error()
}
return status
}
func (e *Engine) setError(err error) {
e.mu.Lock()
defer e.mu.Unlock()
e.lastErr = err
}
func (e *Engine) swapCancel(cancel context.CancelFunc) {
e.mu.Lock()
defer e.mu.Unlock()
if e.cancel != nil {
e.cancel()
}
e.cancel = cancel
}
func (e *Engine) clearCancel() {
e.mu.Lock()
defer e.mu.Unlock()
if e.cancel != nil {
e.cancel()
e.cancel = nil
}
}
func generatePeerID() [20]byte {
const prefix = "-ZT0001-"
const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"
var id [20]byte
copy(id[:], prefix)
buf := make([]byte, len(id)-len(prefix))
if _, err := rand.Read(buf); err != nil {
now := time.Now().UnixNano()
for i := range buf {
buf[i] = byte(now >> (i * 8))
}
}
for i, b := range buf {
id[len(prefix)+i] = alphabet[int(b)%len(alphabet)]
}
return id
}