3636import webbrowser
3737from time import sleep , time
3838from datetime import datetime
39- from threading import Thread
4039from uuid import uuid4
4140import zipfile
41+ import threading
4242
4343import openshot # Python module for libopenshot (required video editing module installed separately)
4444from PyQt5 .QtCore import (
@@ -482,31 +482,32 @@ def actionClearHistory_trigger(self):
482482
483483 def save_project (self , file_path ):
484484 """ Save a project to a file path, and refresh the screen """
485- app = get_app ()
486- _ = app ._tr # Get translation function
485+ with self .lock :
486+ app = get_app ()
487+ _ = app ._tr # Get translation function
487488
488- try :
489- # Update history in project data
490- s = app .get_settings ()
491- app .updates .save_history (app .project , s .get ("history-limit" ))
489+ try :
490+ # Update history in project data
491+ s = app .get_settings ()
492+ app .updates .save_history (app .project , s .get ("history-limit" ))
492493
493- # Save recovery file first
494- self .save_recovery (file_path )
494+ # Save recovery file
495+ self .save_recovery (file_path )
495496
496- # Save project to file
497- app .project .save (file_path )
497+ # Save project to file
498+ app .project .save (file_path )
498499
499- # Set Window title
500- self .SetWindowTitle ()
500+ # Set Window title
501+ self .SetWindowTitle ()
501502
502- # Load recent projects again
503- self .load_recent_menu ()
503+ # Load recent projects again
504+ self .load_recent_menu ()
504505
505- log .info ("Saved project %s" , file_path )
506+ log .info ("Saved project %s" , file_path )
506507
507- except Exception as ex :
508- log .error ("Couldn't save project %s" , file_path , exc_info = 1 )
509- QMessageBox .warning (self , _ ("Error Saving Project" ), str (ex ))
508+ except Exception as ex :
509+ log .error ("Couldn't save project %s" , file_path , exc_info = 1 )
510+ QMessageBox .warning (self , _ ("Error Saving Project" ), str (ex ))
510511
511512 def save_recovery (self , file_path ):
512513 """Saves the project and manages recovery files based on configured limits."""
@@ -523,17 +524,13 @@ def save_recovery(self, file_path):
523524 recovery_filename = f"{ timestamp } -{ file_name } .zip"
524525 recovery_path = os .path .join (info .RECOVERY_PATH , recovery_filename )
525526
526- def create_recovery ():
527- try :
528- with zipfile .ZipFile (recovery_path , 'w' , zipfile .ZIP_DEFLATED ) as zipf :
529- zipf .write (file_path , os .path .basename (file_path ))
530- log .debug (f"Zipped recovery file created: { recovery_path } " )
531- self .manage_recovery_files (daily_limit , historical_limit , file_name )
532- except Exception as e :
533- log .error (f"Failed to create zipped recovery file { recovery_path } : { e } " )
534-
535- # Run the recovery process in a thread
536- Thread (target = create_recovery , daemon = True ).start ()
527+ try :
528+ with zipfile .ZipFile (recovery_path , 'w' , zipfile .ZIP_DEFLATED ) as zipf :
529+ zipf .write (file_path , os .path .basename (file_path ))
530+ log .debug (f"Zipped recovery file created: { recovery_path } " )
531+ self .manage_recovery_files (daily_limit , historical_limit , file_name )
532+ except Exception as e :
533+ log .error (f"Failed to create zipped recovery file { recovery_path } : { e } " )
537534
538535 def manage_recovery_files (self , daily_limit , historical_limit , file_name ):
539536 """Ensures recovery files adhere to the configured daily and historical limits."""
@@ -747,7 +744,7 @@ def actionSave_trigger(self):
747744 file_path = "%s.osp" % file_path
748745
749746 # Save project
750- self .save_project (file_path )
747+ threading . Thread ( target = self .save_project , args = (file_path ,), daemon = True ). start ( )
751748
752749 def auto_save_project (self ):
753750 """Auto save the project"""
@@ -768,7 +765,7 @@ def auto_save_project(self):
768765
769766 # Save project
770767 log .info ("Auto save project file: %s" , file_path )
771- self .save_project (file_path )
768+ threading . Thread ( target = self .save_project , args = (file_path ,), daemon = True ). start ( )
772769
773770 # Remove backup.osp (if any)
774771 if os .path .exists (info .BACKUP_FILE ):
@@ -813,7 +810,7 @@ def actionSaveAs_trigger(self):
813810 file_path = "%s.osp" % file_path
814811
815812 # Save new project
816- self .save_project (file_path )
813+ threading . Thread ( target = self .save_project , args = (file_path ,), daemon = True ). start ( )
817814
818815 def actionImportFiles_trigger (self ):
819816 app = get_app ()
@@ -2918,36 +2915,37 @@ def populate_restore_menu(self):
29182915
29192916 def restore_version_clicked (self , file_path ):
29202917 """Restore a previous project file from the recovery folder"""
2921- app = get_app ()
2922- current_filepath = app .project .current_filepath if app .project else None
2923- _ = get_app ()._tr
2918+ with self .lock :
2919+ app = get_app ()
2920+ current_filepath = app .project .current_filepath if app .project else None
2921+ _ = get_app ()._tr
29242922
2925- try :
2926- # Rename the original project file
2927- recovered_filename = os .path .splitext (os .path .basename (current_filepath ))[0 ] + f"-{ int (time ())} -backup.osp"
2928- recovered_filepath = os .path .join (os .path .dirname (current_filepath ), recovered_filename )
2929- if os .path .exists (current_filepath ):
2930- shutil .move (current_filepath , recovered_filepath )
2931- log .info (f"Backup current project to: { recovered_filepath } " )
2932-
2933- # Unzip if the selected recovery file is a .zip file
2934- if file_path .endswith (".zip" ):
2935- with zipfile .ZipFile (file_path , 'r' ) as zipf :
2936- # Extract over top original project *.osp file
2937- zipf .extractall (os .path .dirname (current_filepath ))
2938- extracted_files = zipf .namelist ()
2939- if len (extracted_files ) != 1 :
2940- raise ValueError ("Unexpected number of files in recovery zip." )
2941- else :
2942- # Replace the original *.osp project file with the recovery file *.osp
2943- shutil .copyfile (file_path , current_filepath )
2944- log .info (f"Recovery file `{ file_path } ` restored to: `{ current_filepath } `" )
2923+ try :
2924+ # Rename the original project file
2925+ recovered_filename = os .path .splitext (os .path .basename (current_filepath ))[0 ] + f"-{ int (time ())} -backup.osp"
2926+ recovered_filepath = os .path .join (os .path .dirname (current_filepath ), recovered_filename )
2927+ if os .path .exists (current_filepath ):
2928+ shutil .move (current_filepath , recovered_filepath )
2929+ log .info (f"Backup current project to: { recovered_filepath } " )
2930+
2931+ # Unzip if the selected recovery file is a .zip file
2932+ if file_path .endswith (".zip" ):
2933+ with zipfile .ZipFile (file_path , 'r' ) as zipf :
2934+ # Extract over top original project *.osp file
2935+ zipf .extractall (os .path .dirname (current_filepath ))
2936+ extracted_files = zipf .namelist ()
2937+ if len (extracted_files ) != 1 :
2938+ raise ValueError ("Unexpected number of files in recovery zip." )
2939+ else :
2940+ # Replace the original *.osp project file with the recovery file *.osp
2941+ shutil .copyfile (file_path , current_filepath )
2942+ log .info (f"Recovery file `{ file_path } ` restored to: `{ current_filepath } `" )
29452943
2946- # Open the recovered project
2947- self .OpenProjectSignal .emit (current_filepath )
2944+ # Open the recovered project
2945+ self .OpenProjectSignal .emit (current_filepath )
29482946
2949- except Exception as ex :
2950- log .error (f"Error recovering project from `{ file_path } ` to `{ current_filepath } `: { ex } " , exc_info = True )
2947+ except Exception as ex :
2948+ log .error (f"Error recovering project from `{ file_path } ` to `{ current_filepath } `: { ex } " , exc_info = True )
29512949
29522950 def remove_recent_project (self , file_path ):
29532951 """Remove a project from the Recent menu if OpenShot can't find it"""
@@ -3554,6 +3552,7 @@ def __init__(self, *args):
35543552 super ().__init__ (* args )
35553553 self .initialized = False
35563554 self .shutting_down = False
3555+ self .lock = threading .Lock ()
35573556 self .installEventFilter (self )
35583557
35593558 # set window on app for reference during initialization of children
0 commit comments