11# 写UI
2+ import time
23import tkinter as tk
34from datetime import date
45from pathlib import Path
@@ -123,9 +124,23 @@ def __init__(self, selected_language: str = "en") -> None:
123124 self .progress_bar = ttk .Progressbar (
124125 main_frame , orient = "horizontal" , length = 400 , mode = "determinate"
125126 )
126- self .progress_bar .grid (column = 0 , row = 4 , columnspan = 2 , padx = 10 , pady = 10 )
127+ self .progress_bar .grid (column = 0 , row = 4 , columnspan = 2 , padx = 10 , pady = ( 10 , 0 ) )
127128 logger .debug ("Progress bar created" )
128129
130+ # Percentage label
131+ self .progress_label = ttk .Label (main_frame , text = "0%" )
132+ self .progress_label .grid (column = 0 , row = 5 , columnspan = 2 )
133+ logger .debug ("Progress label created" )
134+
135+ # Cancel button
136+ self .cancel_download = False # flag to signal cancellation
137+ cancel_button = ttk .Button (
138+ main_frame ,
139+ text = self .translations ["cancel_button" ],
140+ command = self ._cancel_download ,
141+ )
142+ cancel_button .grid (column = 0 , row = 6 , columnspan = 2 , pady = (5 , 10 ))
143+
129144 # Create a menu bar
130145 menuBar = tk .Menu (self .win )
131146 self .win .config (menu = menuBar )
@@ -227,6 +242,12 @@ def download_video_and_display_progress(self, resp: requests.Response) -> None:
227242 logger .error ("Invalid URL: %s" , resp .url )
228243 return
229244
245+ self .scr .insert (
246+ tk .INSERT ,
247+ self .translations ["total_size_message" ].format (content_size ) + "\n " ,
248+ )
249+ logger .debug ("Total size of the video: {:.2f} MB" .format (content_size ))
250+
230251 # Extract the media extension from the URL path
231252 file_extension : str = parsed_url .path .split ("." )[- 1 ]
232253 file_name : str = parsed_url .path .split ("/" )[- 1 ] or f"video.{ file_extension } "
@@ -236,21 +257,36 @@ def download_video_and_display_progress(self, resp: requests.Response) -> None:
236257 # Set up progress bar
237258 self .progress_bar ["maximum" ] = content_size
238259 self .progress_bar ["value" ] = 0
260+ self .progress_label .config (text = "0%" )
261+ self .cancel_download = False # reset cancel flag
239262 self .win .update_idletasks () # Ensure initial draw
240263
264+ downloaded = 0
265+ start_time : float = time .time ()
266+
241267 with open (file_path , mode = "wb" ) as f :
242- self .scr .insert (
243- tk .INSERT ,
244- self .translations ["total_size_message" ].format (content_size ) + "\n " ,
245- )
246- logger .debug ("Total size of the video: {:.2f} MB" .format (content_size ))
247- downloaded = 0.0
248268 for chunk in resp .iter_content (MB ):
269+ if self .cancel_download :
270+ self .scr .insert (
271+ tk .INSERT , self .translations ["cancelled_message" ] + "\n "
272+ )
273+ logger .info ("Download cancelled by user." )
274+ return
275+
249276 if not chunk :
250277 continue
278+
251279 f .write (chunk )
252280 downloaded += 1
281+
282+ percent : float = (downloaded / content_size ) * 100
283+ elapsed : float = time .time () - start_time
284+ rate : float = downloaded / elapsed if elapsed > 0 else 0
285+ remaining : float = (content_size - downloaded ) / rate if rate > 0 else 0
286+ eta : str = time .strftime ("%M:%S" , time .gmtime (remaining ))
287+
253288 self .progress_bar ["value" ] = downloaded
289+ self .progress_label .config (text = f"{ percent :.1f} % ETA: { eta } " )
254290 self .win .update_idletasks () # Refresh GUI
255291
256292 self .scr .insert (
@@ -343,6 +379,11 @@ def _help(self) -> None:
343379 self .translations ["help_button" ], self .translations ["help_message" ]
344380 )
345381
382+ def _cancel_download (self ) -> None :
383+ """Sets the cancel flag to True."""
384+ logger .debug ("Cancel download button clicked" )
385+ self .cancel_download = True
386+
346387 def run (self ) -> None :
347388 """Run the program."""
348389 logger .debug ("Starting the main loop of the GUI" )
0 commit comments