сырой проект

This commit is contained in:
2026-09-14 20:24:13 +02:00
commit 81b806cfed
14 changed files with 953 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using SpotXInstaller.Models;
namespace SpotXInstaller.Services;
public sealed class InstallerService
{
private const string BashUrl = "https://api.kirieska.xyz/files/SPOTX/spotx.sh";
private const string PsUrl = "https://api.kirieska.xyz/files/SPOTX/run.ps1";
private const string PsUninstallUrl = "https://api.kirieska.xyz/files/SPOTX/uninstall.ps1";
public Task<int> InstallAsync(InstallOptions o,
IProgress<(int Percent, string Status)> progress,
IProgress<string> log, CancellationToken ct = default)
=> RunAsync(o, isUninstall: false, progress, log, ct);
public Task<int> UninstallAsync(
IProgress<(int Percent, string Status)> progress,
IProgress<string> log, CancellationToken ct = default)
=> RunAsync(new InstallOptions(false, false, false, false),
isUninstall: true, progress, log, ct);
private async Task<int> RunAsync(InstallOptions o, bool isUninstall,
IProgress<(int, string)> progress, IProgress<string> log, CancellationToken ct)
{
bool isWin = OperatingSystem.IsWindows();
string url = isUninstall
? (isWin ? PsUninstallUrl : BashUrl)
: (isWin ? PsUrl : BashUrl);
string ext = isWin ? ".ps1" : ".sh";
string scriptPath = Path.Combine(Path.GetTempPath(), $"spotx_{Guid.NewGuid():N}{ext}");
try
{
// === ШАГ 1 (0–20%): скачивание ===
progress.Report((3, "Загрузка скрипта..."));
using (var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) })
{
http.DefaultRequestHeaders.UserAgent.ParseAdd("SpotXInstaller/1.0");
byte[] data = await http.GetByteArrayAsync(url, ct).ConfigureAwait(false);
await File.WriteAllBytesAsync(scriptPath, data, ct).ConfigureAwait(false);
}
progress.Report((20, "Скрипт загружен"));
if (!isWin)
{
progress.Report((25, "Установка прав на исполнение..."));
await ExecAsync("/bin/chmod", new[] { "+x", scriptPath }, _ => { }, ct)
.ConfigureAwait(false);
}
progress.Report((30, isUninstall ? "Восстановление..." : "Запуск установки..."));
// === ШАГ 2: маппинг флагов ===
string file;
var argv = new List<string>();
if (isWin)
{
file = "powershell.exe";
argv.AddRange(new[]
{
"-NoProfile", "-ExecutionPolicy", "Bypass",
"-File", scriptPath
});
if (isUninstall)
{
// uninstall.ps1 не принимает флагов — просто отрабатывает
}
else
{
if (o.BlockUpdates) argv.Add("-block_update_on");
if (o.HidePodcasts) argv.Add("-podcasts_off");
if (o.HidePremium) argv.Add("-adsections_off");
argv.Add("-no_pause"); // не ждать Enter в конце
}
}
else
{
file = "/bin/bash";
argv.Add(scriptPath);
if (isUninstall)
{
argv.Add("--uninstall");
}
else
{
if (o.BlockUpdates) argv.Add("-B");
if (o.HidePodcasts) argv.Add("-h");
if (o.HidePremium) argv.Add("-e");
if (o.ClearCache) argv.Add("-c");
}
}
// === ШАГ 3 (30–95%): запуск + построчный стрим ===
int cur = 30;
var gate = new object();
int exit = await ExecAsync(file, argv.ToArray(), line =>
{
log.Report(line);
lock (gate)
{
cur = Math.Min(cur + 2, 95);
progress.Report((cur, isUninstall ? "Восстановление..." : "Установка..."));
}
}, ct).ConfigureAwait(false);
progress.Report((100, "Готово"));
return exit;
}
finally
{
try { if (File.Exists(scriptPath)) File.Delete(scriptPath); } catch { }
}
}
private static Task<int> ExecAsync(string file, string[] argv,
Action<string> onLine, CancellationToken ct)
{
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var psi = new ProcessStartInfo
{
FileName = file,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
foreach (var a in argv) psi.ArgumentList.Add(a);
var proc = new Process { StartInfo = psi, EnableRaisingEvents = true };
proc.OutputDataReceived += (_, e) => { if (e.Data is { } s) onLine(s); };
proc.ErrorDataReceived += (_, e) => { if (e.Data is { } s) onLine(s); };
proc.Exited += (_, _) =>
{
try { tcs.TrySetResult(proc.ExitCode); }
finally { proc.Dispose(); }
};
using var reg = ct.Register(() =>
{
try { if (!proc.HasExited) proc.Kill(entireProcessTree: true); }
catch { }
});
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
return tcs.Task;
}
}