diff --git a/OpenUtau.Core/DocManager.cs b/OpenUtau.Core/DocManager.cs
index 8a309a28e..533ce7a6d 100644
--- a/OpenUtau.Core/DocManager.cs
+++ b/OpenUtau.Core/DocManager.cs
@@ -115,11 +115,13 @@ public void SearchAllPlugins() {
UCommandGroup? undoGroup = null;
UCommandGroup? savedPoint = null;
UCommandGroup? autosavedPoint = null;
+ public bool Recovered { get; set; } = false; // Flag to not overwrite backup file
public bool ChangesSaved {
get {
return (Project.Saved || (Project.tracks.Count <= 1 && Project.parts.Count == 0)) &&
- (undoQueue.Count > 0 && savedPoint == undoQueue.Last() || undoQueue.Count == 0 && savedPoint == null);
+ (undoQueue.Count > 0 && savedPoint == undoQueue.Last() || undoQueue.Count == 0 && savedPoint == null) &&
+ !Recovered;
}
}
@@ -142,7 +144,7 @@ private void CrashSave() {
: Path.GetFileNameWithoutExtension(Project.FilePath);
string backup = Path.Join(dir, filename + "-backup.ustx");
Log.Information($"Saving backup {backup}.");
- Format.Ustx.Save(backup, Project);
+ Format.Ustx.AutoSave(backup, Project);
Log.Information($"Saved backup {backup}.");
} catch (Exception e) {
Log.Error(e, "Save backup failed.");
diff --git a/OpenUtau.Core/Format/Formats.cs b/OpenUtau.Core/Format/Formats.cs
index d378ca925..672fa1f84 100644
--- a/OpenUtau.Core/Format/Formats.cs
+++ b/OpenUtau.Core/Format/Formats.cs
@@ -108,6 +108,25 @@ public static UProject[] ReadProjects(string[] files){
.ToArray();
}
+ ///
+ /// Load project from backup file.
+ ///
+ /// Names of the files to be loaded
+ public static void RecoveryProject(string[] files) {
+ UProject project = ReadProject(files);
+ if (project != null) {
+ string originalPath = project.FilePath.Replace("-autosave.ustx", ".ustx").Replace("-backup.ustx", ".ustx");
+ if (File.Exists(originalPath)) {
+ project.FilePath = originalPath;
+ } else {
+ project.FilePath = string.Empty;
+ project.Saved = false;
+ }
+
+ DocManager.Inst.ExecuteCmd(new LoadProjectNotification(project));
+ }
+ }
+
///
/// Import tracks from files to the current existing editing project.
///
diff --git a/OpenUtau.Core/Format/USTx.cs b/OpenUtau.Core/Format/USTx.cs
index a27888054..aaa19f74a 100644
--- a/OpenUtau.Core/Format/USTx.cs
+++ b/OpenUtau.Core/Format/USTx.cs
@@ -5,6 +5,7 @@
using System.Text;
using OpenUtau.Classic;
using OpenUtau.Core.Ustx;
+using OpenUtau.Core.Util;
using Serilog;
namespace OpenUtau.Core.Format {
@@ -101,6 +102,9 @@ public static void Save(string filePath, UProject project) {
File.WriteAllText(filePath, Yaml.DefaultSerializer.Serialize(project), Encoding.UTF8);
project.Saved = true;
project.AfterSave();
+ Preferences.Default.RecoveryPath = string.Empty;
+ Preferences.Save();
+ DocManager.Inst.Recovered = false;
} catch (Exception ex) {
var e = new MessageCustomizableException("Failed to save ustx: {filePath}", $": {filePath}", ex);
DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(e));
@@ -113,6 +117,8 @@ public static void AutoSave(string filePath, UProject project) {
project.BeforeSave();
File.WriteAllText(filePath, Yaml.DefaultSerializer.Serialize(project), Encoding.UTF8);
project.AfterSave();
+ Preferences.Default.RecoveryPath = filePath;
+ Preferences.Save();
} catch (Exception ex) {
Log.Error(ex, $"Failed to autosave: {filePath}");
}
diff --git a/OpenUtau.Core/Util/Preferences.cs b/OpenUtau.Core/Util/Preferences.cs
index 1c525fb54..58c47b3b0 100644
--- a/OpenUtau.Core/Util/Preferences.cs
+++ b/OpenUtau.Core/Util/Preferences.cs
@@ -192,9 +192,9 @@ public class SerializablePreferences {
public bool LockUnselectedNotesPitch = true;
public bool LockUnselectedNotesVibrato = true;
public bool LockUnselectedNotesExpressions = true;
-
public bool VoicebankPublishUseIgnore = true;
public string VoicebankPublishIgnores = "#Adobe Audition\n*.pkf\n\n#UTAU Engines\n*.ctspec\n*.d4c\n*.dio\n*.frc\n*.frt\n#*.frq\n*.harvest\n*.lessaudio\n*.llsm\n*.mrq\n*.pitchtier\n*.pkf\n*.platinum\n*.pmk\n*.star\n*.uspec\n*.vs4ufrq\n\n#UTAU related tools\n$read\n*.setParam-Scache\n*.lbp\n*.lbp.caches/*\n\n#OpenUtau\nerrors.txt\n*.sc.npz";
+ public string RecoveryPath = string.Empty;
}
}
}
diff --git a/OpenUtau/Strings/Strings.axaml b/OpenUtau/Strings/Strings.axaml
index 9daf39aff..9260c8f42 100644
--- a/OpenUtau/Strings/Strings.axaml
+++ b/OpenUtau/Strings/Strings.axaml
@@ -48,6 +48,8 @@
Yes
No resampler
No resampler! Put your favourite resampler exe or dll in the Resamplers folder and choose it in Preferences!
+ Previously, OpenUtau terminated abnormally. Do you want to recover?
+ Recovery project
This tool will change the tempo of the project without changing the actual positions and durations (in seconds) of notes.
New BPM:
Time Signature
diff --git a/OpenUtau/Strings/Strings.ja-JP.axaml b/OpenUtau/Strings/Strings.ja-JP.axaml
index 23547156e..2e930fe0b 100644
--- a/OpenUtau/Strings/Strings.ja-JP.axaml
+++ b/OpenUtau/Strings/Strings.ja-JP.axaml
@@ -48,6 +48,8 @@
はい
エンジンがありません
エンジンが設定されていません!お好みのエンジンのEXEやDLLファイルをResamplerフォルダに入れて、環境設定でエンジンを選択してください!
+ 前回異常終了しました。復旧しますか?
+ プロジェクトの復旧
このツールでは、音符の実際の位置と長さ(秒単位)を変えずに、プロジェクトのテンポを変更します。
新しいBPM:
拍子
diff --git a/OpenUtau/ViewModels/MainWindowViewModel.cs b/OpenUtau/ViewModels/MainWindowViewModel.cs
index b7b177ace..64ecaec82 100644
--- a/OpenUtau/ViewModels/MainWindowViewModel.cs
+++ b/OpenUtau/ViewModels/MainWindowViewModel.cs
@@ -9,6 +9,7 @@
using OpenUtau.App.Views;
using OpenUtau.Core;
using OpenUtau.Core.Ustx;
+using OpenUtau.Core.Util;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
@@ -90,19 +91,38 @@ public void Redo() {
DocManager.Inst.Redo();
}
- public void InitProject() {
+ public async void InitProject(MainWindow window) {
+ var recPath = Preferences.Default.RecoveryPath;
+ if (!string.IsNullOrWhiteSpace(recPath) && File.Exists(recPath)) {
+ var result = await MessageBox.Show(
+ window,
+ $"{ThemeManager.GetString("dialogs.recovery")}\n{recPath}",
+ ThemeManager.GetString("dialogs.recovery.caption"),
+ MessageBox.MessageBoxButtons.YesNo);
+ if (result == MessageBox.MessageBoxResult.Yes) {
+ DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), true, "project"));
+ try {
+ Core.Format.Formats.RecoveryProject(new string[] { recPath });
+ DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(-1, true));
+ DocManager.Inst.Recovered = true;
+ this.RaisePropertyChanged(nameof(Title));
+ } finally {
+ DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), false, "project"));
+ }
+ return;
+ }
+ }
+
var args = Environment.GetCommandLineArgs();
if (args.Length == 2 && File.Exists(args[1])) {
try {
Core.Format.Formats.LoadProject(new string[] { args[1] });
DocManager.Inst.ExecuteCmd(new VoiceColorRemappingNotification(-1, true));
- return;
} catch (Exception e) {
var customEx = new MessageCustomizableException($"Failed to open file {args[1]}", $": {args[1]}", e);
DocManager.Inst.ExecuteCmd(new ErrorMessageNotification(customEx));
}
}
- NewProject();
}
public void NewProject() {
@@ -119,6 +139,7 @@ public void NewProject() {
}
}
DocManager.Inst.ExecuteCmd(new LoadProjectNotification(Core.Format.Ustx.Create()));
+ DocManager.Inst.Recovered = false;
}
public void OpenProject(string[] files) {
@@ -133,6 +154,7 @@ public void OpenProject(string[] files) {
} finally {
DocManager.Inst.ExecuteCmd(new LoadingNotification(typeof(MainWindow), false, "project"));
}
+ DocManager.Inst.Recovered = false;
}
public void SaveProject(string file = "") {
@@ -142,7 +164,7 @@ public void SaveProject(string file = "") {
DocManager.Inst.ExecuteCmd(new SaveProjectNotification(file));
this.RaisePropertyChanged(nameof(Title));
}
-
+
public void ImportTracks(UProject[] loadedProjects, bool importTempo){
if (loadedProjects == null || loadedProjects.Length < 1) {
return;
diff --git a/OpenUtau/Views/MainWindow.axaml.cs b/OpenUtau/Views/MainWindow.axaml.cs
index 9dc59b8fd..916f18993 100644
--- a/OpenUtau/Views/MainWindow.axaml.cs
+++ b/OpenUtau/Views/MainWindow.axaml.cs
@@ -4,7 +4,6 @@
using System.IO;
using System.Linq;
using System.Reactive;
-using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
@@ -53,7 +52,7 @@ public MainWindow() {
Log.Information("Initialized main window component.");
DataContext = viewModel = new MainWindowViewModel();
- viewModel.InitProject();
+ viewModel.NewProject();
viewModel.AddTempoChangeCmd = ReactiveCommand.Create(tick => AddTempoChange(tick));
viewModel.DelTempoChangeCmd = ReactiveCommand.Create(tick => DelTempoChange(tick));
viewModel.AddTimeSigChangeCmd = ReactiveCommand.Create(bar => AddTimeSigChange(bar));
@@ -88,6 +87,10 @@ public MainWindow() {
Log.Information("Created main window.");
}
+ public void InitProject() {
+ viewModel.InitProject(this);
+ }
+
void OnEditTimeSignature(object sender, PointerPressedEventArgs args) {
var project = DocManager.Inst.Project;
var timeSig = project.timeSignatures[0];
@@ -1298,6 +1301,8 @@ public async void WindowClosing(object? sender, WindowClosingEventArgs e) {
PathManager.Inst.ClearCache();
Log.Information("Cache cleared.");
}
+ Preferences.Default.RecoveryPath = string.Empty;
+ Preferences.Save();
return;
}
e.Cancel = true;
@@ -1361,4 +1366,4 @@ public void OnNext(UCommand cmd, bool isUndo) {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenUtau/Views/SplashWindow.axaml.cs b/OpenUtau/Views/SplashWindow.axaml.cs
index edc90f2f4..902afdce9 100644
--- a/OpenUtau/Views/SplashWindow.axaml.cs
+++ b/OpenUtau/Views/SplashWindow.axaml.cs
@@ -54,6 +54,7 @@ private void Start() {
var mainWindow = new MainWindow();
mainWindow.Show();
desktop.MainWindow = mainWindow;
+ mainWindow.InitProject();
Close();
}
}, CancellationToken.None, TaskContinuationOptions.None, mainScheduler);