Skip to content

Commit

Permalink
Adding recovery menu to OpenShot for recovering previous auto-save files
Browse files Browse the repository at this point in the history
  • Loading branch information
jonoomph committed Oct 12, 2024
1 parent 5ed8300 commit 7215088
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 0 deletions.
84 changes: 84 additions & 0 deletions src/windows/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ def actionNew_trigger(self):
app.updates.reset()
self.updateStatusChanged(False, False)

# Load recent projects again
self.load_recent_menu()

# Refresh files views
self.refreshFilesSignal.emit()
log.info("New Project created.")
Expand Down Expand Up @@ -2787,6 +2790,86 @@ def load_recent_menu(self):
self.recent_menu.addAction(self.actionClearRecents)
self.actionClearRecents.triggered.connect(self.clear_recents_clicked)

# Build recovery menu as well
self.load_restore_menu()

def time_ago_string(self, timestamp):
""" Returns a friendly time difference string for the given timestamp. """
from datetime import datetime
delta = datetime.now() - datetime.fromtimestamp(timestamp)
seconds = delta.total_seconds()

if seconds < 60:
return f"{int(seconds)} seconds ago"
elif seconds < 3600:
minutes = seconds // 60
return f"{int(minutes)} minute{'s' if minutes > 1 else ''} ago"
elif seconds < 86400:
hours = seconds // 3600
return f"{int(hours)} hour{'s' if hours > 1 else ''} ago"
else:
days = seconds // 86400
return f"{int(days)} day{'s' if days > 1 else ''} ago"

def load_restore_menu(self):
""" Clear and load the list of restore version menu items """
recovery_dir = info.RECOVERY_PATH
_ = get_app()._tr # Get translation function
app = get_app() # Get the application instance

# Get list of recovery files in the directory that match the current project
current_filepath = app.project.current_filepath if app.project else None
recovery_files = [
f for f in os.listdir(recovery_dir)
if f.endswith(".osp") and current_filepath and f.split("-", 1)[1].startswith(os.path.basename(current_filepath).replace(".osp", ""))
]

# Add Restore Previous Version menu (after Open File)
if not self.restore_menu:
# Create a new restore menu
self.restore_menu = self.menuFile.addMenu(QIcon.fromTheme("edit-undo"), _("Recovery"))
self.menuFile.insertMenu(self.actionRecoveryProjects, self.restore_menu)
else:
# Clear the existing children
self.restore_menu.clear()

# Add recovery files to menu
# Show just a placeholder menu, if we have no recovery files
if not recovery_files:
self.restore_menu.addAction(_("No Previous Versions Available")).setDisabled(True)
return

# Sort files in descending order (latest first)
recovery_files.sort(reverse=True)

for file_name in recovery_files:
# Extract timestamp from file name
try:
timestamp = int(file_name.split("-", 1)[0])
friendly_time = self.time_ago_string(timestamp)
file_path = os.path.join(recovery_dir, file_name)

# Add each recovery file
new_action = self.restore_menu.addAction(friendly_time)
new_action.triggered.connect(functools.partial(self.restore_version_clicked, file_path))
except ValueError:
continue

def restore_version_clicked(self, file_path):
""" Handle restoring a previous version when a menu item is clicked """
app = get_app() # Get the application instance
current_filepath = app.project.current_filepath if app.project else None

if current_filepath:
# Copy the recovery file to the current project folder without overwriting existing project
project_folder = os.path.dirname(current_filepath)
new_file_path = os.path.join(project_folder, os.path.basename(file_path))
log.info(f"Recover project from {file_path} to {new_file_path}")
shutil.copy(file_path, new_file_path)

# Emit signal to open the copied project file
self.OpenProjectSignal.emit(new_file_path)

def remove_recent_project(self, file_path):
"""Remove a project from the Recent menu if OpenShot can't find it"""
s = get_app().get_settings()
Expand Down Expand Up @@ -3407,6 +3490,7 @@ def __init__(self, *args):
# Load user settings for window
s = app.get_settings()
self.recent_menu = None
self.restore_menu = None

# Track metrics
track_metric_session() # start session
Expand Down
16 changes: 16 additions & 0 deletions src/windows/ui/main-window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
<addaction name="actionNew"/>
<addaction name="actionOpen"/>
<addaction name="actionRecentProjects"/>
<addaction name="actionRecoveryProjects"/>
<addaction name="separator"/>
<addaction name="actionSave"/>
<addaction name="actionSaveAs"/>
Expand Down Expand Up @@ -1257,6 +1258,21 @@
<string>Show All</string>
</property>
</action>
<action name="actionRecoveryProjects">
<property name="checkable">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="edit-undo" resource="../../../images/openshot.qrc">
<normaloff>:/icons/Humanity/actions/16/edit-undo.svg</normaloff>:/icons/Humanity/actions/16/edit-undo.svg</iconset>
</property>
<property name="text">
<string>Recovery Placeholder</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionRecentProjects">
<property name="checkable">
<bool>false</bool>
Expand Down

0 comments on commit 7215088

Please sign in to comment.