package app import ( "context" "encoding/hex" "time" "github.com/veggiedefender/torrent-client/internal/history" "github.com/veggiedefender/torrent-client/internal/torrent" ) type Controller struct { engine *torrent.Engine ctx context.Context cancel context.CancelFunc currentPath string currentOut string } func NewController() *Controller { engine := torrent.NewEngine() ctx, cancel := context.WithCancel(context.Background()) return &Controller{ engine: engine, ctx: ctx, cancel: cancel, } } func (c *Controller) StartTorrent(path, outputRoot string) error { // Cancel any previous history updater if c.cancel != nil { c.cancel() } c.ctx, c.cancel = context.WithCancel(context.Background()) c.currentPath = path c.currentOut = outputRoot err := c.engine.LoadTorrent(path, outputRoot) if err != nil { return err } c.updateHistory() go func() { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { select { case <-c.ctx.Done(): return case <-ticker.C: c.updateHistory() } } }() return nil } func (c *Controller) StopTorrent() { if c.cancel != nil { c.cancel() } c.updateHistory() c.engine.Stop() } func (c *Controller) updateHistory() { status := c.engine.Status() if status.Phase == "" { return } hashHex := "" if len(status.InfoHash) > 0 { hashHex = hex.EncodeToString(status.InfoHash[:]) } hItem := history.Item{ InfoHash: hashHex, Name: status.Name, TorrentPath: c.currentPath, OutputDir: c.currentOut, Status: status.Phase, Progress: c.engine.Progress() * 100, // store as percentage 0-100 Size: status.TotalBytes, } history.Update(hItem) } func (c *Controller) Progress() float64 { return c.engine.Progress() } func (c *Controller) Status() torrent.Status { return c.engine.Status() }