diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 86c2f27..d73144e 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -62,12 +62,14 @@ func PrintState(state *ratiospoof.RatioSpoof) { fmt.Printf("#%v downloaded: %v(%.2f%%) | left: %v | uploaded: %v | announced\n", dequeItem.Count, humanReadableSize(float64(dequeItem.Downloaded)), dequeItem.PercentDownloaded, humanReadableSize(float64(dequeItem.Left)), humanReadableSize(float64(dequeItem.Uploaded))) } lastDequeItem := state.AnnounceHistory.At(state.AnnounceHistory.Len() - 1).(ratiospoof.AnnounceEntry) + + remaining := time.Until(state.EstimatedTimeToAnnounce) fmt.Printf("#%v downloaded: %v(%.2f%%) | left: %v | uploaded: %v | next announce in: %v %v\n", lastDequeItem.Count, humanReadableSize(float64(lastDequeItem.Downloaded)), lastDequeItem.PercentDownloaded, humanReadableSize(float64(lastDequeItem.Left)), humanReadableSize(float64(lastDequeItem.Uploaded)), - fmtDuration(state.CurrentAnnounceTimer), + fmtDuration(remaining), retryStr) if state.Input.Debug { @@ -114,7 +116,9 @@ func humanReadableSize(byteSize float64) string { return fmt.Sprintf("%.2f%v", byteSize, unitFound) } -func fmtDuration(seconds int) string { - d := time.Duration(seconds) * time.Second - return fmt.Sprintf("%s", d) +func fmtDuration(d time.Duration) string { + if d.Seconds() < 0 { + return fmt.Sprintf("%s", 0*time.Second) + } + return fmt.Sprintf("%s", time.Duration(int(d.Seconds()))*time.Second) } diff --git a/internal/ratiospoof/ratiospoof.go b/internal/ratiospoof/ratiospoof.go index 2c51e33..fde81c3 100644 --- a/internal/ratiospoof/ratiospoof.go +++ b/internal/ratiospoof/ratiospoof.go @@ -24,21 +24,21 @@ const ( ) type RatioSpoof struct { - mutex *sync.Mutex - TorrentInfo *bencode.TorrentInfo - Input *input.InputParsed - Tracker *tracker.HttpTracker - BitTorrentClient TorrentClientEmulation - CurrentAnnounceTimer int - AnnounceInterval int - NumWant int - Seeders int - Leechers int - AnnounceCount int - Status string - AnnounceHistory announceHistory - timerUpdateCh chan int - StopPrintCH chan interface{} + mutex *sync.Mutex + TorrentInfo *bencode.TorrentInfo + Input *input.InputParsed + Tracker *tracker.HttpTracker + BitTorrentClient TorrentClientEmulation + AnnounceInterval int + EstimatedTimeToAnnounce time.Time + EstimatedTimeToAnnounceUpdateCh chan int + NumWant int + Seeders int + Leechers int + AnnounceCount int + Status string + AnnounceHistory announceHistory + StopPrintCH chan interface{} } type TorrentClientEmulation interface { @@ -63,7 +63,7 @@ type announceHistory struct { } func NewRatioSpoofState(input input.InputArgs, torrentClient TorrentClientEmulation) (*RatioSpoof, error) { - changeTimerCh := make(chan int) + EstimatedTimeToAnnounceUpdateCh := make(chan int) stopPrintCh := make(chan interface{}) dat, err := ioutil.ReadFile(input.TorrentPath) if err != nil { @@ -75,7 +75,7 @@ func NewRatioSpoofState(input input.InputArgs, torrentClient TorrentClientEmulat return nil, errors.New("failed to parse the torrent file") } - httpTracker, err := tracker.NewHttpTracker(torrentInfo, changeTimerCh) + httpTracker, err := tracker.NewHttpTracker(torrentInfo) if err != nil { return nil, err } @@ -86,15 +86,15 @@ func NewRatioSpoofState(input input.InputArgs, torrentClient TorrentClientEmulat } return &RatioSpoof{ - BitTorrentClient: torrentClient, - TorrentInfo: torrentInfo, - Tracker: httpTracker, - Input: inputParsed, - NumWant: 200, - Status: "started", - mutex: &sync.Mutex{}, - timerUpdateCh: changeTimerCh, - StopPrintCH: stopPrintCh, + BitTorrentClient: torrentClient, + TorrentInfo: torrentInfo, + Tracker: httpTracker, + Input: inputParsed, + NumWant: 200, + Status: "started", + mutex: &sync.Mutex{}, + StopPrintCH: stopPrintCh, + EstimatedTimeToAnnounceUpdateCh: EstimatedTimeToAnnounceUpdateCh, }, nil } @@ -120,8 +120,7 @@ func (R *RatioSpoof) Run() { signal.Notify(sigCh, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) R.firstAnnounce() - go R.decreaseTimer() - go R.updateTimer() + go R.updateEstimatedTimeToAnnounceListener() go func() { for { R.generateNextAnnounce() @@ -138,12 +137,26 @@ func (R *RatioSpoof) firstAnnounce() { R.fireAnnounce(false) } -func (R *RatioSpoof) updateInterval(resp tracker.TrackerResponse) { - if resp.Interval > 0 { - R.AnnounceInterval = resp.Interval +func (R *RatioSpoof) updateInterval(interval int) { + if interval > 0 { + R.AnnounceInterval = interval } else { R.AnnounceInterval = 1800 } + R.updateEstimatedTimeToAnnounce(R.AnnounceInterval) +} + +func (R *RatioSpoof) updateEstimatedTimeToAnnounce(interval int) { + R.mutex.Lock() + defer R.mutex.Unlock() + R.EstimatedTimeToAnnounce = time.Now().Add(time.Duration(interval) * time.Second) +} + +func (R *RatioSpoof) updateEstimatedTimeToAnnounceListener() { + for { + interval := <-R.EstimatedTimeToAnnounceUpdateCh + R.updateEstimatedTimeToAnnounce(interval) + } } func (R *RatioSpoof) updateSeedersAndLeechers(resp tracker.TrackerResponse) { @@ -166,19 +179,18 @@ func (R *RatioSpoof) fireAnnounce(retry bool) error { "{event}", R.Status, "{numwant}", fmt.Sprint(R.NumWant)) query := replacer.Replace(R.BitTorrentClient.Query()) - trackerResp, err := R.Tracker.Announce(query, R.BitTorrentClient.Headers(), retry, R.timerUpdateCh) + trackerResp, err := R.Tracker.Announce(query, R.BitTorrentClient.Headers(), retry, R.EstimatedTimeToAnnounceUpdateCh) if err != nil { log.Fatalf("failed to reach the tracker:\n%s ", err.Error()) } if trackerResp != nil { R.updateSeedersAndLeechers(*trackerResp) - R.updateInterval(*trackerResp) + R.updateInterval(trackerResp.Interval) } return nil } func (R *RatioSpoof) generateNextAnnounce() { - R.timerUpdateCh <- R.AnnounceInterval lastAnnounce := R.AnnounceHistory.Back().(AnnounceEntry) currentDownloaded := lastAnnounce.Downloaded var downloadCandidate int @@ -198,25 +210,6 @@ func (R *RatioSpoof) generateNextAnnounce() { R.addAnnounce(d, u, l, (float32(d)/float32(R.TorrentInfo.TotalSize))*100) } -func (R *RatioSpoof) decreaseTimer() { - for { - time.Sleep(1 * time.Second) - R.mutex.Lock() - if R.CurrentAnnounceTimer > 0 { - R.CurrentAnnounceTimer-- - } - R.mutex.Unlock() - } -} - -func (R *RatioSpoof) updateTimer() { - for { - newValue := <-R.timerUpdateCh - R.mutex.Lock() - R.CurrentAnnounceTimer = newValue - R.mutex.Unlock() - } -} func calculateNextTotalSizeByte(speedBytePerSecond, currentByte, pieceSizeByte, seconds, limitTotalBytes int) int { if speedBytePerSecond == 0 { diff --git a/internal/tracker/tracker.go b/internal/tracker/tracker.go index ebb2c0d..883c9fb 100644 --- a/internal/tracker/tracker.go +++ b/internal/tracker/tracker.go @@ -26,7 +26,7 @@ type TrackerResponse struct { Leechers int } -func NewHttpTracker(torrentInfo *bencode.TorrentInfo, timerChangeChannel chan<- int) (*HttpTracker, error) { +func NewHttpTracker(torrentInfo *bencode.TorrentInfo) (*HttpTracker, error) { var result []string for _, url := range torrentInfo.TrackerInfo.Urls { @@ -46,20 +46,20 @@ func (T *HttpTracker) SwapFirst(currentIdx int) { T.Urls[currentIdx] = aux } -func (T *HttpTracker) Announce(query string, headers map[string]string, retry bool, timerUpdateChannel chan<- int) (*TrackerResponse, error) { +func (T *HttpTracker) Announce(query string, headers map[string]string, retry bool, estimatedTimeToAnnounceUpdateCh chan<- int) (*TrackerResponse, error) { defer func() { T.RetryAttempt = 0 }() if retry { - retryDelay := 30 * time.Second + retryDelay := 30 for { trackerResp, err := T.tryMakeRequest(query, headers) if err != nil { - timerUpdateChannel <- int(retryDelay.Seconds()) + estimatedTimeToAnnounceUpdateCh <- retryDelay T.RetryAttempt++ - time.Sleep(retryDelay) + time.Sleep(time.Duration(retryDelay) * time.Second) retryDelay *= 2 - if retryDelay.Seconds() > 900 { + if retryDelay > 900 { retryDelay = 900 } continue