Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions MSUScripter/Configs/MsuSongMsuPcmInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public List<string> GetFiles()
return files;
}

[YamlIgnore]
[YamlIgnore, JsonSchemaIgnore]
public bool HasBothSubTracksAndSubChannels
{
get
Expand All @@ -110,7 +110,7 @@ public bool HasBothSubTracksAndSubChannels
}
}

[YamlIgnore]
[YamlIgnore, JsonSchemaIgnore]
public bool HasValidSubChannelCount
{
get
Expand All @@ -120,7 +120,7 @@ public bool HasValidSubChannelCount
}
}

[YamlIgnore]
[YamlIgnore, JsonSchemaIgnore]
public bool HasValidChildTypes
{
get
Expand Down
2 changes: 1 addition & 1 deletion MSUScripter/MSUScripter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>MSUScripterIcon.ico</ApplicationIcon>
<PackageIcon>MSUScripterIcon.ico</PackageIcon>
<Version>5.0.0</Version>
<Version>5.0.1</Version>
<RuntimeFrameworkVersion>9.0.0</RuntimeFrameworkVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>12</LangVersion>
Expand Down
78 changes: 72 additions & 6 deletions MSUScripter/Services/ControlServices/MainWindowService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using AvaloniaControls.ControlServices;
Expand Down Expand Up @@ -254,12 +256,10 @@ private bool CleanDirectory(string path, TimeSpan? timeout = null)

if (newerGitHubRelease != null)
{
if (OperatingSystem.IsLinux())
{
return (newerGitHubRelease.Url,
newerGitHubRelease.Asset.FirstOrDefault(x => x.Url.ToLower().EndsWith(".appimage"))?.Url);
}
return (newerGitHubRelease.Url, null);
var downloadUrl = OperatingSystem.IsLinux()
? newerGitHubRelease.Asset.FirstOrDefault(x => x.Url.ToLower().EndsWith(".appimage"))?.Url
: newerGitHubRelease.Asset.FirstOrDefault(x => x.Url.ToLower().EndsWith(".exe"))?.Url;
return (newerGitHubRelease.Url, downloadUrl);
}

return null;
Expand Down Expand Up @@ -296,6 +296,72 @@ public void IgnoreFutureUpdates()
settingsService.SaveSettings();
}

public async Task<string?> InstallWindowsUpdate(string url)
{
var filename = Path.GetFileName(new Uri(url).AbsolutePath);
var localPath = Path.Combine(Path.GetTempPath(), filename);

logger.LogInformation("Downloading {Url} to {LocalPath}", url, localPath);

var response = await DownloadFileAsyncAttempt(url, localPath);

if (!response.Item1)
{
logger.LogInformation("Download failed: {Error}", response.Item2);
return response.Item2;
}

try
{
logger.LogInformation("Launching setup file");

var psi = new ProcessStartInfo
{
FileName = localPath,
UseShellExecute = true,
RedirectStandardOutput = false,
RedirectStandardError = false,
RedirectStandardInput = false,
CreateNoWindow = true
};

Process.Start(psi);
return null;
}
catch (Exception e)
{
logger.LogError(e, "Failed to start setup file");
return "Failed to start setup file";
}
}

private async Task<(bool, string?)> DownloadFileAsyncAttempt(string url, string target, int attemptNumber = 0, int totalAttempts = 3)
{

using var httpClient = new HttpClient();

try
{
await using var downloadStream = await httpClient.GetStreamAsync(url);
await using var fileStream = new FileStream(target, FileMode.Create);
await downloadStream.CopyToAsync(fileStream);
return (true, null);
}
catch (Exception ex)
{
logger.LogError(ex, "Download failed");
if (attemptNumber < totalAttempts)
{
await Task.Delay(TimeSpan.FromSeconds(attemptNumber));
return await DownloadFileAsyncAttempt(url, target, attemptNumber + 1, totalAttempts);
}
else
{
return (false, $"Download failed: {ex.Message}");
}
}
}

private async Task CleanUpFolders()
{
await ITaskService.Run(() =>
Expand Down
94 changes: 52 additions & 42 deletions MSUScripter/Services/DependencyInstallerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,63 +32,73 @@ public async Task<bool> InstallPyApp(Action<string> response, Func<string, strin
{
response.Invoke("Setting up directories");
var destination = Path.Combine(Directories.Dependencies, "python");

var pythonPath = OperatingSystem.IsWindows()
? Path.Combine(destination, "python.exe")
: Path.Combine(destination, "bin", "python3.13");
var doesHavePython = false;

if (Directory.Exists(destination))
if (File.Exists(pythonPath))
{
logger.LogInformation("Deleting prior Python installation");
try
var checkPyVersionResult = await runPyFunc(pythonPath, "--version");
doesHavePython = checkPyVersionResult.Success && checkPyVersionResult.Result.StartsWith("Python 3.13");
}

if (!doesHavePython)
{
if (Directory.Exists(destination))
{
await ITaskService.Run(() =>
logger.LogInformation("Deleting prior Python installation");
try
{
Directory.Delete(destination, true);
});
}
catch (TaskCanceledException)
{
// Do Nothing
await ITaskService.Run(() =>
{
Directory.Delete(destination, true);
});
}
catch (TaskCanceledException)
{
// Do Nothing
}
}
}

EnsureFolders(destination);

var tempFile = Path.Combine(Directories.TempFolder, "python.tar.gz");
var url = OperatingSystem.IsWindows() ? PythonWindowsDownloadUrl : PythonLinuxDownloadUrl;
EnsureFolders(destination);

response.Invoke("Downloading Python");
if (!await DownloadFileAsync(url, tempFile))
{
return false;
}
var tempFile = Path.Combine(Directories.TempFolder, "python.tar.gz");
var url = OperatingSystem.IsWindows() ? PythonWindowsDownloadUrl : PythonLinuxDownloadUrl;

response.Invoke("Extracting Python files");
if (!await ExtractTarGzFile(tempFile, Directories.Dependencies))
{
return false;
}
response.Invoke("Downloading Python");
if (!await DownloadFileAsync(url, tempFile))
{
return false;
}

var pythonPath = OperatingSystem.IsWindows()
? Path.Combine(destination, "python.exe")
: Path.Combine(destination, "bin", "python3.13");
response.Invoke("Extracting Python files");
if (!await ExtractTarGzFile(tempFile, Directories.Dependencies))
{
return false;
}

if (OperatingSystem.IsLinux())
{
File.SetUnixFileMode(pythonPath,
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
}
if (OperatingSystem.IsLinux())
{
File.SetUnixFileMode(pythonPath,
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
}

response.Invoke("Verifying Python version");
response.Invoke("Verifying Python version");

var runPyResult = await runPyFunc(pythonPath, "--version");
if (!runPyResult.Success || !runPyResult.Result.StartsWith("Python 3"))
{
logger.LogError("Python version response incorrect: {Response} | {Error}", runPyResult.Result,
runPyResult.Error);
return false;
var verifyPythonInstallResult = await runPyFunc(pythonPath, "--version");
if (!verifyPythonInstallResult.Success || !verifyPythonInstallResult.Result.StartsWith("Python 3.13"))
{
logger.LogError("Python version response incorrect: {Response} | {Error}", verifyPythonInstallResult.Result,
verifyPythonInstallResult.Error);
return false;
}
}

response.Invoke("Installing companion app");

runPyResult = await runPyFunc(pythonPath, "-m pip install py-msu-scripter-app");
var runPyResult = await runPyFunc(pythonPath, "-m pip install --upgrade py-msu-scripter-app");
if (!runPyResult.Success && !runPyResult.Error.StartsWith("[notice]"))
{
logger.LogError("Failed to install Python companion app: {Error}", runPyResult.Error);
Expand Down
14 changes: 11 additions & 3 deletions MSUScripter/Services/PythonCompanionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
using AvaloniaControls.Services;
using Microsoft.Extensions.Logging;
using MSUScripter.Models;
using MSUScripter.Tools;

namespace MSUScripter.Services;

public class PythonCompanionService(ILogger<PythonCompanionService> logger, YamlService yamlService, DependencyInstallerService dependencyInstallerService)
{
private const string BaseCommand = "py_msu_scripter_app";
private const string MinVersion = "v0.1.5";
private const string MinVersion = "v0.1.9";
private RunMethod _runMethod = RunMethod.Unknown;
private string? _pythonExecutablePath;
private string? _ffmpegPath;
Expand All @@ -34,7 +35,7 @@ public async Task<bool> VerifyInstalledAsync()
_pythonExecutablePath = null;
var response = await RunCommandAsync("--version");

IsValid = response.Success && response.Result.EndsWith(MinVersion) &&
IsValid = response.Success && VerifyVersionNumber(response.Result) &&
!response.Error.Contains("Couldn't find ffmpeg");

if (IsValid)
Expand All @@ -49,6 +50,13 @@ public async Task<bool> VerifyInstalledAsync()
return IsValid;
}

private bool VerifyVersionNumber(string versionString)
{
var minVersion = MinVersion.VersionStringToDecimal();
var currentVersion = versionString.Substring(versionString.IndexOf('v')).VersionStringToDecimal();
return currentVersion >= minVersion;
}

public async Task<bool> VerifyFfMpegAsync()
{
var ffmpegFolder = Path.Combine(Directories.Dependencies, "ffmpeg", "bin");
Expand Down Expand Up @@ -603,7 +611,7 @@ private async Task<RunPyResult> RunInternalAsync(string command, string argument
await process.WaitForExitAsync(cancellationToken ?? CancellationToken.None);

var resultText = "";
var errorText = "";
string errorText;

if (isPipInstall)
{
Expand Down
29 changes: 29 additions & 0 deletions MSUScripter/Tools/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,33 @@ public static int GetUnicodeLength(this string str)
{
return new StringInfo(str.CleanString()).LengthInTextElements;
}

public static decimal? VersionStringToDecimal(this string str)
{
if (!str.Contains('.'))
{
return null;
}

if (str.StartsWith('v'))
{
str = str[1..];
}

var parts = str.Split('.');
if (parts.Length <= 2)
{
return decimal.Parse(str);
}
else if (parts.Length == 3)
{
return decimal.Parse(parts[0]) * 1000 + decimal.Parse(parts[1]) + decimal.Parse(parts[2]) / 1000;
}
else if (parts.Length == 4)
{
return decimal.Parse(parts[0]) * 1000000 + decimal.Parse(parts[1]) * 1000 + decimal.Parse(parts[2]) + decimal.Parse(parts[3]) / 1000;
}

return null;
}
}
17 changes: 15 additions & 2 deletions MSUScripter/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ private async Task ShowDesktopFileWindow()

private async Task ShowNewReleaseWindow(string releaseUrl, string? downloadUrl)
{
if (_service == null)
{
return;
}

downloadUrl ??= "";
var hasDownloadUrl = !string.IsNullOrEmpty(downloadUrl);

Expand All @@ -151,7 +156,7 @@ private async Task ShowNewReleaseWindow(string releaseUrl, string? downloadUrl)

if (messageWindow.DialogResult.CheckedBox)
{
_service?.IgnoreFutureUpdates();
_service.IgnoreFutureUpdates();
}

if (messageWindow.DialogResult.PressedAcceptButton)
Expand All @@ -178,7 +183,15 @@ private async Task ShowNewReleaseWindow(string releaseUrl, string? downloadUrl)
}
else
{
throw new InvalidOperationException("Not supported on Windows");
var result = await _service.InstallWindowsUpdate(downloadUrl);
if (!string.IsNullOrEmpty(result))
{
await MessageWindow.ShowErrorDialog(result);
}
else
{
Close();;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion PyMsuScripterApp/py_msu_scripter_app/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def cli():
print("Error: the input YAML file was not found")
exit(1)

with open(args.input, "r") as stream:
with open(args.input, "r", encoding="utf-8") as stream:
try:
yaml_file = yaml.safe_load(stream)
except yaml.YAMLError as exc:
Expand Down
Loading