diff --git a/README.en.md b/README.en.md index c1c056af5..73d9c023a 100644 --- a/README.en.md +++ b/README.en.md @@ -160,6 +160,7 @@ Read what we [milestones](https://github.com/SteamTools-Team/SteamTools/mileston * [ZXing.Net](https://github.com/micjahn/ZXing.Net) * [ZXing.Net.Mobile](https://github.com/Redth/ZXing.Net.Mobile) * [QRCoder](https://github.com/codebude/QRCoder) +* [SharpCompress](https://github.com/adamhathcock/sharpcompress) * [React](https://github.com/facebook/react) * [Ant Design](https://github.com/ant-design/ant-design) * [Ant Design Blazor](https://github.com/ant-design-blazor/ant-design-blazor) diff --git a/README.md b/README.md index d265280b1..db6708a5f 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ * [ZXing.Net](https://github.com/micjahn/ZXing.Net) * [ZXing.Net.Mobile](https://github.com/Redth/ZXing.Net.Mobile) * [QRCoder](https://github.com/codebude/QRCoder) +* [SharpCompress](https://github.com/adamhathcock/sharpcompress) * [React](https://github.com/facebook/react) * [Ant Design](https://github.com/ant-design/ant-design) * [Ant Design Blazor](https://github.com/ant-design-blazor/ant-design-blazor) diff --git a/release-template.md b/release-template.md index faa3f17e0..ff6c7bae9 100644 --- a/release-template.md +++ b/release-template.md @@ -17,7 +17,8 @@ 17. 改进 开机自启动现仅对当前用户生效 18. 改进 程序根目录结构,相较于上一个版本移除了 Bin 文件夹,减少 dll 文件数量 19. 改进 优化了发行版本压缩包体积(移除了 CEF 模块,对于需要使用第三方账号快速登录功能可使用上一个版本的压缩包中 \Bin\CEF 将这个文件夹拷贝到 \CEF 中) -20. Android/macOS/Linux 版现已开启 Beta/Alpha 测试,可从 GitHub/Gitee 上下载 +20. 改进 现已编译为 ReadyToRun (R2R) 格式,这将改进应用程序的启动时间和延迟(仅 Windows 版本) +21. Android/macOS/Linux 版现已开启 Beta/Alpha 测试,可从 GitHub/Gitee 上下载 *** diff --git a/src/Common.CoreLib/Properties/AssemblyInfo.cs b/src/Common.CoreLib/Properties/AssemblyInfo.cs index b7f44b95f..be4fe0a0e 100644 --- a/src/Common.CoreLib/Properties/AssemblyInfo.cs +++ b/src/Common.CoreLib/Properties/AssemblyInfo.cs @@ -2,7 +2,9 @@ using System.Reflection; using System.Runtime.CompilerServices; using static System.Properties.ThisAssembly; +#pragma warning disable SYSLIB0025 // 类型或成员已过时 [assembly: SuppressIldasm] +#pragma warning restore SYSLIB0025 // 类型或成员已过时 [assembly: AssemblyTitle(AssemblyTrademark)] [assembly: AssemblyTrademark(AssemblyTrademark)] [assembly: AssemblyDescription(AssemblyDescription)] diff --git a/src/ST.Client.Desktop.Avalonia.App/Properties/PublishProfiles/win-x64.aot.pubxml b/src/ST.Client.Desktop.Avalonia.App/Properties/PublishProfiles/win-x64.aot.pubxml index 1874ca008..7ef04c93d 100644 --- a/src/ST.Client.Desktop.Avalonia.App/Properties/PublishProfiles/win-x64.aot.pubxml +++ b/src/ST.Client.Desktop.Avalonia.App/Properties/PublishProfiles/win-x64.aot.pubxml @@ -8,15 +8,13 @@ https://go.microsoft.com/fwlink/?LinkID=208121. Release bin\Release\Publish\win-x64 FileSystem - true win7-x64 - true - false - false Any CPU net6.0 - false + true + true false false + true diff --git a/src/ST.Client.Desktop/ST.Client.Desktop.csproj b/src/ST.Client.Desktop/ST.Client.Desktop.csproj index 0af39d961..8c7d7bddc 100644 --- a/src/ST.Client.Desktop/ST.Client.Desktop.csproj +++ b/src/ST.Client.Desktop/ST.Client.Desktop.csproj @@ -38,6 +38,7 @@ + diff --git a/src/ST.Client.Desktop/Services/Implementation/DesktopAppUpdateServiceImpl.cs b/src/ST.Client.Desktop/Services/Implementation/DesktopAppUpdateServiceImpl.cs index e19835417..722ca5b7a 100644 --- a/src/ST.Client.Desktop/Services/Implementation/DesktopAppUpdateServiceImpl.cs +++ b/src/ST.Client.Desktop/Services/Implementation/DesktopAppUpdateServiceImpl.cs @@ -23,7 +23,7 @@ namespace System.Application.Services.Implementation /// protected abstract void OnExit(); - protected override void OverwriteUpgrade(string value, bool isIncrement) + protected override void OverwriteUpgrade(string value, bool isIncrement, AppDownloadType downloadType) { if (isIncrement) // 增量更新 { @@ -38,7 +38,19 @@ namespace System.Application.Services.Implementation Directory.Delete(dirPath, true); } - if (TarGZipHelper.Unpack(value, dirPath, progress: new Progress(OnReportDecompressing), maxProgress: MaxProgressValue)) + var isOK = downloadType switch + { + AppDownloadType.Compressed_GZip + => TarGZipHelper.Unpack(value, dirPath, + progress: new Progress(OnReportDecompressing), + maxProgress: MaxProgressValue), + AppDownloadType.Compressed_7z + => SevenZipHelper.Unpack(value, dirPath, + progress: new Progress(OnReportDecompressing), + maxProgress: MaxProgressValue), + _ => throw new NotSupportedException(), + }; + if (isOK) { OnReport(MaxProgressValue); IOPath.FileTryDelete(value); diff --git a/src/ST.Client.Desktop/SevenZipHelper.cs b/src/ST.Client.Desktop/SevenZipHelper.cs new file mode 100644 index 000000000..eccaf3c28 --- /dev/null +++ b/src/ST.Client.Desktop/SevenZipHelper.cs @@ -0,0 +1,80 @@ +using SharpCompress.Archives; +using SharpCompress.Archives.SevenZip; +using SharpCompress.Common; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Application +{ + public static class SevenZipHelper + { + /// + /// 解压压缩包文件 + /// + /// 要解压的压缩包文件路径 + /// 要解压的文件夹路径,文件夹必须不存在 + /// 进度值监听 + /// + /// + public static bool Unpack(string filePath, string dirPath, + IProgress? progress = null, + float maxProgress = 100f) + { + if (!File.Exists(filePath)) return false; + if (Directory.Exists(dirPath)) return false; + + using var fs = File.OpenRead(filePath); + float length = fs.Length; + var isFinish = false; + CancellationTokenSource? cts = null; + async void ProgressMonitor() + { + try + { + while (!isFinish) + { + var value = fs.Position / length * maxProgress; + progress!.Report(value); + await Task.Delay(200, cts.Token); + } + } + catch (OperationCanceledException) + { + } + } + Directory.CreateDirectory(dirPath); + using var archive = SevenZipArchive.Open(fs); + var hasProgress = progress != null; + if (hasProgress) + { + cts = new(); + try + { + Task.Factory.StartNew(ProgressMonitor, cts.Token); + } + catch (OperationCanceledException) + { + } + } + var files = archive.Entries.Where(entry => !entry.IsDirectory).ToArray(); + for (int i = 0; i < files.Length; i++) + { + var file = files[i]; + file.WriteToDirectory(dirPath, new ExtractionOptions() + { + ExtractFullPath = true, + Overwrite = true + }); + } + isFinish = true; + if (hasProgress) + { + cts!.Cancel(); + progress!.Report(maxProgress); + } + return true; + } + } +} \ No newline at end of file diff --git a/src/ST.Client.Desktop/TarGZipHelper.cs b/src/ST.Client.Desktop/TarGZipHelper.cs index c64d2f2cc..cc43a00d5 100644 --- a/src/ST.Client.Desktop/TarGZipHelper.cs +++ b/src/ST.Client.Desktop/TarGZipHelper.cs @@ -1,8 +1,9 @@ -using ICSharpCode.SharpZipLib.GZip; +using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip.Compression; using System.IO; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace System.Application @@ -75,23 +76,45 @@ namespace System.Application // https://github.com/icsharpcode/SharpZipLib/blob/v1.3.1/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs#L542 float length = fs.Length; var isFinish = false; + CancellationTokenSource? cts = null; async void ProgressMonitor() { - while (!isFinish) + try + { + while (!isFinish) + { + var value = fs.Position / length * maxProgress; + progress!.Report(value); + await Task.Delay(200, cts.Token); + } + } + catch (OperationCanceledException) { - var value = fs.Position / length * maxProgress; - progress.Report(value); - await Task.Delay(200); } } Directory.CreateDirectory(dirPath); using var s = new GZipInputStream(fs); using var archive = TarArchive.CreateInputTarArchive(s, Encoding.UTF8); if (progressMessage != null) archive.ProgressMessageEvent += progressMessage; - if (progress != null) Task.Factory.StartNew(ProgressMonitor); + var hasProgress = progress != null; + if (hasProgress) + { + cts = new(); + try + { + Task.Factory.StartNew(ProgressMonitor, cts.Token); + } + catch (OperationCanceledException) + { + } + } archive.ExtractContents(dirPath); isFinish = true; - progress?.Report(maxProgress); + if (hasProgress) + { + cts!.Cancel(); + progress!.Report(maxProgress); + } return true; } } diff --git a/src/ST.Client.Desktop/UI/Resx/AppResources.Designer.cs b/src/ST.Client.Desktop/UI/Resx/AppResources.Designer.cs index 09bac85d1..3cb4d1cc3 100644 --- a/src/ST.Client.Desktop/UI/Resx/AppResources.Designer.cs +++ b/src/ST.Client.Desktop/UI/Resx/AppResources.Designer.cs @@ -3151,7 +3151,7 @@ namespace System.Application.UI.Resx { } /// - /// 查找类似 *需重启生效 的本地化字符串。 + /// 查找类似 *需重启软件后生效 的本地化字符串。 /// public static string Settings_RestartEffective { get { diff --git a/src/ST.Client.Desktop/UI/Resx/AppResources.resx b/src/ST.Client.Desktop/UI/Resx/AppResources.resx index 1b5d2ecef..47645350c 100644 --- a/src/ST.Client.Desktop/UI/Resx/AppResources.resx +++ b/src/ST.Client.Desktop/UI/Resx/AppResources.resx @@ -1488,7 +1488,7 @@ 使用硬件加速 - *需重启生效 + *需重启软件后生效 下载更新渠道 @@ -1502,4 +1502,4 @@ 浏览基目录 - \ No newline at end of file + diff --git a/src/ST.Client.Desktop/UI/Resx/R.cs b/src/ST.Client.Desktop/UI/Resx/R.cs index 47905fd82..d11832163 100644 --- a/src/ST.Client.Desktop/UI/Resx/R.cs +++ b/src/ST.Client.Desktop/UI/Resx/R.cs @@ -4,6 +4,7 @@ using System.Application.UI.ViewModels; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Properties; using System.Reactive.Linq; using AppRes = System.Application.UI.Resx.AppResources; #if __MOBILE__ @@ -30,7 +31,7 @@ namespace System.Application.UI.Resx static R() { DefaultCurrentUICulture = CultureInfo.CurrentUICulture; - Languages = new Dictionary(StringComparer.OrdinalIgnoreCase) + var languagesDict = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "", "Auto" }, { "zh-Hans", "Chinese(Simplified)" }, @@ -39,9 +40,17 @@ namespace System.Application.UI.Resx { "ko", "Koreana" }, { "ja", "Japanese" }, { "ru", "Russian" }, - //{ "es", "Spanish" }, - //{ "it", "Italian" }, - }.ToList(); + { "es", "Spanish" }, + { "it", "Italian" }, + }; +#if !DEBUG + if (new Version(ThisAssembly.Version) < new Version(2, 5)) + { + languagesDict.Remove("es"); + languagesDict.Remove("it"); + } +#endif + Languages = languagesDict.ToList(); SteamLanguages = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "zh-CN", "schinese" }, diff --git a/src/ST.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs b/src/ST.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs index ee519eea8..df21363a9 100644 --- a/src/ST.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs +++ b/src/ST.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs @@ -75,7 +75,19 @@ namespace System.Application.UI.ViewModels AddTabItem(); AddTabItem(); AddTabItem(); - AddTabItem(); + + var isVersion_2_5_OR_GREATER = +#if DEBUG + true; +#else + new Version(ThisAssembly.Version) < new Version(2, 5); +#endif + + if (isVersion_2_5_OR_GREATER) + { + AddTabItem(); + } + //AddTabItem(); if (DI.Platform == Platform.Windows) AddTabItem(); diff --git a/src/ST.Client/Resources/OpenSourceLibraryList.mpo b/src/ST.Client/Resources/OpenSourceLibraryList.mpo index eff811bad..d1ef1514b 100644 Binary files a/src/ST.Client/Resources/OpenSourceLibraryList.mpo and b/src/ST.Client/Resources/OpenSourceLibraryList.mpo differ diff --git a/src/ST.Client/Services/Implementation/AppUpdateServiceImpl.cs b/src/ST.Client/Services/Implementation/AppUpdateServiceImpl.cs index 7b61eefed..8226e1c4a 100644 --- a/src/ST.Client/Services/Implementation/AppUpdateServiceImpl.cs +++ b/src/ST.Client/Services/Implementation/AppUpdateServiceImpl.cs @@ -2,8 +2,8 @@ using Microsoft.Extensions.Options; using ReactiveUI; using System.Application.Models; using System.Application.Properties; -using System.IO; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -254,7 +254,15 @@ namespace System.Application.Services.Implementation goto end; } - if (newVersionInfo.CurrentAllFiles.Any_Nullable() && + var isAndroid = DI.Platform == Platform.Android; + var isDesktop = DI.Platform == Platform.Windows || DI.Platform == Platform.Linux || DI.IsmacOS; + if (!isAndroid && !isDesktop) + { + OpenInAppStore(); + goto end; + } + + if (!isAndroid && newVersionInfo.CurrentAllFiles.Any_Nullable() && newVersionInfo.CurrentAllFiles.All(x => x.HasValue()) && newVersionInfo.AllFiles.Any_Nullable() && newVersionInfo.AllFiles.All(x => x.HasValue())) // 增量更新 v2 @@ -385,96 +393,28 @@ namespace System.Application.Services.Implementation OnReport(MaxProgressValue); OverwriteUpgrade(packDirPath, isIncrement: true); } - //if (newVersionInfo!.IncrementalUpdate.Any_Nullable()) // 增量更新 v1 - //{ - // if (Path.DirectorySeparatorChar != '\\') - // { - // foreach (var item in newVersionInfo.IncrementalUpdate!) - // { - // item.FileRelativePath = - // item.FileRelativePath?.Replace('\\', Path.DirectorySeparatorChar); - // } - // } - - // var packDirName = GetPackName(newVersionInfo, isDirOrFile: false); - // var packDirPath = Path.Combine(GetPackCacheDirPath(!isSupportedResume), packDirName); - - // var incrementalUpdate = newVersionInfo.IncrementalUpdate.ToDictionary(x => x, x => packDirPath + Path.DirectorySeparatorChar + x.FileRelativePath); - - // if (Directory.Exists(packDirPath)) - // { - // foreach (var item in newVersionInfo.IncrementalUpdate!) - // { - // var filePath = incrementalUpdate[item]; - // var fileInfo = new FileInfo(filePath); - // if (fileInfo.Exists) - // { - // if (fileInfo.Length == item.Length) - // { - // using var fileStream = fileInfo.OpenRead(); - // var sha256 = Hashs.String.SHA256(fileStream); - // if (sha256 == item.SHA256) - // { - // incrementalUpdate.Remove(item); - // continue; - // } - // } - // fileInfo.Delete(); - // } - // } - // } - - // int i = 1; - // void OnReportDownloading3_(float value) => OnReportDownloading3(value, i, incrementalUpdate.Count); - // OnReportDownloading3_(0f); - // foreach (var item in incrementalUpdate) - // { - // var fileName = item.Value; - // var cacheFileName = fileName + FileExDownloadCache; - // var requestUri = GetSingleFileUrl(item.Key.FileId!); - - // var rsp = await client.Download( - // isAnonymous: true, - // requestUri: requestUri, - // cacheFileName, - // null); - // if (rsp.IsSuccess) - // { - // File.Move(cacheFileName, fileName); - - // if (!UpdatePackVerification(fileName, item.Key.SHA256!, i, incrementalUpdate.Count)) - // { - // Fail(SR.UpdatePackVerificationFail); - // break; - // } - // } - // else - // { - // Fail(SR.DownloadUpdateFail); - // break; - // } - // i++; - // OnReportDownloading3_(i / (float)incrementalUpdate.Count * MaxProgressValue); - // } - - // OnReport(MaxProgressValue); - // OverwriteUpgrade(packDirPath, isIncrement: true); - //} else // 全量更新 { - if (DI.Platform != Platform.Android) // 全量更新预计仅支持 Android 平台 + AppVersionDTO.Download? download = null; + if (newVersionInfo.Downloads != null) { - OpenInAppStore(); - goto end; + if (isAndroid) + { + download = newVersionInfo.Downloads.FirstOrDefault(x => x.DownloadType == AppDownloadType.Install); + } + else if (isDesktop) + { + download = newVersionInfo.Downloads.FirstOrDefault(x => x.DownloadType == AppDownloadType.Compressed_7z); + if (download == null) + { + download = newVersionInfo.Downloads.FirstOrDefault(x => x.DownloadType == AppDownloadType.Compressed_GZip); + } + } + else + { + throw new PlatformNotSupportedException(); + } } - - var downloadType = DI.Platform switch - { - Platform.Windows or Platform.Linux or Platform.Apple => AppDownloadType.Compressed, - Platform.Android => AppDownloadType.Install, - _ => throw new PlatformNotSupportedException(), - }; - var download = newVersionInfo.Downloads?.FirstOrDefault(x => x.DownloadType == downloadType); if (download.HasValue()) // 压缩包格式是否正确 { var packFileName = GetPackName(newVersionInfo, isDirOrFile: true); @@ -492,22 +432,23 @@ namespace System.Application.Services.Implementation } } - string fileEx; string filePlatform; string fileArch; - switch (newVersionInfo.Platform) + var fileEx = newVersionInfo.Platform switch { - case Platform.Android: - fileEx = FileEx.APK; - break; - case Platform.Windows: - case Platform.Linux: - case Platform.Apple: - fileEx = FileEx.TAR_GZ; - break; - default: - Fail(SR.UpdateEnumOutOfRange); - goto end; + Platform.Android => FileEx.APK, + Platform.Windows or Platform.Linux or Platform.Apple => download!.DownloadType switch + { + AppDownloadType.Compressed_GZip => FileEx.TAR_GZ, + AppDownloadType.Compressed_7z => FileEx._7Z, + _ => string.Empty, + }, + _ => string.Empty, + }; + if (fileEx == string.Empty) + { + Fail(SR.UpdateEnumOutOfRange); + goto end; } switch (newVersionInfo.Platform) { @@ -535,16 +476,32 @@ namespace System.Application.Services.Implementation case ArchitectureFlags.Arm64: fileArch = "arm64"; break; + case ArchitectureFlags.Arm: + fileArch = "arm"; + break; default: fileArch = ((int)newVersionInfo.SupportedAbis).ToString(); break; case 0: - case ArchitectureFlags.Arm: Fail(SR.UpdateEnumOutOfRange); goto end; } - var downloadFileName = $"{filePlatform}-{fileArch}_{newVersionInfo.Version}{fileEx}"; - var requestUri = GetPackFileUrl(downloadFileName); + + string requestUri; + if (string.IsNullOrWhiteSpace(download!.FileIdOrUrl)) + { + var downloadFileName = $"{filePlatform}-{fileArch}_{newVersionInfo.Version}{fileEx}"; + requestUri = GetPackFileUrl(downloadFileName); + } + else if (IsHttpUrl(download.FileIdOrUrl)) + { + requestUri = download.FileIdOrUrl; + } + else + { + requestUri = GetSingleFileUrl(download.FileIdOrUrl!); + } + var cacheFilePath = packFilePath + FileExDownloadCache; OnReportDownloading(0f); var rsp = await client.Download( @@ -559,7 +516,7 @@ namespace System.Application.Services.Implementation if (UpdatePackVerification(packFilePath, download!.SHA256!)) // (下载文件)哈希验证成功,进行覆盖安装 { - OverwriteUpgrade(packFileName, isIncrement: false); + OverwriteUpgrade(packFileName, isIncrement: false, downloadType: download.DownloadType); OnReportDownloading(MaxProgressValue); } else @@ -574,7 +531,7 @@ namespace System.Application.Services.Implementation } else { - Fail("The new version compressed package is missing, please contact the administrator."); + OpenInAppStore(); } } } @@ -595,7 +552,8 @@ namespace System.Application.Services.Implementation /// /// /// - protected abstract void OverwriteUpgrade(string value, bool isIncrement); + /// + protected abstract void OverwriteUpgrade(string value, bool isIncrement, AppDownloadType downloadType = default); /// /// 在应用商店中打开 diff --git a/src/ST.Services.CloudService.Models/Models/AppVersionDTO.cs b/src/ST.Services.CloudService.Models/Models/AppVersionDTO.cs index 6b3212452..ddbb6679a 100644 --- a/src/ST.Services.CloudService.Models/Models/AppVersionDTO.cs +++ b/src/ST.Services.CloudService.Models/Models/AppVersionDTO.cs @@ -88,12 +88,11 @@ namespace System.Application.Models public long Length { get; set; } [MPKey(3)] - public string? FileId { get; set; } + public string? FileIdOrUrl { get; set; } bool IExplicitHasValue.ExplicitHasValue() { return !string.IsNullOrWhiteSpace(SHA256) && - !string.IsNullOrWhiteSpace(FileId) && Length > 0; } } diff --git a/src/ST/AppDownloadType.cs b/src/ST/AppDownloadType.cs index 0a41aeb3d..4824f80d2 100644 --- a/src/ST/AppDownloadType.cs +++ b/src/ST/AppDownloadType.cs @@ -1,4 +1,4 @@ -namespace System.Application +namespace System.Application { /// /// APP下载类型 @@ -11,8 +11,13 @@ Install, /// - /// 压缩包 + /// 压缩包(GZip) /// - Compressed, + Compressed_GZip, + + /// + /// 压缩包(7z) + /// + Compressed_7z, } } \ No newline at end of file