@@ -179,6 +179,10 @@ class ProgressState:
179179 unit_scale : bool = False
180180 rate : Optional [float ] = None
181181 last_update : float = field (default_factory = time .time )
182+ # Tracks the last state timestamp that was actually rendered.
183+ # Used to avoid advancing animation frames or forcing redraws
184+ # when nothing meaningful has changed.
185+ _last_render_seen : float = 0.0
182186 finished : bool = False
183187 failed : bool = False
184188 paused : bool = False
@@ -191,7 +195,8 @@ class ProgressManager:
191195 """
192196
193197 def __init__ (self , max_workers : int = 5 , file : Optional [TextIO ] = None ,
194- use_colors : bool = True , use_emojis : bool = True ):
198+ use_colors : bool = True , use_emojis : bool = True ,
199+ update_interval : float = 0.2 ):
195200 """
196201 Initialize the progress manager.
197202
@@ -212,29 +217,35 @@ def __init__(self, max_workers: int = 5, file: Optional[TextIO] = None,
212217 self ._total_jobs = 0
213218 self ._completed_jobs = 0
214219 self ._failed_jobs = 0
220+ self ._existed_jobs = 0
215221
216222 # Terminal control
217223 self ._lines_written = 0
218224 self ._last_display_time = 0
219- self ._update_interval = 0.1 # Update every 100ms
225+ # Update interval (seconds). When downloads change, refresh at most
226+ # once per `update_interval`. Default is 1.0s to avoid excessive redraws.
227+ self ._update_interval = float (update_interval )
220228
221229 # Display deduplication
222230 self ._last_display_content = ""
223231
224- def set_job_totals (self , total : int , completed : int = 0 , failed : int = 0 ):
232+ def set_job_totals (self , total : int , completed : int = 0 , failed : int = 0 , existed : int = 0 ):
225233 """Set the total number of jobs for overall progress tracking"""
226234 with self ._lock :
227235 self ._total_jobs = total
228236 self ._completed_jobs = completed
229237 self ._failed_jobs = failed
238+ self ._existed_jobs = existed
230239
231- def update_job_progress (self , completed : int = None , failed : int = None ):
240+ def update_job_progress (self , completed : int = None , failed : int = None , existed : int = None ):
232241 """Update overall job progress"""
233242 with self ._lock :
234243 if completed is not None :
235244 self ._completed_jobs = completed
236245 if failed is not None :
237246 self ._failed_jobs = failed
247+ if existed is not None :
248+ self ._existed_jobs = existed
238249
239250 def create_progress_bar (self , desc : str , total : Optional [int ] = None ,
240251 unit : str = "B" , unit_scale : bool = True ) -> 'ManagedTqdm' :
@@ -434,11 +445,13 @@ def _render_overall_progress(self) -> List[str]:
434445 total_colored = ColorTheme .colorize (str (self ._total_jobs ), ColorTheme .BRIGHT_WHITE )
435446 running_colored = ColorTheme .colorize (str (running ), ColorTheme .BRIGHT_CYAN )
436447 waiting_colored = ColorTheme .colorize (str (waiting ), ColorTheme .BRIGHT_YELLOW )
448+ existed_colored = ColorTheme .colorize (str (self ._existed_jobs ), ColorTheme .BRIGHT_WHITE )
437449 else :
438450 completed_colored = str (self ._completed_jobs )
439451 total_colored = str (self ._total_jobs )
440452 running_colored = str (running )
441453 waiting_colored = str (waiting )
454+ existed_colored = str (self ._existed_jobs )
442455
443456 # Calculate overall download speed from active progress bars
444457 total_rate = 0
@@ -468,7 +481,8 @@ def _render_overall_progress(self) -> List[str]:
468481 pct_colored ,
469482 f"| Jobs: { completed_colored } /{ total_colored } " ,
470483 f"| { running_colored } running" ,
471- f"| { waiting_colored } waiting"
484+ f"| { waiting_colored } waiting" ,
485+ f"| { existed_colored } existed"
472486 ])
473487
474488 if speed_str :
@@ -521,10 +535,15 @@ def _render_single_progress_bar(self, state: ProgressState) -> str:
521535 status_emoji = ColorTheme .WAITING
522536 else :
523537 # Animated spinner for active downloads
538+ # Advance spinner only when this progress state's content changed
539+ # since the last render to avoid continuous terminal refreshes
540+ # caused solely by animation frames.
524541 current_time = time .time ()
525- if current_time - _animation_state ['last_update' ] > 0.1 :
542+ last_seen = getattr (state , '_last_render_seen' , 0.0 )
543+ if state .last_update != last_seen :
526544 _animation_state ['frame' ] = (_animation_state ['frame' ] + 1 ) % len (ColorTheme .SPINNER_FRAMES )
527545 _animation_state ['last_update' ] = current_time
546+ state ._last_render_seen = state .last_update
528547 status_emoji = ColorTheme .SPINNER_FRAMES [_animation_state ['frame' ]]
529548 else :
530549 status_emoji = ""
0 commit comments