| 15 | ) |
| 16 | |
| 17 | func (m *RootModel) processProgressMsg(msg events.ProgressMsg) tea.Cmd { |
| 18 | d := m.FindDownloadByID(msg.DownloadID) |
| 19 | if d == nil || d.done || d.paused { |
| 20 | return nil |
| 21 | } |
| 22 | |
| 23 | prevDownloaded := d.Downloaded |
| 24 | d.Downloaded = msg.Downloaded |
| 25 | d.Total = msg.Total |
| 26 | d.Speed = msg.Speed |
| 27 | d.Elapsed = msg.Elapsed |
| 28 | d.Connections = msg.ActiveConnections |
| 29 | |
| 30 | // Keep "Resuming..." visible until we observe actual transfer. |
| 31 | if d.resuming && (d.Speed > 0 || d.Downloaded > prevDownloaded) { |
| 32 | d.resuming = false |
| 33 | } |
| 34 | |
| 35 | // Update Chunk State if provided |
| 36 | if msg.BitmapWidth > 0 && len(msg.ChunkBitmap) > 0 { |
| 37 | if d.state != nil && msg.Total > 0 { |
| 38 | d.state.SetTotalSize(msg.Total) |
| 39 | } |
| 40 | // We only get bitmap, no progress array (to save bandwidth) |
| 41 | // State needs to be updated carefully |
| 42 | if d.state != nil { |
| 43 | d.state.RestoreBitmap(msg.ChunkBitmap, msg.ActualChunkSize) |
| 44 | } |
| 45 | if d.state != nil && len(msg.ChunkProgress) > 0 { |
| 46 | d.state.SetChunkProgress(msg.ChunkProgress) |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | var cmd tea.Cmd |
| 51 | if d.Total > 0 { |
| 52 | percentage := float64(d.Downloaded) / float64(d.Total) |
| 53 | cmd = d.progress.SetPercent(percentage) |
| 54 | } |
| 55 | |
| 56 | // Update speed graph history with EMA smoothing for smooth transitions |
| 57 | if time.Since(m.lastSpeedHistoryUpdate) >= GraphUpdateInterval { |
| 58 | totalSpeed := float64(m.calcTotalSpeedBps()) |
| 59 | // EMA smooth against previous graph point for visual continuity |
| 60 | var smoothed float64 |
| 61 | if m.Settings != nil && config.Resolve[bool](m.Settings.General.LiveSpeedGraph) { |
| 62 | smoothed = totalSpeed |
| 63 | } else if len(m.SpeedHistory) > 0 { |
| 64 | prev := m.SpeedHistory[len(m.SpeedHistory)-1] |
| 65 | const graphAlpha = 0.3 // Graph smoothing factor |
| 66 | smoothed = graphAlpha*totalSpeed + (1-graphAlpha)*prev |
| 67 | } else { |
| 68 | smoothed = totalSpeed |
| 69 | } |
| 70 | if len(m.SpeedHistory) > 0 { |
| 71 | m.SpeedHistory = append(m.SpeedHistory[1:], smoothed) |
| 72 | } |
| 73 | m.lastSpeedHistoryUpdate = time.Now() |
| 74 | } |