36
36
import webbrowser
37
37
from time import sleep , time
38
38
from datetime import datetime
39
- from threading import Thread
40
39
from uuid import uuid4
41
40
import zipfile
41
+ import threading
42
42
43
43
import openshot # Python module for libopenshot (required video editing module installed separately)
44
44
from PyQt5 .QtCore import (
@@ -482,31 +482,32 @@ def actionClearHistory_trigger(self):
482
482
483
483
def save_project (self , file_path ):
484
484
""" 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
487
488
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" ))
492
493
493
- # Save recovery file first
494
- self .save_recovery (file_path )
494
+ # Save recovery file
495
+ self .save_recovery (file_path )
495
496
496
- # Save project to file
497
- app .project .save (file_path )
497
+ # Save project to file
498
+ app .project .save (file_path )
498
499
499
- # Set Window title
500
- self .SetWindowTitle ()
500
+ # Set Window title
501
+ self .SetWindowTitle ()
501
502
502
- # Load recent projects again
503
- self .load_recent_menu ()
503
+ # Load recent projects again
504
+ self .load_recent_menu ()
504
505
505
- log .info ("Saved project %s" , file_path )
506
+ log .info ("Saved project %s" , file_path )
506
507
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 ))
510
511
511
512
def save_recovery (self , file_path ):
512
513
"""Saves the project and manages recovery files based on configured limits."""
@@ -523,17 +524,13 @@ def save_recovery(self, file_path):
523
524
recovery_filename = f"{ timestamp } -{ file_name } .zip"
524
525
recovery_path = os .path .join (info .RECOVERY_PATH , recovery_filename )
525
526
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 } " )
537
534
538
535
def manage_recovery_files (self , daily_limit , historical_limit , file_name ):
539
536
"""Ensures recovery files adhere to the configured daily and historical limits."""
@@ -747,7 +744,7 @@ def actionSave_trigger(self):
747
744
file_path = "%s.osp" % file_path
748
745
749
746
# Save project
750
- self .save_project (file_path )
747
+ threading . Thread ( target = self .save_project , args = (file_path ,), daemon = True ). start ( )
751
748
752
749
def auto_save_project (self ):
753
750
"""Auto save the project"""
@@ -768,7 +765,7 @@ def auto_save_project(self):
768
765
769
766
# Save project
770
767
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 ( )
772
769
773
770
# Remove backup.osp (if any)
774
771
if os .path .exists (info .BACKUP_FILE ):
@@ -813,7 +810,7 @@ def actionSaveAs_trigger(self):
813
810
file_path = "%s.osp" % file_path
814
811
815
812
# Save new project
816
- self .save_project (file_path )
813
+ threading . Thread ( target = self .save_project , args = (file_path ,), daemon = True ). start ( )
817
814
818
815
def actionImportFiles_trigger (self ):
819
816
app = get_app ()
@@ -2918,36 +2915,37 @@ def populate_restore_menu(self):
2918
2915
2919
2916
def restore_version_clicked (self , file_path ):
2920
2917
"""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
2924
2922
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 } `" )
2945
2943
2946
- # Open the recovered project
2947
- self .OpenProjectSignal .emit (current_filepath )
2944
+ # Open the recovered project
2945
+ self .OpenProjectSignal .emit (current_filepath )
2948
2946
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 )
2951
2949
2952
2950
def remove_recent_project (self , file_path ):
2953
2951
"""Remove a project from the Recent menu if OpenShot can't find it"""
@@ -3554,6 +3552,7 @@ def __init__(self, *args):
3554
3552
super ().__init__ (* args )
3555
3553
self .initialized = False
3556
3554
self .shutting_down = False
3555
+ self .lock = threading .Lock ()
3557
3556
self .installEventFilter (self )
3558
3557
3559
3558
# set window on app for reference during initialization of children
0 commit comments