diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 3f3a8fd0..86468524 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -15,6 +15,21 @@ on: description: 'Skip running unit tests' required: false default: true + runs_on: + type: string + description: 'The GitHub hosted runner to use' + required: true + OS: + type: string + description: > + The operating system targeted by the build. + + There must be a corresponding Bundle_$OS.sh script file in ./Scripts + required: true + architecture: + type: string + description: 'CPU architecture targeted by the build.' + required: true env: DOTNET_CONFIGURATION: 'Release' @@ -23,11 +38,8 @@ env: jobs: build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] - arch: [x64, arm64] + name: '${{ inputs.OS }}-${{ inputs.architecture }}' + runs-on: ${{ inputs.runs_on }} steps: - uses: actions/checkout@v3 - name: Setup .NET @@ -48,6 +60,7 @@ jobs: version="$(grep -Eio -m 1 '.*' ./Source/AppScaffolding/AppScaffolding.csproj | sed -r 's/<\/?Version>//g')" fi echo "version=${version}" >> "${GITHUB_OUTPUT}" + - name: Unit test if: ${{ inputs.run_unit_tests }} working-directory: ./Source @@ -56,52 +69,64 @@ jobs: - name: Publish id: publish working-directory: ./Source - run: | - os=${{ matrix.os }} - target_os="$(echo ${os/-latest/} | sed 's/ubuntu/linux/')" - display_os="$(echo ${target_os/macos/macOS} | sed 's/linux/Linux/')" + run: | + if [[ "${{ inputs.OS }}" == "MacOS" ]] + then + display_os="macOS" + RUNTIME_ID="osx-${{ inputs.architecture }}" + else + display_os="Linux" + RUNTIME_ID="linux-${{ inputs.architecture }}" + fi + + OUTPUT="bin/Publish/${display_os}-${{ inputs.architecture }}-${{ env.RELEASE_NAME }}" + echo "display_os=${display_os}" >> $GITHUB_OUTPUT - RUNTIME_IDENTIFIER="$(echo ${target_os/macos/osx})-${{ matrix.arch }}" - echo "$RUNTIME_IDENTIFIER" + echo "Runtime Identifier: $RUNTIME_ID" + echo "Output Directory: $OUTPUT" + dotnet publish \ LibationAvalonia/LibationAvalonia.csproj \ - --runtime "$RUNTIME_IDENTIFIER" \ + --runtime $RUNTIME_ID \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + --output $OUTPUT \ -p:PublishProfile=LibationAvalonia/Properties/PublishProfiles/${display_os}Profile.pubxml dotnet publish \ LoadByOS/${display_os}ConfigApp/${display_os}ConfigApp.csproj \ - --runtime "$RUNTIME_IDENTIFIER" \ + --runtime $RUNTIME_ID \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + --output $OUTPUT \ -p:PublishProfile=LoadByOS/Properties/${display_os}ConfigApp/PublishProfiles/${display_os}Profile.pubxml dotnet publish \ LibationCli/LibationCli.csproj \ - --runtime "$RUNTIME_IDENTIFIER" \ + --runtime $RUNTIME_ID \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + --output $OUTPUT \ -p:PublishProfile=LibationCli/Properties/PublishProfiles/${display_os}Profile.pubxml dotnet publish \ HangoverAvalonia/HangoverAvalonia.csproj \ - --runtime "$RUNTIME_IDENTIFIER" \ + --runtime $RUNTIME_ID \ --configuration ${{ env.DOTNET_CONFIGURATION }} \ - --output bin/Publish/${display_os}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} \ + --output $OUTPUT \ -p:PublishProfile=HangoverAvalonia/Properties/PublishProfiles/${display_os}Profile.pubxml + - name: Build bundle id: bundle - working-directory: ./Source/bin/Publish/${{ steps.publish.outputs.display_os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }} + working-directory: ./Source/bin/Publish/${{ steps.publish.outputs.display_os }}-${{ inputs.architecture }}-${{ env.RELEASE_NAME }} run: | BUNDLE_DIR=$(pwd) echo "Bundle dir: ${BUNDLE_DIR}" cd .. - SCRIPT=../../../Scripts/Bundle_${{ steps.publish.outputs.display_os }}.sh + SCRIPT=../../../Scripts/Bundle_${{ inputs.OS }}.sh chmod +rx ${SCRIPT} - ${SCRIPT} "${BUNDLE_DIR}" "${{ steps.get_version.outputs.version }}" "${{ matrix.arch }}" + ${SCRIPT} "${BUNDLE_DIR}" "${{ steps.get_version.outputs.version }}" "${{ inputs.architecture }}" 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 \ No newline at end of file + if-no-files-found: error + retention-days: 7 diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 605016a1..a14c5a38 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -22,6 +22,7 @@ env: jobs: build: + name: '${{ matrix.os }}-${{ matrix.release_name }}' runs-on: windows-latest strategy: matrix: @@ -110,4 +111,4 @@ jobs: name: ${{ steps.zip.outputs.artifact }}.zip path: ./Source/bin/Publish/${{ steps.zip.outputs.artifact }}.zip if-no-files-found: error - + retention-days: 7 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6cf9787..ce1186ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,14 +17,34 @@ on: default: true jobs: + windows: uses: ./.github/workflows/build-windows.yml with: version_override: ${{ inputs.version_override }} run_unit_tests: ${{ inputs.run_unit_tests }} - + linux: + strategy: + matrix: + OS: [Redhat, Debian] + architecture: [x64, arm64] uses: ./.github/workflows/build-linux.yml with: version_override: ${{ inputs.version_override }} + runs_on: ubuntu-latest + OS: ${{ matrix.OS }} + architecture: ${{ matrix.architecture }} + run_unit_tests: ${{ inputs.run_unit_tests }} + + macos: + strategy: + matrix: + architecture: [x64, arm64] + uses: ./.github/workflows/build-linux.yml + with: + version_override: ${{ inputs.version_override }} + runs_on: macos-latest + OS: MacOS + architecture: ${{ matrix.architecture }} run_unit_tests: ${{ inputs.run_unit_tests }} diff --git a/.releaseindex.json b/.releaseindex.json index 5e44f230..9d9dea23 100644 --- a/.releaseindex.json +++ b/.releaseindex.json @@ -1,8 +1,10 @@ { - "WindowsClassic": "Classic-Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-classic\\.zip", - "WindowsAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-chardonnay\\.zip", - "LinuxAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-amd64\\.deb", - "MacOSAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-macOS-chardonnay-x64\\.tgz", - "LinuxAvalonia_Arm64": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-arm64\\.deb", - "MacOSAvalonia_Arm64": "Libation\\.\\d+\\.\\d+\\.\\d+-macOS-chardonnay-arm64\\.tgz" + "WindowsClassic": "Classic-Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-classic\\.zip", + "WindowsAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-chardonnay\\.zip", + "LinuxAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-amd64\\.deb", + "LinuxAvalonia_RPM": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-amd64\\.rpm", + "MacOSAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-macOS-chardonnay-x64\\.tgz", + "LinuxAvalonia_Arm64": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-arm64\\.deb", + "LinuxAvalonia_Arm64_RPM": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-arm64\\.rpm", + "MacOSAvalonia_Arm64": "Libation\\.\\d+\\.\\d+\\.\\d+-macOS-chardonnay-arm64\\.tgz" } diff --git a/Scripts/Bundle_Linux.sh b/Scripts/Bundle_Debian.sh similarity index 100% rename from Scripts/Bundle_Linux.sh rename to Scripts/Bundle_Debian.sh diff --git a/Scripts/Bundle_Redhat.sh b/Scripts/Bundle_Redhat.sh new file mode 100644 index 00000000..77bb4769 --- /dev/null +++ b/Scripts/Bundle_Redhat.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +BIN_DIR=$1; shift +VERSION=$1; shift +ARCH=$1; shift + +if [ -z "$BIN_DIR" ] +then + echo "This script must be called with a the Libation Linux bins directory as an argument." + exit +fi + +if [ ! -d "$BIN_DIR" ] +then + echo "The directory \"$BIN_DIR\" does not exist." + exit +fi + +if [ -z "$VERSION" ] +then + echo "This script must be called with the Libation version number as an argument." + exit +fi + +if [ -z "$ARCH" ] +then + echo "This script must be called with the Libation cpu architecture as an argument." + exit +fi + +contains() { case "$1" in *"$2"*) true ;; *) false ;; esac } + +if ! contains "$BIN_DIR" "$ARCH" +then + echo "This script must be called with a Libation binaries for ${ARCH}." + exit +fi + +BASEDIR=$(pwd) + +delfiles=('libmp3lame.arm64.dylib' 'libmp3lame.x64.dylib' 'libmp3lame.x64.dll' 'libmp3lame.x86.dll' 'ffmpegaac.arm64.dylib' 'ffmpegaac.x64.dylib' 'ffmpegaac.x64.dll' 'ffmpegaac.x86.dll' 'LinuxConfigApp' 'LinuxConfigApp.deps.json' 'LinuxConfigApp.runtimeconfig.json') +if [[ "$ARCH" == "x64" ]] +then + delfiles+=('libmp3lame.arm64.so' 'ffmpegaac.arm64.so') + ARCH_RPM="x86_64" + ARCH="amd64" +else + delfiles+=('libmp3lame.x64.so' 'ffmpegaac.x64.so') + ARCH_RPM="aarch64" +fi + +notinstalled=('libcoreclrtraceptprovider.so' 'libation_glass.svg' 'Libation.desktop') + +mkdir -p ~/rpmbuild/SPECS +mkdir ~/rpmbuild/BUILD +mkdir ~/rpmbuild/RPMS + +echo "Name: libation +Version: ${VERSION} +Release: 1 +Summary: Liberate your Audible Library + +License: GPLv3+ +URL: https://github.com/rmcrackan/Libation +Source0: https://github.com/rmcrackan/Libation + +Requires: bash + + +%define __os_install_post %{nil} + +%description +Liberate your Audible Library + +%install +mkdir -p %{buildroot}%{_libdir}/%{name} +mkdir -p %{buildroot}%{_datadir}/icons/hicolor/scalable/apps +mkdir -p %{buildroot}%{_datadir}/applications + +if test -f 'libcoreclrtraceptprovider.so'; then + rm 'libcoreclrtraceptprovider.so' +fi + +touch appsettings.json +chmod 666 appsettings.json + +install -m 666 libation_glass.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/libation.svg +install -m 666 Libation.desktop %{buildroot}%{_datadir}/applications/Libation.desktop + +rm libation_glass.svg +rm Libation.desktop + +install * %{buildroot}%{_libdir}/%{name}/ + +%post + +ln -s %{_libdir}/%{name}/Libation %{_bindir}/libation +ln -s %{_libdir}/%{name}/Hangover %{_bindir}/hangover +ln -s %{_libdir}/%{name}/LibationCli %{_bindir}/libationcli + +gtk-update-icon-cache -f %{_datadir}/icons/hicolor/ + +%postun + +rm %{_bindir}/libation +rm %{_bindir}/hangover +rm %{_bindir}/libationcli + +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 + +%files +%{_datadir}/icons/hicolor/scalable/apps/libation.svg +%{_datadir}/applications/Libation.desktop +%{_libdir}/%{name}/appsettings.json" >> ~/rpmbuild/SPECS/libation.spec + + +cd "$BIN_DIR" + +for f in *; do + if [[ " ${delfiles[*]} " =~ " ${f} " ]]; then + echo "Deleting $f" + elif [[ ! " ${notinstalled[*]} " =~ " ${f} " ]]; then + echo "%{_libdir}/%{name}/${f}" >> ~/rpmbuild/SPECS/libation.spec + cp $f ~/rpmbuild/BUILD/ + else + cp $f ~/rpmbuild/BUILD/ + fi +done + +cd ~/rpmbuild/SPECS/ +rpmbuild -bb --target $ARCH_RPM libation.spec + +cd $BASEDIR +RPM_FILE=$(ls ~/rpmbuild/RPMS/${ARCH_RPM}) + +mkdir bundle + +mv ~/rpmbuild/RPMS/${ARCH_RPM}/$RPM_FILE "./bundle/Libation.${VERSION}-linux-chardonnay-${ARCH}.rpm" diff --git a/Source/AppScaffolding/LibationScaffolding.cs b/Source/AppScaffolding/LibationScaffolding.cs index efaaf63b..e474191f 100644 --- a/Source/AppScaffolding/LibationScaffolding.cs +++ b/Source/AppScaffolding/LibationScaffolding.cs @@ -330,7 +330,18 @@ namespace AppScaffolding //Download the release index var bts = await gitHubClient.Repository.Content.GetRawContent(ownerAccount, repoName, ".releaseindex.json"); var releaseIndex = JObject.Parse(System.Text.Encoding.ASCII.GetString(bts)); - var regexPattern = releaseIndex.Value(ReleaseIdentifier.ToString()); + + string regexPattern; + + try + { + regexPattern = releaseIndex.Value(InteropFactory.Create().ReleaseIdString); + } + catch + { + regexPattern = releaseIndex.Value(ReleaseIdentifier.ToString()); + } + var regex = new System.Text.RegularExpressions.Regex(regexPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase); //https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#get-the-latest-release diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index c9c202ae..be96e3f0 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -249,8 +249,26 @@ namespace AudibleUtilities } } - foreach (var child in children) + int lastEpNum = -1, dupeCount = 0; + foreach (var child in children.OrderBy(i => i.EpisodeNumber).ThenBy(i => i.PublicationDateTime)) { + string sequence; + if (child.EpisodeNumber is null) + { + // This should properly be Single() not FirstOrDefault(), but FirstOrDefault is defensive for malformed data from audible + sequence = parent.Relationships.FirstOrDefault(r => r.Asin == child.Asin)?.Sort?.ToString() ?? "0"; + } + else + { + //multipart episodes may have the same episode number + if (child.EpisodeNumber == lastEpNum) + dupeCount++; + else + lastEpNum = child.EpisodeNumber.Value; + + sequence = (lastEpNum + dupeCount).ToString(); + } + // use parent's 'DateAdded'. DateAdded is just a convenience prop for: PurchaseDate.UtcDateTime child.PurchaseDate = parent.PurchaseDate; // parent is essentially a series @@ -259,8 +277,7 @@ namespace AudibleUtilities new Series { Asin = parent.Asin, - // This should properly be Single() not FirstOrDefault(), but FirstOrDefault is defensive for malformed data from audible - Sequence = parent.Relationships.FirstOrDefault(r => r.Asin == child.Asin)?.Sort?.ToString() ?? "0", + Sequence = sequence, Title = parent.TitleWithSubtitle } }; diff --git a/Source/HangoverAvalonia/Controls/CheckedListBox.axaml b/Source/HangoverAvalonia/Controls/CheckedListBox.axaml index df1c6319..b7d8c94d 100644 --- a/Source/HangoverAvalonia/Controls/CheckedListBox.axaml +++ b/Source/HangoverAvalonia/Controls/CheckedListBox.axaml @@ -24,7 +24,7 @@ diff --git a/Source/HangoverAvalonia/HangoverAvalonia.csproj b/Source/HangoverAvalonia/HangoverAvalonia.csproj index fb32461e..676b4164 100644 --- a/Source/HangoverAvalonia/HangoverAvalonia.csproj +++ b/Source/HangoverAvalonia/HangoverAvalonia.csproj @@ -66,13 +66,13 @@ - - + + - - - - + + + + diff --git a/Source/LibationAvalonia/App.axaml b/Source/LibationAvalonia/App.axaml index ce461dcf..6716b9de 100644 --- a/Source/LibationAvalonia/App.axaml +++ b/Source/LibationAvalonia/App.axaml @@ -53,15 +53,15 @@ - - + - + + + + + + + + + + + + + + + diff --git a/Source/LibationAvalonia/Assets/DataGridFluentTheme.xaml b/Source/LibationAvalonia/Assets/DataGridFluentTheme.xaml deleted file mode 100644 index 82ef3f10..00000000 --- a/Source/LibationAvalonia/Assets/DataGridFluentTheme.xaml +++ /dev/null @@ -1,588 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0.6 - 0.8 - - M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z - M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z - M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z - M109 486 19 576 1024 1581 2029 576 1939 486 1024 1401z - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Source/LibationAvalonia/Controls/CheckedListBox.axaml b/Source/LibationAvalonia/Controls/CheckedListBox.axaml index cf70be9a..66625cbc 100644 --- a/Source/LibationAvalonia/Controls/CheckedListBox.axaml +++ b/Source/LibationAvalonia/Controls/CheckedListBox.axaml @@ -24,7 +24,7 @@ diff --git a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml index c61583eb..00577f6f 100644 --- a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml @@ -23,7 +23,7 @@ CanUserSortColumns="False" AutoGenerateColumns="False" IsReadOnly="False" - Items="{Binding Accounts}" + ItemsSource="{Binding Accounts}" GridLinesVisibility="All"> @@ -64,14 +64,11 @@ - - - - - - - - + + @@ -96,13 +93,10 @@ - - - - - - - + diff --git a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml index b28db5c7..92f6252e 100644 --- a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml @@ -24,7 +24,7 @@ CanUserSortColumns="True" AutoGenerateColumns="False" IsReadOnly="False" - Items="{Binding DataGridCollectionView}" + ItemsSource="{Binding DataGridCollectionView}" GridLinesVisibility="All"> diff --git a/Source/LibationAvalonia/Dialogs/DialogWindow.cs b/Source/LibationAvalonia/Dialogs/DialogWindow.cs index a46aea77..e6213841 100644 --- a/Source/LibationAvalonia/Dialogs/DialogWindow.cs +++ b/Source/LibationAvalonia/Dialogs/DialogWindow.cs @@ -41,9 +41,9 @@ namespace LibationAvalonia.Dialogs } protected virtual void SaveAndClose() => Close(DialogResult.OK); - protected virtual Task SaveAndCloseAsync() => Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(SaveAndClose); + protected virtual async Task SaveAndCloseAsync() => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(SaveAndClose); protected virtual void CancelAndClose() => Close(DialogResult.Cancel); - protected virtual Task CancelAndCloseAsync() => Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(CancelAndClose); + protected virtual async Task CancelAndCloseAsync() => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(CancelAndClose); private async void DialogWindow_KeyDown(object sender, Avalonia.Input.KeyEventArgs e) { diff --git a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml index db6dee86..54aad592 100644 --- a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml +++ b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml @@ -23,7 +23,7 @@ CanUserSortColumns="False" AutoGenerateColumns="False" IsReadOnly="False" - Items="{Binding Filters}" + ItemsSource="{Binding Filters}" GridLinesVisibility="All"> @@ -44,14 +44,11 @@ - - - - - - - - + diff --git a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml index 4039abf5..f8d8a42b 100644 --- a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml +++ b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml @@ -24,7 +24,7 @@ BeginningEdit="ReplacementGrid_BeginningEdit" CellEditEnding="ReplacementGrid_CellEditEnding" KeyDown="ReplacementGrid_KeyDown" - Items="{Binding replacements}"> + ItemsSource="{Binding replacements}"> diff --git a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml index 4632519b..cd5a9ba6 100644 --- a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml @@ -45,7 +45,7 @@ GridLinesVisibility="All" AutoGenerateColumns="False" DoubleTapped="EditTemplateViewModel_DoubleTapped" - Items="{Binding ListItems}" > + ItemsSource="{Binding ListItems}" > diff --git a/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs index 2fce4497..4d6c230b 100644 --- a/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs @@ -53,7 +53,7 @@ namespace LibationAvalonia.Dialogs.Login public async void CopyUrlToClipboard_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) - => await Application.Current.Clipboard.SetTextAsync(ExternalLoginUrl); + => await App.MainWindow.Clipboard.SetTextAsync(ExternalLoginUrl); public void LaunchInBrowser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => Go.To.Url(ExternalLoginUrl); diff --git a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml index ccadfc6f..c98ce952 100644 --- a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml @@ -2,10 +2,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="950" d:DesignHeight="650" - MinWidth="950" MinHeight="650" - MaxWidth="950" MaxHeight="650" - Width="950" Height="650" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="650" + MinWidth="800" MinHeight="650" + MaxWidth="800" MaxHeight="650" + Width="800" Height="650" x:Class="LibationAvalonia.Dialogs.SearchSyntaxDialog" Title="Filter Options" WindowStartupLocation="CenterOwner" @@ -16,48 +16,55 @@ RowDefinitions="Auto,Auto,*" ColumnDefinitions="Auto,Auto,Auto,Auto"> - + + + + - - - - - - - - diff --git a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs index 4c3c45f9..07992c0b 100644 --- a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs @@ -25,6 +25,9 @@ Find books between 1-100 minutes long length:[1 TO 100] Find books exactly 1 hr long length:60 +Find books published from 2020-1-1 to +2023-12-31 + datepublished:[20200101 TO 20231231] " + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchNumberFields()); diff --git a/Source/LibationAvalonia/FormSaveExtension.cs b/Source/LibationAvalonia/FormSaveExtension.cs index c9bca50b..0a9f0123 100644 --- a/Source/LibationAvalonia/FormSaveExtension.cs +++ b/Source/LibationAvalonia/FormSaveExtension.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform; using LibationFileManager; using System; using System.Linq; @@ -111,12 +112,12 @@ namespace LibationAvalonia public static void HideMinMaxBtns(this Window form) { - if (Design.IsDesignMode || !Configuration.IsWindows) + if (Design.IsDesignMode || !Configuration.IsWindows || form.TryGetPlatformHandle() is not IPlatformHandle handle) return; - var handle = form.PlatformImpl.Handle.Handle; - var currentStyle = GetWindowLong(handle, GWL_STYLE); - SetWindowLong(handle, GWL_STYLE, currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX); + var currentStyle = GetWindowLong(handle.Handle, GWL_STYLE); + + SetWindowLong(handle.Handle, GWL_STYLE, currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX); } const long WS_MINIMIZEBOX = 0x00020000L; diff --git a/Source/LibationAvalonia/LibationAvalonia.csproj b/Source/LibationAvalonia/LibationAvalonia.csproj index 744f3e98..39448922 100644 --- a/Source/LibationAvalonia/LibationAvalonia.csproj +++ b/Source/LibationAvalonia/LibationAvalonia.csproj @@ -32,8 +32,8 @@ - - + + @@ -70,13 +70,13 @@ - - - - - - - + + + + + + + diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml index 555576dc..03323d4e 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml @@ -46,7 +46,7 @@ VerticalCacheLength="1.2" HorizontalCacheLength="1" Background="Transparent" - Items="{Binding Items}" + ItemsSource="{Binding Items}" ItemTemplate="{StaticResource elementFactory}" /> @@ -81,7 +81,7 @@ - + diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs index 70c95408..0b7761d8 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs @@ -130,7 +130,7 @@ namespace LibationAvalonia.Views private async void LogCopyBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { string logText = string.Join("\r\n", _viewModel.LogEntries.Select(r => $"{r.LogDate.ToShortDateString()} {r.LogDate.ToShortTimeString()}\t{r.LogMessage}")); - await Application.Current.Clipboard.SetTextAsync(logText); + await App.MainWindow.Clipboard.SetTextAsync(logText); } private async void cancelAllBtn_Click(object sender, EventArgs e) diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml b/Source/LibationAvalonia/Views/ProductsDisplay.axaml index 62207098..a7fb4bf9 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml @@ -15,7 +15,7 @@ ClipboardCopyMode="IncludeHeader" GridLinesVisibility="All" AutoGenerateColumns="False" - Items="{Binding GridEntries}" + ItemsSource="{Binding GridEntries}" CanUserSortColumns="True" BorderThickness="3" CanUserReorderColumns="True"> diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 51860b77..9fe44d1b 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -231,7 +231,7 @@ namespace LibationAvalonia.Views var menuItem = new MenuItem { Header = "_Copy Cell Contents" }; menuItem.Click += async (s, e) - => await Application.Current.Clipboard.SetTextAsync(args.CellClipboardContents); + => await App.MainWindow.Clipboard.SetTextAsync(args.CellClipboardContents); args.ContextMenuItems.Add(menuItem); } diff --git a/Source/LibationAvalonia/Views/SeriesViewGrid.axaml b/Source/LibationAvalonia/Views/SeriesViewGrid.axaml index ad7d3a91..69e3326a 100644 --- a/Source/LibationAvalonia/Views/SeriesViewGrid.axaml +++ b/Source/LibationAvalonia/Views/SeriesViewGrid.axaml @@ -11,7 +11,7 @@ ClipboardCopyMode="IncludeHeader" GridLinesVisibility="All" AutoGenerateColumns="False" - Items="{Binding SeriesEntries}" + ItemsSource="{Binding SeriesEntries}" CanUserSortColumns="True" CanUserReorderColumns="True" BorderThickness="3"> diff --git a/Source/LibationFileManager/IInteropFunctions.cs b/Source/LibationFileManager/IInteropFunctions.cs index 7b0df53c..a79cc0f3 100644 --- a/Source/LibationFileManager/IInteropFunctions.cs +++ b/Source/LibationFileManager/IInteropFunctions.cs @@ -16,6 +16,7 @@ namespace LibationFileManager Process RunAsRoot(string exe, string args); void InstallUpgrade(string upgradeBundle); bool CanUpgrade { get; } + string ReleaseIdString { get; } } public class WebViewNavigationEventArgs : EventArgs diff --git a/Source/LibationFileManager/NullInteropFunctions.cs b/Source/LibationFileManager/NullInteropFunctions.cs index aba0b46f..b72e354d 100644 --- a/Source/LibationFileManager/NullInteropFunctions.cs +++ b/Source/LibationFileManager/NullInteropFunctions.cs @@ -15,6 +15,7 @@ namespace LibationFileManager public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException(); public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException(); public bool CanUpgrade => throw new PlatformNotSupportedException(); + public string ReleaseIdString => throw new PlatformNotSupportedException(); public Process RunAsRoot(string exe, string args) => throw new PlatformNotSupportedException(); public void InstallUpgrade(string updateBundle) => throw new PlatformNotSupportedException(); } diff --git a/Source/LibationSearchEngine/LuceneExtensions.cs b/Source/LibationSearchEngine/LuceneExtensions.cs index 0a0c2071..20c3eaa9 100644 --- a/Source/LibationSearchEngine/LuceneExtensions.cs +++ b/Source/LibationSearchEngine/LuceneExtensions.cs @@ -37,6 +37,8 @@ namespace LibationSearchEngine internal static string ToLuceneString(this float f) => ((double)f).ToLuceneString(); internal static string ToLuceneString(this DateTime dt) => dt.ToString("yyyyMMdd") + DECIMAL_PRECISION; + internal static string ToLuceneString(this DateTime? dt) + => dt?.ToLuceneString() ?? ""; internal static string ToLuceneString(this double d) => d.ToString("0" + DECIMAL_PRECISION).PadLeft(PAD_DIGITS + DECIMAL_PRECISION.Length, '0'); } diff --git a/Source/LibationSearchEngine/SearchEngine.cs b/Source/LibationSearchEngine/SearchEngine.cs index a24266cd..693775f3 100644 --- a/Source/LibationSearchEngine/SearchEngine.cs +++ b/Source/LibationSearchEngine/SearchEngine.cs @@ -46,9 +46,6 @@ namespace LibationSearchEngine = new ReadOnlyDictionary>( new Dictionary> { - [nameof(LibraryBook.DateAdded)] = lb => lb.DateAdded.ToLuceneString(), - [nameof(Book.DatePublished)] = lb => lb.Book.DatePublished?.ToLuceneString(), - [nameof(Book.Title)] = lb => lb.Book.Title, [ALL_AUTHOR_NAMES] = lb => lb.Book.AuthorNames(), ["Author"] = lb => lb.Book.AuthorNames(), @@ -91,7 +88,13 @@ namespace LibationSearchEngine ["ProductRating"] = lb => lb.Book.Rating.OverallRating.ToLuceneString(), ["Rating"] = lb => lb.Book.Rating.OverallRating.ToLuceneString(), ["UserRating"] = lb => userOverallRating(lb.Book), - ["MyRating"] = lb => userOverallRating(lb.Book) + ["MyRating"] = lb => userOverallRating(lb.Book), + + [nameof(LibraryBook.DateAdded)] = lb => lb.DateAdded.ToLuceneString(), + [nameof(Book.DatePublished)] = lb => lb.Book.DatePublished?.ToLuceneString(), + + ["LastDownload"] = lb => lb.Book.UserDefinedItem.LastDownloaded.ToLuceneString(), + ["LastDownloaded"] = lb => lb.Book.UserDefinedItem.LastDownloaded.ToLuceneString() } ); @@ -127,6 +130,9 @@ namespace LibationSearchEngine ["Episode"] = lb => lb.Book.IsEpisodeChild(), ["Episodes"] = lb => lb.Book.IsEpisodeChild(), ["IsEpisode"] = lb => lb.Book.IsEpisodeChild(), + + ["Absent"] = lb => lb.AbsentFromLastScan, + ["AbsentFromLastScan"] = lb => lb.AbsentFromLastScan, } ); @@ -287,7 +293,13 @@ namespace LibationSearchEngine var v2 = liberatedError(book); d.RemoveField("liberatederror"); d.AddBool("LiberatedError", v2); - }); + + var v3 = book.UserDefinedItem.LastDownloaded?.ToLuceneString() ?? ""; + d.RemoveField("LastDownload"); + d.AddNotAnalyzed("LastDownload", v3); + d.RemoveField("LastDownloaded"); + d.AddNotAnalyzed("LastDownloaded", v3); + }); public void UpdateUserRatings(Book book) =>updateDocument( diff --git a/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.Designer.cs b/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.Designer.cs index 7600c226..f1aecf39 100644 --- a/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.Designer.cs +++ b/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.Designer.cs @@ -1,132 +1,135 @@ namespace LibationWinForms.Dialogs { - partial class SearchSyntaxDialog - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; + partial class SearchSyntaxDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } - #region Windows Form Designer generated code + #region Windows Form Designer generated code - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.label1 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); - this.closeBtn = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(12, 9); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(358, 52); - this.label1.TabIndex = 0; - this.label1.Text = "Full Lucene query syntax is supported\r\nFields with similar names are synomyns (eg" + - ": Author, Authors, AuthorNames)\r\n\r\nTAG FORMAT: [tagName]"; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(12, 71); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(118, 65); - this.label2.TabIndex = 1; - this.label2.Text = "STRING FIELDS\r\n\r\nSearch for wizard of oz:\r\n title:oz\r\n title:\"wizard of o" + - "z\""; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(233, 71); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(195, 78); - this.label3.TabIndex = 2; - this.label3.Text = "NUMBER FIELDS\r\n\r\nFind books between 1-100 minutes long\r\n length:[1 TO 100]\r\nF" + - "ind books exactly 1 hr long\r\n length:60"; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(454, 71); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(168, 52); - this.label4.TabIndex = 3; - this.label4.Text = "BOOLEAN (TRUE/FALSE) FIELDS\r\n\r\nFind books that you haven\'t rated:\r\n -IsRated"; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(673, 71); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(257, 78); - this.label5.TabIndex = 4; - this.label5.Text = "ID FIELDS\r\n\r\nAlice\'s Adventures in Wonderland (ID: B015D78L0U)\r\n id:B015D78L0" + - "U\r\n\r\nAll of these are synonyms for the ID field"; - // - // closeBtn - // - this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.closeBtn.Location = new System.Drawing.Point(890, 465); - this.closeBtn.Name = "closeBtn"; - this.closeBtn.Size = new System.Drawing.Size(75, 23); - this.closeBtn.TabIndex = 5; - this.closeBtn.Text = "Close"; - this.closeBtn.UseVisualStyleBackColor = true; - this.closeBtn.Click += new System.EventHandler(this.CloseBtn_Click); - // - // SearchSyntaxDialog - // - this.AcceptButton = this.closeBtn; - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.CancelButton = this.closeBtn; - this.ClientSize = new System.Drawing.Size(977, 500); - this.Controls.Add(this.closeBtn); - this.Controls.Add(this.label5); - this.Controls.Add(this.label4); - this.Controls.Add(this.label3); - this.Controls.Add(this.label2); - this.Controls.Add(this.label1); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "SearchSyntaxDialog"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "Filter options"; - this.ResumeLayout(false); - this.PerformLayout(); + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SearchSyntaxDialog)); + label1 = new System.Windows.Forms.Label(); + label2 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label5 = new System.Windows.Forms.Label(); + closeBtn = new System.Windows.Forms.Button(); + SuspendLayout(); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(14, 10); + label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(410, 60); + label1.TabIndex = 0; + label1.Text = "Full Lucene query syntax is supported\r\nFields with similar names are synomyns (eg: Author, Authors, AuthorNames)\r\n\r\nTAG FORMAT: [tagName]"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(14, 82); + label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(129, 75); + label2.TabIndex = 1; + label2.Text = "STRING FIELDS\r\n\r\nSearch for wizard of oz:\r\n title:oz\r\n title:\"wizard of oz\""; + // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(272, 82); + label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(224, 135); + label3.TabIndex = 2; + label3.Text = resources.GetString("label3.Text"); + // + // label4 + // + label4.AutoSize = true; + label4.Location = new System.Drawing.Point(530, 82); + label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(187, 60); + label4.TabIndex = 3; + label4.Text = "BOOLEAN (TRUE/FALSE) FIELDS\r\n\r\nFind books that you haven't rated:\r\n -IsRated"; + // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(785, 82); + label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(278, 90); + label5.TabIndex = 4; + label5.Text = "ID FIELDS\r\n\r\nAlice's Adventures in Wonderland (ID: B015D78L0U)\r\n id:B015D78L0U\r\n\r\nAll of these are synonyms for the ID field"; + // + // closeBtn + // + closeBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + closeBtn.Location = new System.Drawing.Point(1038, 537); + closeBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + closeBtn.Name = "closeBtn"; + closeBtn.Size = new System.Drawing.Size(88, 27); + closeBtn.TabIndex = 5; + closeBtn.Text = "Close"; + closeBtn.UseVisualStyleBackColor = true; + closeBtn.Click += CloseBtn_Click; + // + // SearchSyntaxDialog + // + AcceptButton = closeBtn; + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + CancelButton = closeBtn; + ClientSize = new System.Drawing.Size(1140, 577); + Controls.Add(closeBtn); + Controls.Add(label5); + Controls.Add(label4); + Controls.Add(label3); + Controls.Add(label2); + Controls.Add(label1); + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + MaximizeBox = false; + MinimizeBox = false; + Name = "SearchSyntaxDialog"; + StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + Text = "Filter options"; + ResumeLayout(false); + PerformLayout(); + } - } + #endregion - #endregion - - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label label5; - private System.Windows.Forms.Button closeBtn; - } + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Button closeBtn; + } } \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.resx b/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.resx index e8ae276d..395c5678 100644 --- a/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.resx +++ b/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.resx @@ -1,5 +1,4 @@ - - + @@ -58,4 +57,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + NUMBER FIELDS + +Find books between 1-100 minutes long + length:[1 TO 100] +Find books exactly 1 hr long + length:60 +Find books published from 2020-1-1 to +2023-12-31 + datepublished:[20200101 TO 20231231] + \ No newline at end of file diff --git a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs index d3d76482..d005c122 100644 --- a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs +++ b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs @@ -1,4 +1,5 @@ -using LibationFileManager; +using AppScaffolding; +using LibationFileManager; using System.Diagnostics; namespace LinuxConfigApp @@ -24,12 +25,17 @@ namespace LinuxConfigApp public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException(); public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException(); + public string ReleaseIdString => LibationScaffolding.ReleaseIdentifier.ToString() + (File.Exists("/bin/yum") ? "_RPM" : ""); + //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. + //.deb or .rpm package. Try to detect this by checking if the symlink exists. public bool CanUpgrade => Directory.Exists("/usr/lib/libation"); public void InstallUpgrade(string upgradeBundle) { - RunAsRoot("apt", $"install '{upgradeBundle}'"); + if (File.Exists("/bin/yum")) + RunAsRoot("yum", $"install '{upgradeBundle}'"); + else + RunAsRoot("apt", $"install '{upgradeBundle}'"); } public Process RunAsRoot(string exe, string args) diff --git a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs index 0cc75660..ca48996a 100644 --- a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs +++ b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs @@ -24,6 +24,8 @@ namespace MacOSConfigApp //the running process, so don't upgrade unless it's "installed" in /Applications public bool CanUpgrade => Directory.Exists(AppPath); + public string ReleaseIdString => AppScaffolding.LibationScaffolding.ReleaseIdentifier.ToString(); + public void InstallUpgrade(string upgradeBundle) { Serilog.Log.Information($"Extracting upgrade bundle to {AppPath}"); diff --git a/Source/LoadByOS/WindowsConfigApp/WinInterop.cs b/Source/LoadByOS/WindowsConfigApp/WinInterop.cs index bf8eae77..32fce3da 100644 --- a/Source/LoadByOS/WindowsConfigApp/WinInterop.cs +++ b/Source/LoadByOS/WindowsConfigApp/WinInterop.cs @@ -25,6 +25,9 @@ namespace WindowsConfigApp => new DirectoryInfo(directory)?.DeleteIcon(); public bool CanUpgrade => true; + + public string ReleaseIdString => AppScaffolding.LibationScaffolding.ReleaseIdentifier.ToString(); + public void InstallUpgrade(string upgradeBundle) { var thisExe = Environment.ProcessPath;