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);