diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 56d1b219..3f3a8fd0 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -23,10 +23,10 @@ env: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: - os: [Linux, MacOS] + os: [ubuntu-latest, macos-latest] arch: [x64, arm64] steps: - uses: actions/checkout@v3 @@ -45,62 +45,63 @@ jobs: then version="${inputVersion}" else - version="$(grep -oP '(?<=).*(?=> "${GITHUB_OUTPUT}" - - name: Unit test if: ${{ inputs.run_unit_tests }} working-directory: ./Source run: dotnet test - name: Publish + id: publish working-directory: ./Source run: | os=${{ matrix.os }} - RUNTIME_IDENTIFIER="$(echo ${os,} | sed 's/macOS/osx/')-${{ matrix.arch }}" + target_os="$(echo ${os/-latest/} | sed 's/ubuntu/linux/')" + display_os="$(echo ${target_os/macos/macOS} | sed 's/linux/Linux/')" + echo "display_os=${display_os}" >> $GITHUB_OUTPUT + RUNTIME_IDENTIFIER="$(echo ${target_os/macos/osx})-${{ matrix.arch }}" echo "$RUNTIME_IDENTIFIER" dotnet publish \ LibationAvalonia/LibationAvalonia.csproj \ --runtime "$RUNTIME_IDENTIFIER" \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${{ matrix.os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ - -p:PublishProfile=LibationAvalonia/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml + --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + -p:PublishProfile=LibationAvalonia/Properties/PublishProfiles/${display_os}Profile.pubxml dotnet publish \ - LoadByOS/${{ matrix.os }}ConfigApp/${{ matrix.os }}ConfigApp.csproj \ + LoadByOS/${display_os}ConfigApp/${display_os}ConfigApp.csproj \ --runtime "$RUNTIME_IDENTIFIER" \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${{ matrix.os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ - -p:PublishProfile=LoadByOS/Properties/${{ matrix.os }}ConfigApp/PublishProfiles/${{ matrix.os }}Profile.pubxml + --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + -p:PublishProfile=LoadByOS/Properties/${display_os}ConfigApp/PublishProfiles/${display_os}Profile.pubxml dotnet publish \ LibationCli/LibationCli.csproj \ --runtime "$RUNTIME_IDENTIFIER" \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${{ matrix.os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ - -p:PublishProfile=LibationCli/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml + --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + -p:PublishProfile=LibationCli/Properties/PublishProfiles/${display_os}Profile.pubxml dotnet publish \ HangoverAvalonia/HangoverAvalonia.csproj \ --runtime "$RUNTIME_IDENTIFIER" \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${{ matrix.os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ - -p:PublishProfile=HangoverAvalonia/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml - + --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + -p:PublishProfile=HangoverAvalonia/Properties/PublishProfiles/${display_os}Profile.pubxml - name: Build bundle id: bundle - working-directory: ./Source/bin/Publish/${{ matrix.os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} + working-directory: ./Source/bin/Publish/${{ steps.publish.outputs.display_os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} run: | BUNDLE_DIR=$(pwd) echo "Bundle dir: ${BUNDLE_DIR}" cd .. - SCRIPT=../../../Scripts/Bundle_${{ matrix.os }}.sh + SCRIPT=../../../Scripts/Bundle_${{ steps.publish.outputs.display_os }}.sh chmod +rx ${SCRIPT} ${SCRIPT} "${BUNDLE_DIR}" "${{ steps.get_version.outputs.version }}" "${{ matrix.arch }}" artifact=$(ls ./bundle) echo "artifact=${artifact}" >> "${GITHUB_OUTPUT}" - - name: Publish bundle uses: actions/upload-artifact@v3 with: name: ${{ steps.bundle.outputs.artifact }} path: ./Source/bin/Publish/bundle/${{ steps.bundle.outputs.artifact }} - if-no-files-found: error + if-no-files-found: error \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..9940d7d0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/Source/bin/Avalonia/Debug/Libation.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "console": "internalConsole" + } + + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..2ba35f87 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "dependsOn": [ + "build_libation", + "build_linuxconfigapp" + ] + }, + { + "label": "build_libation", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "${workspaceFolder}/Source/LibationAvalonia/LibationAvalonia.csproj" + ], + "group": "build", + "presentation": { + //"reveal": "silent" + }, + "problemMatcher": "$msCompile" + }, + { + "label": "build_linuxconfigapp", + "type": "shell", + "command": "dotnet", + "args": [ + "build", + "${workspaceFolder}/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj" + ], + "group": "build", + "presentation": { + //"reveal": "silent" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Documentation/InstallOnMac.md b/Documentation/InstallOnMac.md index 7d4a9af3..45e0be78 100644 --- a/Documentation/InstallOnMac.md +++ b/Documentation/InstallOnMac.md @@ -13,14 +13,10 @@ This walkthrough should get you up and running with Libation on your Mac. - Move the extracted Libation app bundle to your applications folder. - Open a terminal (Go > Utilities > Terminal) - Copy/paste/run the following command (you'll be prompted to enter your password) - - macOS x64 - ```Console - sudo spctl --master-disable && sudo spctl --add --label "Libation" /Applications/Libation.app && open /Applications/Libation.app && sudo spctl --master-enable - ``` - - macOS arm64 - ```Console - codesign --force --deep -s - /Applications/Libation.app && sudo spctl --master-disable && sudo spctl --add --label "Libation" /Applications/Libation.app && open /Applications/Libation.app && sudo spctl --master-enable - ``` + + ```Console + sudo spctl --master-disable && sudo spctl --add --label "Libation" /Applications/Libation.app && open /Applications/Libation.app && sudo spctl --master-enable + ``` - Close the terminal and use Libation! ## Running Hangover diff --git a/Scripts/Bundle_Linux.sh b/Scripts/Bundle_Linux.sh index a5f842ef..9fcb1437 100644 --- a/Scripts/Bundle_Linux.sh +++ b/Scripts/Bundle_Linux.sh @@ -88,38 +88,27 @@ cp $FOLDER_EXEC/Libation.desktop $FOLDER_DESKTOP/Libation.desktop echo "Creating pre-install file..." echo "#!/bin/bash - # Pre-install script, removes previous installation program files and sym links - echo \"Removing previously created symlinks...\" - rm /usr/bin/libation rm /usr/bin/hangover rm /usr/bin/libationcli - echo \"Removing previously installed Libation files...\" - rm -r /usr/lib/libation - # making sure it won't stop installation exit 0 " >> $FOLDER_DEBIAN/preinst echo "Creating post-install file..." echo "#!/bin/bash - gtk-update-icon-cache -f /usr/share/icons/hicolor/ - ln -s /usr/lib/libation/Libation /usr/bin/libation ln -s /usr/lib/libation/Hangover /usr/bin/hangover ln -s /usr/lib/libation/LibationCli /usr/bin/libationcli - # Increase the maximum number of inotify instances - -if ! grep -q 'fs.inotify.max_user_instances=524288' /etc/sysctl.conf; then +if ! grep -q 'fs.inotify.max_user_instances=524288' /etc/sysctl.conf; then echo fs.inotify.max_user_instances=524288 | tee -a /etc/sysctl.conf && sysctl -p fi - # workaround until this file is moved to the user's home directory touch /usr/lib/libation/appsettings.json chmod 666 /usr/lib/libation/appsettings.json @@ -139,6 +128,11 @@ echo "Changing permissions for pre- and post-install files..." chmod +x "$FOLDER_DEBIAN/preinst" chmod +x "$FOLDER_DEBIAN/postinst" +if [ "$(uname -s)" == "Darwin" ]; then + echo "macOS detected, installing dpkg" + brew install dpkg +fi + DEB_FILE=Libation.${VERSION}-linux-chardonnay-${ARCH}.deb echo "Creating $DEB_FILE" dpkg-deb -Zxz --build $DEB_DIR ./$DEB_FILE @@ -149,4 +143,4 @@ mv $DEB_FILE ./bundle/$DEB_FILE rm -r "$BIN_DIR" -echo "Done!" +echo "Done!" \ No newline at end of file diff --git a/Scripts/Bundle_MacOS.sh b/Scripts/Bundle_MacOS.sh index d9bd31b9..57bc60e1 100644 --- a/Scripts/Bundle_MacOS.sh +++ b/Scripts/Bundle_MacOS.sh @@ -96,6 +96,9 @@ done APP_FILE=Libation.${VERSION}-macOS-chardonnay-${ARCH}.tgz +echo "Signing executables in: $BUNDLE" +codesign --force --deep -s - $BUNDLE + echo "Creating app bundle: $APP_FILE" tar -czvf $APP_FILE $BUNDLE @@ -105,4 +108,4 @@ mv $APP_FILE ./bundle/$APP_FILE rm -r $BUNDLE -echo "Done!" +echo "Done!" \ No newline at end of file diff --git a/Source/AaxDecrypter/AudiobookDownloadBase.cs b/Source/AaxDecrypter/AudiobookDownloadBase.cs index fff17d58..9f8bb535 100644 --- a/Source/AaxDecrypter/AudiobookDownloadBase.cs +++ b/Source/AaxDecrypter/AudiobookDownloadBase.cs @@ -3,7 +3,6 @@ using Dinah.Core.Net.Http; using Dinah.Core.StepRunner; using FileManager; using System; -using System.Diagnostics; using System.IO; using System.Threading.Tasks; @@ -225,6 +224,7 @@ namespace AaxDecrypter } finally { + nfsp.NetworkFileStream.RequestHeaders["User-Agent"] = DownloadOptions.UserAgent; nfsp.NetworkFileStream.SpeedLimit = DownloadOptions.DownloadSpeedBps; } diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 46f173ac..376bde2c 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -39,42 +39,6 @@ namespace AudibleUtilities return new ApiExtended(api); } - /// Get api from existing tokens else login with native api callbacks. - public static async Task CreateAsync(Account account, ILoginCallback loginCallback) - { - Serilog.Log.Logger.Information("{@DebugInfo}", new - { - LoginType = nameof(ILoginCallback), - Account = account?.MaskedLogEntry ?? "[null]", - LocaleName = account?.Locale?.Name - }); - - var api = await EzApiCreator.GetApiAsync( - loginCallback, - account.Locale, - AudibleApiStorage.AccountsSettingsFile, - account.GetIdentityTokensJsonPath()); - return new ApiExtended(api); - } - - /// Get api from existing tokens else login with external browser - public static async Task CreateAsync(Account account, ILoginExternal loginExternal) - { - Serilog.Log.Logger.Information("{@DebugInfo}", new - { - LoginType = nameof(ILoginExternal), - Account = account?.MaskedLogEntry ?? "[null]", - LocaleName = account?.Locale?.Name - }); - - var api = await EzApiCreator.GetApiAsync( - loginExternal, - account.Locale, - AudibleApiStorage.AccountsSettingsFile, - account.GetIdentityTokensJsonPath()); - return new ApiExtended(api); - } - /// Get api from existing tokens. Assumes you have valid login tokens. Else exception public static async Task CreateAsync(Account account) { diff --git a/Source/AudibleUtilities/AudibleUtilities.csproj b/Source/AudibleUtilities/AudibleUtilities.csproj index 95ffd248..48394512 100644 --- a/Source/AudibleUtilities/AudibleUtilities.csproj +++ b/Source/AudibleUtilities/AudibleUtilities.csproj @@ -5,7 +5,7 @@ - + diff --git a/Source/AudibleUtilities/Mkb79Auth.cs b/Source/AudibleUtilities/Mkb79Auth.cs index f3bc6db5..2e4c56ef 100644 --- a/Source/AudibleUtilities/Mkb79Auth.cs +++ b/Source/AudibleUtilities/Mkb79Auth.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using AudibleApi; using AudibleApi.Authorization; +using AudibleApi.Cryptography; using Dinah.Core; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -178,7 +179,7 @@ namespace AudibleUtilities LocaleCode = account.Locale.CountryCode, RefreshToken = account.IdentityTokens.RefreshToken.Value, StoreAuthenticationCookie = account.IdentityTokens.StoreAuthenticationCookie, - WebsiteCookies = new(account.IdentityTokens.Cookies.ToKeyValuePair()), + WebsiteCookies = new(account.IdentityTokens.Cookies), }; } diff --git a/Source/HangoverAvalonia/HangoverAvalonia.csproj b/Source/HangoverAvalonia/HangoverAvalonia.csproj index c00bc51c..6db44c40 100644 --- a/Source/HangoverAvalonia/HangoverAvalonia.csproj +++ b/Source/HangoverAvalonia/HangoverAvalonia.csproj @@ -21,13 +21,7 @@ - - en;es + en diff --git a/Source/HangoverWinForms/HangoverWinForms.csproj b/Source/HangoverWinForms/HangoverWinForms.csproj index dd37f2e3..78b131cf 100644 --- a/Source/HangoverWinForms/HangoverWinForms.csproj +++ b/Source/HangoverWinForms/HangoverWinForms.csproj @@ -16,13 +16,7 @@ - - en;es + en - copyused true libation.ico Libation @@ -17,13 +15,7 @@ - - en;es + en @@ -109,7 +101,7 @@ - + diff --git a/Source/LibationAvalonia/ViewModels/MainWindowViewModel.cs b/Source/LibationAvalonia/ViewModels/MainWindowViewModel.cs index 0fca0bb4..8f5fa93a 100644 --- a/Source/LibationAvalonia/ViewModels/MainWindowViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/MainWindowViewModel.cs @@ -25,6 +25,9 @@ namespace LibationAvalonia.ViewModels public ProcessQueueViewModel ProcessQueue { get; } = new ProcessQueueViewModel(); public ProductsDisplayViewModel ProductsDisplay { get; } = new ProductsDisplayViewModel(); + private double? _downloadProgress = null; + public double? DownloadProgress { get => _downloadProgress; set => this.RaiseAndSetIfChanged(ref _downloadProgress, value); } + /// Library filterting query public string FilterString { get => _filterString; set => this.RaiseAndSetIfChanged(ref _filterString, value); } diff --git a/Source/LibationAvalonia/Views/MainWindow.ScanAuto.cs b/Source/LibationAvalonia/Views/MainWindow.ScanAuto.cs index 422872e4..f201e57f 100644 --- a/Source/LibationAvalonia/Views/MainWindow.ScanAuto.cs +++ b/Source/LibationAvalonia/Views/MainWindow.ScanAuto.cs @@ -9,7 +9,6 @@ using System.Linq; namespace LibationAvalonia.Views { - //DONE public partial class MainWindow { private InterruptableTimer autoScanTimer; @@ -17,11 +16,7 @@ namespace LibationAvalonia.Views private void Configure_ScanAuto() { // creating InterruptableTimer inside 'Configure_' is a break from the pattern. As long as no one else needs to access or subscribe to it, this is ok - var hours = 0; - var minutes = 5; - var seconds = 0; - var _5_minutes = new TimeSpan(hours, minutes, seconds); - autoScanTimer = new InterruptableTimer(_5_minutes); + autoScanTimer = new InterruptableTimer(TimeSpan.FromMinutes(5)); // subscribe as async/non-blocking. I'd actually rather prefer blocking but real-world testing found that caused a deadlock in the AudibleAPI autoScanTimer.Elapsed += async (_, __) => @@ -44,9 +39,9 @@ namespace LibationAvalonia.Views }; _viewModel.AutoScanChecked = Configuration.Instance.AutoScan; - + // if enabled: begin on load - Load += startAutoScan; + Opened += startAutoScan; // if new 'default' account is added, run autoscan AccountsSettingsPersister.Saving += accountsPreSave; diff --git a/Source/LibationAvalonia/Views/MainWindow.Update.cs b/Source/LibationAvalonia/Views/MainWindow.Update.cs deleted file mode 100644 index e466c41a..00000000 --- a/Source/LibationAvalonia/Views/MainWindow.Update.cs +++ /dev/null @@ -1,87 +0,0 @@ -using AppScaffolding; -using LibationAvalonia.Dialogs; -using LibationFileManager; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace LibationAvalonia.Views -{ - public partial class MainWindow - { - private void Configure_Update() - { - Opened += async (_, _) => await checkForUpdates(); - } - - private async Task checkForUpdates() - { - async Task downloadUpdate(UpgradeProperties upgradeProperties) - { - if (upgradeProperties.ZipUrl is null) - { - Serilog.Log.Logger.Warning("Download link for new version not found"); - return null; - } - - //Silently download the update in the background, save it to a temp file. - - var zipFile = Path.Combine(Path.GetTempPath(), Path.GetFileName(upgradeProperties.ZipUrl)); - - Serilog.Log.Logger.Information($"Downloading {zipFile}"); - - try - { - System.Net.Http.HttpClient cli = new(); - using var fs = File.OpenWrite(zipFile); - using var dlStream = await cli.GetStreamAsync(new Uri(upgradeProperties.ZipUrl)); - await dlStream.CopyToAsync(fs); - } - catch (Exception ex) - { - Serilog.Log.Logger.Error(ex, "Failed to download the update: {pdate}", upgradeProperties.ZipUrl); - return null; - } - return zipFile; - } - - try - { - var upgradeProperties = await Task.Run(LibationScaffolding.GetLatestRelease); - if (upgradeProperties is null) return; - - const string ignoreUpdate = "IgnoreUpdate"; - var config = Configuration.Instance; - - if (config.GetString(propertyName: ignoreUpdate) == upgradeProperties.LatestRelease.ToString()) - return; - - var interop = InteropFactory.Create(); - - if (!interop.CanUpdate) - Serilog.Log.Logger.Information("Can't perform update automatically"); - - var notificationResult = await new UpgradeNotificationDialog(upgradeProperties, interop.CanUpdate).ShowDialog(this); - - if (notificationResult == DialogResult.Ignore) - config.SetString(upgradeProperties.LatestRelease.ToString(), ignoreUpdate); - - if (notificationResult != DialogResult.OK) return; - - //Download the update file in the background, - string updateBundle = await downloadUpdate(upgradeProperties); - - if (string.IsNullOrEmpty(updateBundle) || !File.Exists(updateBundle)) return; - - //Install the update - Serilog.Log.Logger.Information($"Begin running auto-updater"); - interop.InstallUpdate(updateBundle); - Serilog.Log.Logger.Information($"Completed running auto-updater"); - } - catch (Exception ex) - { - Serilog.Log.Logger.Error(ex, "An error occured while checking for app updates."); - } - } - } -} diff --git a/Source/LibationAvalonia/Views/MainWindow.Upgrade.cs b/Source/LibationAvalonia/Views/MainWindow.Upgrade.cs new file mode 100644 index 00000000..02b315ba --- /dev/null +++ b/Source/LibationAvalonia/Views/MainWindow.Upgrade.cs @@ -0,0 +1,34 @@ +using Avalonia.Threading; +using LibationAvalonia.Dialogs; +using LibationUiBase; +using System.Threading.Tasks; + +namespace LibationAvalonia.Views +{ + public partial class MainWindow + { + private void Configure_Upgrade() + { + setProgressVisible(false); +#if !DEBUG + async Task upgradeAvailable(UpgradeEventArgs e) + { + var notificationResult = await new UpgradeNotificationDialog(e.UpgradeProperties, e.CapUpgrade).ShowDialogAsync(this); + + e.Ignore = notificationResult == DialogResult.Ignore; + e.InstallUpgrade = notificationResult == DialogResult.OK; + } + + var upgrader = new Upgrader(); + upgrader.DownloadProgress += async (_, e) => await Dispatcher.UIThread.InvokeAsync(() => _viewModel.DownloadProgress = e.ProgressPercentage); + upgrader.DownloadBegin += async (_, _) => await Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(true)); + upgrader.DownloadCompleted += async (_, _) => await Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(false)); + + Opened += async (_, _) => await upgrader.CheckForUpgradeAsync(upgradeAvailable); +#endif + } + + private void setProgressVisible(bool visible) => _viewModel.DownloadProgress = visible ? 0 : null; + + } +} diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml b/Source/LibationAvalonia/Views/MainWindow.axaml index c1a44e47..8b2fffb8 100644 --- a/Source/LibationAvalonia/Views/MainWindow.axaml +++ b/Source/LibationAvalonia/Views/MainWindow.axaml @@ -194,9 +194,16 @@ - - - + + + + + + + + diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml.cs b/Source/LibationAvalonia/Views/MainWindow.axaml.cs index 33094e6f..88f8a705 100644 --- a/Source/LibationAvalonia/Views/MainWindow.axaml.cs +++ b/Source/LibationAvalonia/Views/MainWindow.axaml.cs @@ -40,9 +40,7 @@ namespace LibationAvalonia.Views Configure_Export(); Configure_Settings(); Configure_ProcessQueue(); -#if !DEBUG - Configure_Update(); -#endif + Configure_Upgrade(); Configure_Filter(); // misc which belongs in winforms app but doesn't have a UI element Configure_NonUI(); diff --git a/Source/LibationCli/LibationCli.csproj b/Source/LibationCli/LibationCli.csproj index b61341eb..04926587 100644 --- a/Source/LibationCli/LibationCli.csproj +++ b/Source/LibationCli/LibationCli.csproj @@ -11,13 +11,7 @@ - - en;es + en - - en;es + en @@ -44,7 +37,6 @@ - diff --git a/Source/LibationWinForms/Program.cs b/Source/LibationWinForms/Program.cs index 28148e21..5afd05b1 100644 --- a/Source/LibationWinForms/Program.cs +++ b/Source/LibationWinForms/Program.cs @@ -51,9 +51,6 @@ namespace LibationWinForms MessageBoxLib.VerboseLoggingWarning_ShowIfTrue(); -#if !DEBUG - checkForUpdate(); -#endif // logging is init'd here AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config); } @@ -165,31 +162,6 @@ namespace LibationWinForms // - long running. won't get a chance to finish in cli. don't move to app scaffolding } - private static void checkForUpdate() - { - AppScaffolding.UpgradeProperties upgradeProperties; - - try - { - upgradeProperties = AppScaffolding.LibationScaffolding.GetLatestRelease(); - if (upgradeProperties is null) - return; - } - catch (Exception ex) - { - MessageBoxLib.ShowAdminAlert(null, "Error checking for update", "Error checking for update", ex); - return; - } - - if (upgradeProperties.ZipUrl is null) - { - MessageBox.Show(upgradeProperties.HtmlUrl, "New version available"); - return; - } - - Updater.Run(upgradeProperties); - } - private static void postLoggingGlobalExceptionHandling() { // this line is all that's needed for strict handling diff --git a/Source/LibationWinForms/Updater.cs b/Source/LibationWinForms/Updater.cs deleted file mode 100644 index 3bb6a926..00000000 --- a/Source/LibationWinForms/Updater.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Windows.Forms; -using AppScaffolding; -using AutoUpdaterDotNET; -using LibationFileManager; -using LibationWinForms.Dialogs; - -namespace LibationWinForms -{ - public static class Updater - { - public static void Run(UpgradeProperties upgradeProperties) - { - string latestVersionOnServer = upgradeProperties.LatestRelease.ToString(); - string downloadZipUrl = upgradeProperties.ZipUrl; - AutoUpdater.ParseUpdateInfoEvent += - args => args.UpdateInfo = new() - { - CurrentVersion = latestVersionOnServer, - DownloadURL = downloadZipUrl, - ChangelogURL = LibationScaffolding.RepositoryLatestUrl - }; - - void AutoUpdaterOnCheckForUpdateEvent(UpdateInfoEventArgs args) - { - if (args is null || !args.IsUpdateAvailable) - return; - - const string ignoreUpdate = "IgnoreUpdate"; - var config = Configuration.Instance; - - if (config.GetString(propertyName: ignoreUpdate) == args.CurrentVersion) - return; - - var notificationResult = new UpgradeNotificationDialog(upgradeProperties).ShowDialog(); - - if (notificationResult == DialogResult.Ignore) - config.SetString(upgradeProperties.LatestRelease.ToString(), ignoreUpdate); - - if (notificationResult != DialogResult.Yes) return; - - try - { - Serilog.Log.Logger.Information("Start upgrade. {@DebugInfo}", new { CurrentlyInstalled = args.InstalledVersion, TargetVersion = args.CurrentVersion }); - AutoUpdater.DownloadUpdate(args); - } - catch (Exception ex) - { - MessageBoxLib.ShowAdminAlert(null, "Error downloading update", "Error downloading update", ex); - } - } - - AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent; - AutoUpdater.Start(LibationScaffolding.RepositoryLatestUrl); - } - } -} diff --git a/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj b/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj index 855b7034..a28cc197 100644 --- a/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj +++ b/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj @@ -11,13 +11,7 @@ - - en;es + en @@ -31,7 +25,7 @@ - + diff --git a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs index bd6458b0..b9dd02e5 100644 --- a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs +++ b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs @@ -23,12 +23,12 @@ namespace LinuxConfigApp public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException(); public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException(); - //only run the auto updater if the current app was installed from the + //only run the auto upgrader if the current app was installed from the //.deb package. Try to detect this by checking if the symlink exists. - public bool CanUpdate => Directory.Exists("/usr/lib/libation"); - public void InstallUpdate(string updateBundle) + public bool CanUpgrade => Directory.Exists("/usr/lib/libation"); + public void InstallUpgrade(string upgradeBundle) { - RunAsRoot("apt", $"install '{updateBundle}'"); + RunAsRoot("apt", $"install '{upgradeBundle}'"); } public Process RunAsRoot(string exe, string args) diff --git a/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj b/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj index 865067f1..1d61b115 100644 --- a/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj +++ b/Source/LoadByOS/MacOSConfigApp/MacOSConfigApp.csproj @@ -11,13 +11,7 @@ - - en;es + en @@ -31,7 +25,7 @@ - + diff --git a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs index a8993345..23f26a7c 100644 --- a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs +++ b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs @@ -13,15 +13,15 @@ namespace MacOSConfigApp public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException(); //I haven't figured out how to find the app bundle's directory from within - //the running process, so don't update unless it's "installed" in /Applications - public bool CanUpdate => Directory.Exists(AppPath); + //the running process, so don't upgrade unless it's "installed" in /Applications + public bool CanUpgrade => Directory.Exists(AppPath); - public void InstallUpdate(string updateBundle) + public void InstallUpgrade(string upgradeBundle) { - Serilog.Log.Information($"Extracting update bundle to {AppPath}"); + Serilog.Log.Information($"Extracting upgrade bundle to {AppPath}"); //tar wil overwrite existing without elevated privileges - Process.Start("tar", $"-xf \"{updateBundle}\" -C \"/Applications\"").WaitForExit(); + Process.Start("tar", $"-xf \"{upgradeBundle}\" -C \"/Applications\"").WaitForExit(); //For now, it seems like this step is unnecessary. We can overwrite and //run Libation without needing to re-add the exception. This is insurance. diff --git a/Source/LoadByOS/WindowsConfigApp/FolderIcon.cs b/Source/LoadByOS/WindowsConfigApp/FolderIcon.cs new file mode 100644 index 00000000..e35eb57c --- /dev/null +++ b/Source/LoadByOS/WindowsConfigApp/FolderIcon.cs @@ -0,0 +1,106 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace WindowsConfigApp +{ + internal static partial class FolderIcon + { + // https://stackoverflow.com/a/21389253 + public static byte[] ToIcon(this Image img) + { + using var ms = new MemoryStream(); + using var bw = new BinaryWriter(ms); + // Header + bw.Write((short)0); // 0-1 : reserved + bw.Write((short)1); // 2-3 : 1=ico, 2=cur + bw.Write((short)1); // 4-5 : number of images + // Image directory + var w = img.Width; + if (w >= 256) w = 0; + bw.Write((byte)w); // 0 : width of image + var h = img.Height; + if (h >= 256) h = 0; + bw.Write((byte)h); // 1 : height of image + bw.Write((byte)0); // 2 : number of colors in palette + bw.Write((byte)0); // 3 : reserved + bw.Write((short)0); // 4 : number of color planes + bw.Write((short)0); // 6 : bits per pixel + var sizeHere = ms.Position; + bw.Write((int)0); // 8 : image size + var start = (int)ms.Position + 4; + bw.Write(start); // 12: offset of image data + // Image data + img.Save(ms, new PngEncoder()); + var imageSize = (int)ms.Position - start; + ms.Seek(sizeHere, SeekOrigin.Begin); + bw.Write(imageSize); + ms.Seek(0, SeekOrigin.Begin); + + // And load it + return ms.ToArray(); + } + + public static void DeleteIcon(this DirectoryInfo directoryInfo) => DeleteIcon(directoryInfo.FullName); + public static void DeleteIcon(string dir) + { + string[] array = new string[3] { "desktop.ini", "Icon.ico", ".hidden" }; + foreach (string path in array) + { + string text = Path.Combine(dir, path); + if (File.Exists(text)) + { + File.SetAttributes(text, File.GetAttributes(text) | FileAttributes.Normal); + new FileInfo(text).IsReadOnly = false; + File.Delete(text); + } + } + + refresh(); + } + + // https://github.com/dimuththarindu/FIC-Folder-Icon-Changer/blob/master/project/FIC/Classes/IconCustomizer.cs + + public static void SetIcon(this DirectoryInfo directoryInfo, string icoPath, string folderType) + => SetIcon(directoryInfo.FullName, icoPath, folderType); + + public static void SetIcon(string dir, string icoPath, string folderType) + { + var desktop_ini = Path.Combine(dir, "desktop.ini"); + var Icon_ico = Path.Combine(dir, "Icon.ico"); + var hidden = Path.Combine(dir, ".hidden"); + + //deleting existing files + DeleteIcon(dir); + + //copying Icon file //overwriting + File.Copy(icoPath, Icon_ico, true); + + //writing configuration file + string[] desktopLines = { "[.ShellClassInfo]", "IconResource=Icon.ico,0", "[ViewState]", "Mode=", "Vid=", $"FolderType={folderType}" }; + File.WriteAllLines(desktop_ini, desktopLines); + + //configure file 2 + string[] hiddenLines = { "desktop.ini", "Icon.ico" }; + File.WriteAllLines(hidden, hiddenLines); + + //making system files + File.SetAttributes(desktop_ini, File.GetAttributes(desktop_ini) | FileAttributes.Hidden | FileAttributes.System | FileAttributes.ReadOnly); + File.SetAttributes(Icon_ico, File.GetAttributes(Icon_ico) | FileAttributes.Hidden | FileAttributes.System | FileAttributes.ReadOnly); + File.SetAttributes(hidden, File.GetAttributes(hidden) | FileAttributes.Hidden | FileAttributes.System | FileAttributes.ReadOnly); + + // this strangely completes the process. also hides these 3 hidden system files, even if "show hidden items" is checked + File.SetAttributes(dir, File.GetAttributes(dir) | FileAttributes.ReadOnly); + + refresh(); + } + + private static void refresh() => SHChangeNotify(0x08000000, 0x0000, 0, 0); //SHCNE_ASSOCCHANGED SHCNF_IDLIST + + + [DllImport("shell32.dll", SetLastError = true)] + private static extern void SHChangeNotify(int wEventId, int uFlags, nint dwItem1, nint dwItem2); + } +} diff --git a/Source/LoadByOS/WindowsConfigApp/WinInterop.cs b/Source/LoadByOS/WindowsConfigApp/WinInterop.cs index 42cded34..5aef4bea 100644 --- a/Source/LoadByOS/WindowsConfigApp/WinInterop.cs +++ b/Source/LoadByOS/WindowsConfigApp/WinInterop.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; +using SixLabors.ImageSharp; using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Dinah.Core.WindowsDesktop; -using Dinah.Core.WindowsDesktop.Drawing; using LibationFileManager; +using System.IO; +using System; +using Dinah.Core; namespace WindowsConfigApp { @@ -21,11 +18,11 @@ namespace WindowsConfigApp try { - var icon = ImageReader.ToIcon(image); + var icon = Image.Load(File.ReadAllBytes(image)).ToIcon(); iconPath = Path.Combine(directory, $"{Guid.NewGuid()}.ico"); - icon.Save(iconPath); + File.WriteAllBytes(iconPath, icon); - new DirectoryInfo(directory).SetIcon(iconPath, Directories.FolderTypes.Music); + new DirectoryInfo(directory)?.SetIcon(iconPath, "Music"); } finally { @@ -36,8 +33,9 @@ namespace WindowsConfigApp public void DeleteFolderIcon(string directory) => new DirectoryInfo(directory)?.DeleteIcon(); - public bool CanUpdate => true; - public void InstallUpdate(string updateBundle) + + public bool CanUpgrade => true; + public void InstallUpgrade(string upgradeBundle) { var thisExe = Environment.ProcessPath; var thisDir = Path.GetDirectoryName(thisExe); @@ -45,7 +43,10 @@ namespace WindowsConfigApp File.Copy("ZipExtractor.exe", zipExtractor, overwrite: true); - RunAsRoot(zipExtractor, $"--input \"{updateBundle}\" --output \"{thisDir}\" --executable \"{thisExe}\""); + RunAsRoot(zipExtractor, + $"--input {upgradeBundle.SurroundWithQuotes()} " + + $"--output {thisDir.SurroundWithQuotes()} " + + $"--executable {thisExe.SurroundWithQuotes()}"); } public Process RunAsRoot(string exe, string args) diff --git a/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj b/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj index 0dba0cbf..2057f3a8 100644 --- a/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj +++ b/Source/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj @@ -2,24 +2,15 @@ WinExe - net7.0-windows + net7.0 true - true - enable true - win-x64 false false - - en;es + en @@ -33,11 +24,7 @@ - - - - - +