4 Commits
Author SHA1 Message Date
kirieska 3413e78166 Обновить README.md 2026-09-14 22:49:16 +02:00
kirieska fc91250663 финал 2026-09-14 22:34:33 +02:00
kirieska 45e8e4873a правки под mac 2026-09-14 21:12:08 +02:00
kirieska 30f9e3d1e7 Обновить SpotXInstaller.csproj 2026-09-14 21:10:27 +02:00
7 changed files with 288 additions and 65 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

+1
View File
@@ -0,0 +1 @@
СПОТИФАЙ БЕЗ РЕКЛАМЫ В РЕЛИЗАХ
+36 -57
View File
@@ -145,11 +145,21 @@ public sealed class InstallerService
if (!isUninstall) if (!isUninstall)
{ {
// IMPORTANT:
// SpotX asks interactively about these options when
// neither switch is supplied. Since our GUI has no
// interactive PowerShell input, always provide an
// explicit answer.
if (options.BlockUpdates) if (options.BlockUpdates)
arguments.Add("-block_update_on"); arguments.Add("-block_update_on");
else
arguments.Add("-block_update_off");
if (options.HidePodcasts) if (options.HidePodcasts)
arguments.Add("-podcasts_off"); arguments.Add("-podcasts_off");
else
arguments.Add("-podcasts_on");
if (options.HidePremium) if (options.HidePremium)
arguments.Add("-adsections_off"); arguments.Add("-adsections_off");
@@ -205,8 +215,6 @@ public sealed class InstallerService
string lower = line.ToLowerInvariant(); string lower = line.ToLowerInvariant();
// Никакого фальшивого +2%.
// Только изменение текста статуса по реальному выводу.
if (lower.Contains("download") || if (lower.Contains("download") ||
lower.Contains("скач")) lower.Contains("скач"))
{ {
@@ -243,16 +251,20 @@ public sealed class InstallerService
if (exitCode == 0) if (exitCode == 0)
{ {
progress.Report( progress.Report(
(100, (
100,
isUninstall isUninstall
? "Восстановление завершено" ? "Восстановление завершено"
: "Установка завершена")); : "Установка завершена"
));
} }
else else
{ {
progress.Report( progress.Report(
(100, (
$"Ошибка установки (код {exitCode})")); 100,
$"Ошибка установки (код {exitCode})"
));
} }
return exitCode; return exitCode;
@@ -277,17 +289,11 @@ public sealed class InstallerService
Action<string> onLine, Action<string> onLine,
CancellationToken ct) CancellationToken ct)
{ {
var tcs =
new TaskCompletionSource<int>(
TaskCreationOptions.RunContinuationsAsynchronously);
var psi = new ProcessStartInfo var psi = new ProcessStartInfo
{ {
FileName = file, FileName = file,
RedirectStandardOutput = true, RedirectStandardOutput = true,
RedirectStandardError = true, RedirectStandardError = true,
UseShellExecute = false, UseShellExecute = false,
CreateNoWindow = true CreateNoWindow = true
}; };
@@ -295,14 +301,11 @@ public sealed class InstallerService
foreach (string argument in arguments) foreach (string argument in arguments)
psi.ArgumentList.Add(argument); psi.ArgumentList.Add(argument);
var process = new Process using var process = new Process
{ {
StartInfo = psi, StartInfo = psi
EnableRaisingEvents = true
}; };
int finished = 0;
process.OutputDataReceived += (_, e) => process.OutputDataReceived += (_, e) =>
{ {
if (e.Data is not null) if (e.Data is not null)
@@ -315,31 +318,26 @@ public sealed class InstallerService
onLine(e.Data); onLine(e.Data);
}; };
process.Exited += (_, _) => ct.ThrowIfCancellationRequested();
if (!process.Start())
{ {
if (Interlocked.Exchange(ref finished, 1) != 0) throw new InvalidOperationException(
return; $"Не удалось запустить процесс: {file}");
}
process.BeginOutputReadLine();
process.BeginErrorReadLine();
try try
{ {
tcs.TrySetResult(process.ExitCode); await process.WaitForExitAsync(ct)
} .ConfigureAwait(false);
catch
{
tcs.TrySetResult(-1);
}
finally
{
process.Dispose();
}
};
using CancellationTokenRegistration registration = return process.ExitCode;
ct.Register(() => }
catch (OperationCanceledException)
{ {
if (Interlocked.Exchange(ref finished, 1) != 0)
return;
try try
{ {
if (!process.HasExited) if (!process.HasExited)
@@ -353,28 +351,10 @@ public sealed class InstallerService
// Процесс уже мог завершиться. // Процесс уже мог завершиться.
} }
tcs.TrySetCanceled(ct);
});
try try
{ {
ct.ThrowIfCancellationRequested(); await process.WaitForExitAsync()
.ConfigureAwait(false);
if (!process.Start())
throw new InvalidOperationException(
$"Не удалось запустить процесс: {file}");
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return await tcs.Task.ConfigureAwait(false);
}
catch
{
try
{
if (!process.HasExited)
process.Kill(entireProcessTree: true);
} }
catch catch
{ {
@@ -385,4 +365,3 @@ public sealed class InstallerService
} }
} }
} }
+7 -7
View File
@@ -26,7 +26,6 @@
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.1.2" /> <PackageReference Include="Avalonia.Themes.Fluent" Version="12.1.2" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="12.1.2" /> <PackageReference Include="Avalonia.Fonts.Inter" Version="12.1.2" />
<!-- Native Skia assets -->
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="3.119.4" /> <PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="3.119.4" />
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="3.119.4" /> <PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="3.119.4" />
@@ -38,26 +37,27 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
</ItemGroup> </ItemGroup>
<!-- Windows x64 --> <!-- Windows -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-x64'"> <PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-x64'">
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup> </PropertyGroup>
<!-- Linux x64 --> <!-- Linux -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'"> <PropertyGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract> <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup> </PropertyGroup>
<!-- macOS Intel --> <!-- macOS:
НЕ включаем PublishSingleFile.
Avalonia native libraries должны находиться
внутри .app bundle. -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'osx-x64'"> <PropertyGroup Condition="'$(RuntimeIdentifier)' == 'osx-x64'">
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>false</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env bash
set -e
APP_NAME="SpotXInstaller"
RID="linux-x64"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PUBLISH="$ROOT/bin/Release/net10.0/$RID/publish"
APPDIR="$ROOT/bin/Release/net10.0/$RID/AppDir"
OUTPUT="$ROOT/bin/Release/net10.0/$APP_NAME-x86_64.AppImage"
ICON="$ROOT/Assets/icons/SpotXInstaller.png"
echo
echo "========================================"
echo " Building SpotXInstaller AppImage"
echo "========================================"
echo
echo "[1/5] Cleaning..."
rm -rf "$PUBLISH"
rm -rf "$APPDIR"
rm -f "$OUTPUT"
echo
echo "[2/5] Publishing Linux application..."
dotnet publish "$ROOT/SpotXInstaller.csproj" \
-c Release \
-r "$RID" \
--self-contained true \
-p:PublishSingleFile=false \
-p:IncludeNativeLibrariesForSelfExtract=false \
-o "$PUBLISH"
echo
echo "[3/5] Creating AppDir..."
mkdir -p "$APPDIR/usr/bin"
mkdir -p "$APPDIR/usr/lib"
mkdir -p "$APPDIR/usr/share/applications"
cp -R "$PUBLISH/." "$APPDIR/usr/bin/"
echo
echo "[4/5] Creating AppImage files..."
# Проверяем наличие иконки
if [ ! -f "$ICON" ]; then
echo
echo "ERROR: Icon not found:"
echo "$ICON"
echo
exit 1
fi
# Иконка должна находиться в корне AppDir
cp "$ICON" "$APPDIR/SpotXInstaller.png"
# Проверяем, что иконка действительно скопирована
if [ ! -f "$APPDIR/SpotXInstaller.png" ]; then
echo
echo "ERROR: Failed to copy icon."
echo
exit 1
fi
cat > "$APPDIR/AppRun" <<'EOF'
#!/usr/bin/env bash
HERE="$(dirname "$(readlink -f "$0")")"
export LD_LIBRARY_PATH="$HERE/usr/bin:$HERE/usr/lib:${LD_LIBRARY_PATH:-}"
exec "$HERE/usr/bin/SpotXInstaller" "$@"
EOF
chmod +x "$APPDIR/AppRun"
cat > "$APPDIR/SpotXInstaller.desktop" <<'EOF'
[Desktop Entry]
Name=SpotX Installer
Comment=SpotX Spotify Installer
Exec=SpotXInstaller
Icon=SpotXInstaller
Terminal=false
Type=Application
Categories=Utility;
StartupNotify=true
EOF
cp "$APPDIR/SpotXInstaller.desktop" \
"$APPDIR/usr/share/applications/SpotXInstaller.desktop"
echo
echo "AppDir contents:"
echo " AppRun: $APPDIR/AppRun"
echo " Icon: $APPDIR/SpotXInstaller.png"
echo " Desktop: $APPDIR/SpotXInstaller.desktop"
echo
ls -lh "$APPDIR/SpotXInstaller.png"
echo
echo "[5/5] Building AppImage..."
appimagetool "$APPDIR" "$OUTPUT"
echo
echo "========================================"
echo " BUILD COMPLETE"
echo "========================================"
echo
echo "AppImage:"
echo "$OUTPUT"
echo
ls -lh "$OUTPUT"
echo
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -e
APP_NAME="SpotXInstaller"
RID="osx-x64"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUTPUT_DIR="$ROOT/bin/Release/net10.0/$RID/publish"
ZIP_DIR="$ROOT/bin/Release/net10.0/$RID"
ZIP_FILE="$ZIP_DIR/$APP_NAME-macOS-x64.zip"
echo
echo "========================================"
echo " Building SpotXInstaller macOS"
echo "========================================"
echo
echo "[1/4] Cleaning..."
rm -rf "$OUTPUT_DIR"
rm -f "$ZIP_FILE"
mkdir -p "$ZIP_DIR"
echo
echo "[2/4] Publishing macOS Intel..."
dotnet publish "$ROOT/SpotXInstaller.csproj" \
-c Release \
-r "$RID" \
--self-contained true \
-p:PublishSingleFile=false \
-p:IncludeNativeLibrariesForSelfExtract=false \
-p:DebugType=None \
-p:DebugSymbols=false \
-o "$OUTPUT_DIR"
echo
echo "[3/4] Creating ZIP..."
cd "$OUTPUT_DIR"
zip -r "$ZIP_FILE" . \
-x "*.pdb"
cd "$ROOT"
echo
echo "[4/4] Checking output..."
if [ ! -f "$ZIP_FILE" ]; then
echo
echo "ERROR: macOS ZIP was not created."
exit 1
fi
echo
echo "========================================"
echo " MACOS BUILD COMPLETE"
echo "========================================"
echo
echo "ZIP:"
echo "$ZIP_FILE"
echo
ls -lh "$ZIP_FILE"
echo
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -e
APP_NAME="SpotXInstaller"
RID="win-x64"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUTPUT_DIR="$ROOT/bin/Release/net10.0/$RID/publish"
OUTPUT_EXE="$OUTPUT_DIR/$APP_NAME.exe"
echo
echo "========================================"
echo " Building SpotXInstaller Windows"
echo "========================================"
echo
echo "[1/3] Cleaning..."
rm -rf "$OUTPUT_DIR"
echo
echo "[2/3] Publishing Windows x64..."
dotnet publish "$ROOT/SpotXInstaller.csproj" \
-c Release \
-r "$RID" \
--self-contained true \
-p:PublishSingleFile=true \
-p:IncludeNativeLibrariesForSelfExtract=true \
-p:DebugType=None \
-p:DebugSymbols=false \
-o "$OUTPUT_DIR"
echo
echo "[3/3] Checking output..."
if [ ! -f "$OUTPUT_EXE" ]; then
echo
echo "ERROR: Windows executable was not created."
exit 1
fi
echo
echo "========================================"
echo " WINDOWS BUILD COMPLETE"
echo "========================================"
echo
echo "EXE:"
echo "$OUTPUT_EXE"
echo
ls -lh "$OUTPUT_EXE"
echo