diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index ec8b315d..56d1b219 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -1,5 +1,5 @@
# build-linux.yml
-# Reusable workflow that builds the Linux and MacOS versions of Libation.
+# Reusable workflow that builds the Linux and MacOS (x64 and arm64) versions of Libation.
---
name: build
@@ -19,6 +19,7 @@ on:
env:
DOTNET_CONFIGURATION: 'Release'
DOTNET_VERSION: '7.0.x'
+ RELEASE_NAME: 'chardonnay'
jobs:
build:
@@ -26,8 +27,7 @@ jobs:
strategy:
matrix:
os: [Linux, MacOS]
- ui: [Avalonia]
- release_name: [chardonnay]
+ arch: [x64, arm64]
steps:
- uses: actions/checkout@v3
- name: Setup .NET
@@ -57,25 +57,50 @@ jobs:
- name: Publish
working-directory: ./Source
run: |
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} Libation${{ matrix.ui }}/Libation${{ matrix.ui }}.csproj -p:PublishProfile=Libation${{ matrix.ui }}/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} LoadByOS/${{ matrix.os }}ConfigApp/${{ matrix.os }}ConfigApp.csproj -p:PublishProfile=LoadByOS/Properties/${{ matrix.os }}ConfigApp/PublishProfiles/${{ matrix.os }}Profile.pubxml
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} LibationCli/LibationCli.csproj -p:PublishProfile=LibationCli/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} Hangover${{ matrix.ui }}/Hangover${{ matrix.ui }}.csproj -p:PublishProfile=Hangover${{ matrix.ui }}/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
-
- - name: Zip artifact
- id: zip
- working-directory: ./Source/bin/Publish/${{ matrix.os }}-${{ matrix.release_name }}
+ os=${{ matrix.os }}
+ RUNTIME_IDENTIFIER="$(echo ${os,} | sed 's/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
+ dotnet publish \
+ LoadByOS/${{ matrix.os }}ConfigApp/${{ matrix.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
+ 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
+ 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
+
+ - name: Build bundle
+ id: bundle
+ working-directory: ./Source/bin/Publish/${{ matrix.os }}-${{ matrix.arch }}-${{ env.RELEASE_NAME }}
run: |
- delfiles=("libmp3lame.x86.dll" "libmp3lame.x64.dll" "ffmpegaac.x86.dll" "ffmpegaac.x64.dll")
- for n in "${delfiles[@]}"; do rm "$n"; done
- osbuild="$(echo '${{ matrix.os }}' | tr '[:upper:]' '[:lower:]')"
- artifact="Libation.${{ steps.get_version.outputs.version }}-${osbuild}-${{ matrix.release_name }}"
+ BUNDLE_DIR=$(pwd)
+ echo "Bundle dir: ${BUNDLE_DIR}"
+ cd ..
+ SCRIPT=../../../Scripts/Bundle_${{ matrix.os }}.sh
+ chmod +rx ${SCRIPT}
+ ${SCRIPT} "${BUNDLE_DIR}" "${{ steps.get_version.outputs.version }}" "${{ matrix.arch }}"
+ artifact=$(ls ./bundle)
echo "artifact=${artifact}" >> "${GITHUB_OUTPUT}"
- tar -zcvf "../${artifact}.tar.gz" .
-
- - name: Publish artifact
+
+ - name: Publish bundle
uses: actions/upload-artifact@v3
with:
- name: ${{ steps.zip.outputs.artifact }}.tar.gz
- path: ./Source/bin/Publish/${{ steps.zip.outputs.artifact }}.tar.gz
+ name: ${{ steps.bundle.outputs.artifact }}
+ path: ./Source/bin/Publish/bundle/${{ steps.bundle.outputs.artifact }}
if-no-files-found: error
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index f82aa21e..caa91e33 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -60,21 +60,49 @@ jobs:
- name: Publish
working-directory: ./Source
run: |
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} Libation${{ matrix.ui }}/Libation${{ matrix.ui }}.csproj -p:PublishProfile=Libation${{ matrix.ui }}/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} LoadByOS/${{ matrix.os }}ConfigApp/${{ matrix.os }}ConfigApp.csproj -p:PublishProfile=LoadByOS/Properties/${{ matrix.os }}ConfigApp/PublishProfiles/${{ matrix.os }}Profile.pubxml
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} LibationCli/LibationCli.csproj -p:PublishProfile=LibationCli/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
- dotnet publish -c ${{ env.DOTNET_CONFIGURATION }} -o bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} Hangover${{ matrix.ui }}/Hangover${{ matrix.ui }}.csproj -p:PublishProfile=Hangover${{ matrix.ui }}/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
+ dotnet publish `
+ Libation${{ matrix.ui }}/Libation${{ matrix.ui }}.csproj `
+ --configuration ${{ env.DOTNET_CONFIGURATION }} `
+ --output bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} `
+ -p:PublishProfile=Libation${{ matrix.ui }}/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
+ dotnet publish `
+ LoadByOS/${{ matrix.os }}ConfigApp/${{ matrix.os }}ConfigApp.csproj `
+ --configuration ${{ env.DOTNET_CONFIGURATION }} `
+ --output bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} `
+ -p:PublishProfile=LoadByOS/Properties/${{ matrix.os }}ConfigApp/PublishProfiles/${{ matrix.os }}Profile.pubxml
+ dotnet publish `
+ LibationCli/LibationCli.csproj `
+ --configuration ${{ env.DOTNET_CONFIGURATION }} `
+ --output bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} `
+ -p:PublishProfile=LibationCli/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
+ dotnet publish `
+ Hangover${{ matrix.ui }}/Hangover${{ matrix.ui }}.csproj `
+ --configuration ${{ env.DOTNET_CONFIGURATION }} `
+ --output bin/Publish/${{ matrix.os }}-${{ matrix.release_name }} `
+ -p:PublishProfile=Hangover${{ matrix.ui }}/Properties/PublishProfiles/${{ matrix.os }}Profile.pubxml
- name: Zip artifact
id: zip
working-directory: ./Source/bin/Publish
run: |
- $dir = "${{ matrix.os }}-${{ matrix.release_name }}\"
- $delfiles = @("libmp3lame.so", "ffmpegaac.so", "glass-with-glow_256.svg", "Libation.desktop")
- foreach ($file in $delfiles){ if (test-path $dir$file){ Remove-Item $dir$file } }
+ $bin_dir = "${{ matrix.os }}-${{ matrix.release_name }}\"
+ $delfiles = @(
+ "libmp3lame.x64.so",
+ "libmp3lame.arm64.so",
+ "libmp3lame.x64.dylib",
+ "libmp3lame.arm64.dylib",
+ "ffmpegaac.x64.so",
+ "ffmpegaac.arm64.so",
+ "ffmpegaac.x64.dylib",
+ "ffmpegaac.arm64.dylib",
+ "WindowsConfigApp.exe",
+ "WindowsConfigApp.runtimeconfig.json",
+ "WindowsConfigApp.deps.json"
+ )
+ foreach ($file in $delfiles){ if (test-path $bin_dir$file){ Remove-Item $bin_dir$file } }
$artifact="${{ matrix.prefix }}Libation.${{ steps.get_version.outputs.version }}-" + "${{ matrix.os }}".ToLower() + "-${{ matrix.release_name }}"
"artifact=$artifact" >> $env:GITHUB_OUTPUT
- Compress-Archive -Path "${dir}*" -DestinationPath "$artifact.zip"
+ Compress-Archive -Path "${bin_dir}*" -DestinationPath "$artifact.zip"
- name: Publish artifact
uses: actions/upload-artifact@v3
diff --git a/.github/workflows/bundle-linux.yml b/.github/workflows/bundle-linux.yml
deleted file mode 100644
index 1a187276..00000000
--- a/.github/workflows/bundle-linux.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-# build-linux.yml
-# Reusable workflow that builds the Libation installation bundles for Linux and MacOS.
----
-name: bundle-linux
-
-on:
- workflow_call:
- inputs:
- version:
- type: string
- description: 'Version number'
- required: true
-
-jobs:
- bundle:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- os: [linux, macos]
- release_name: [chardonnay]
- steps:
- - uses: actions/checkout@v3
-
- - name: Download Artifact
- uses: actions/download-artifact@v3
- with:
- name: "Libation.${{ inputs.version }}-${{ matrix.os }}-${{ matrix.release_name }}.tar.gz"
-
- - name: Build bundle
- id: build
- run: |
- SCRIPT=targz2${{ matrix.os }}bundle.sh
- chmod +rwx ./Scripts/${SCRIPT}
- ./Scripts/${SCRIPT} "Libation.${{ inputs.version }}-${{ matrix.os }}-${{ matrix.release_name }}.tar.gz" ${{ inputs.version }}
- artifact=$(ls ./bundle)
- echo "artifact=${artifact}" >> "${GITHUB_OUTPUT}"
-
- - name: Publish bundle
- uses: actions/upload-artifact@v3
- with:
- name: ${{ steps.build.outputs.artifact }}
- path: ./bundle/${{ steps.build.outputs.artifact }}
- if-no-files-found: error
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3d9e5fca..8495289e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -33,15 +33,9 @@ jobs:
with:
version_override: ${{ needs.prerelease.outputs.version }}
run_unit_tests: false
-
- bundle:
- needs: [prerelease,build]
- uses: ./.github/workflows/bundle-linux.yml
- with:
- version: ${{ needs.prerelease.outputs.version }}
release:
- needs: [prerelease,build,bundle]
+ needs: [prerelease,build]
runs-on: ubuntu-latest
steps:
- name: Download artifacts
diff --git a/.releaseindex.json b/.releaseindex.json
index 870c942e..5e44f230 100644
--- a/.releaseindex.json
+++ b/.releaseindex.json
@@ -1,6 +1,8 @@
{
- "WindowsClassic": "Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-classic\\.zip",
- "WindowsAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-chardonnay\\.zip",
- "LinuxAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay\\.deb",
- "MacOSAvalonia": "Libation\\.app-macOS-x64-\\d+\\.\\d+\\.\\d+\\.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",
+ "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"
}
diff --git a/Images/libation_cheers.svg b/Images/libation_cheers.svg
new file mode 100644
index 00000000..9b60dba2
--- /dev/null
+++ b/Images/libation_cheers.svg
@@ -0,0 +1,32 @@
+
diff --git a/Images/libation_glass.svg b/Images/libation_glass.svg
new file mode 100644
index 00000000..d3a0a092
--- /dev/null
+++ b/Images/libation_glass.svg
@@ -0,0 +1,28 @@
+
diff --git a/Images/libation_hangover.svg b/Images/libation_hangover.svg
new file mode 100644
index 00000000..e1759e86
--- /dev/null
+++ b/Images/libation_hangover.svg
@@ -0,0 +1,30 @@
+
diff --git a/Images/libation_slosh.svg b/Images/libation_slosh.svg
new file mode 100644
index 00000000..d97b41ee
--- /dev/null
+++ b/Images/libation_slosh.svg
@@ -0,0 +1,33 @@
+
diff --git a/Scripts/targz2linuxbundle.sh b/Scripts/Bundle_Linux.sh
similarity index 52%
rename from Scripts/targz2linuxbundle.sh
rename to Scripts/Bundle_Linux.sh
index b4806ad8..a5f842ef 100644
--- a/Scripts/targz2linuxbundle.sh
+++ b/Scripts/Bundle_Linux.sh
@@ -1,17 +1,18 @@
#!/bin/bash
-FILE=$1; shift
+BIN_DIR=$1; shift
VERSION=$1; shift
+ARCH=$1; shift
-if [ -z "$FILE" ]
+if [ -z "$BIN_DIR" ]
then
- echo "This script must be called with a the Libation Linux bin zip file as an argument."
+ echo "This script must be called with a the Libation Linux bins directory as an argument."
exit
fi
-if [ ! -f "$FILE" ]
+if [ ! -d "$BIN_DIR" ]
then
- echo "The file \"$FILE\" does not exist."
+ echo "The directory \"$BIN_DIR\" does not exist."
exit
fi
@@ -21,57 +22,69 @@ then
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 "$FILE" "$VERSION"
+if ! contains "$BIN_DIR" "$ARCH"
then
- echo "This script must be called with a Libation version number that is present in the filename passed."
+ echo "This script must be called with a Libation binaries for ${ARCH}."
exit
fi
-# remove trailing ".tar.gz"
-FOLDER_MAIN=${FILE::-7}
-echo "Working dir: $FOLDER_MAIN"
+ARCH=$(echo $ARCH | sed 's/x64/amd64/')
-if [[ -d "$FOLDER_MAIN" ]]
-then
- echo "$FOLDER_MAIN directory already exists, aborting."
- exit
-fi
+DEB_DIR=./deb
-FOLDER_EXEC="$FOLDER_MAIN/usr/lib/libation"
+FOLDER_EXEC=$DEB_DIR/usr/lib/libation
echo "Exec dir: $FOLDER_EXEC"
+mkdir -p $FOLDER_EXEC
-FOLDER_ICON="$FOLDER_MAIN/usr/share/icons/hicolor/scalable/apps/"
-echo "Icon dir: $FOLDER_ICON"
-
-FOLDER_DESKTOP="$FOLDER_MAIN/usr/share/applications"
-echo "Desktop dir: $FOLDER_DESKTOP"
-
-FOLDER_DEBIAN="$FOLDER_MAIN/DEBIAN"
-echo "Debian dir: $FOLDER_DEBIAN"
-
-mkdir -p "$FOLDER_EXEC"
-mkdir -p "$FOLDER_ICON"
-mkdir -p "$FOLDER_DESKTOP"
-mkdir -p "$FOLDER_DEBIAN"
-
-echo "Extracting $FILE to $FOLDER_EXEC..."
-tar -xzf ${FILE} -C ${FOLDER_EXEC}
+echo "Moving bins from $BIN_DIR to $FOLDER_EXEC"
+mv "${BIN_DIR}/"* $FOLDER_EXEC
if [ $? -ne 0 ]
- then echo "Error extracting ${FILE}"
+ then echo "Error moving ${BIN_DIR} files"
exit
fi
+
+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" == "arm64" ]]
+then
+ delfiles+=('libmp3lame.x64.so' 'ffmpegaac.x64.so')
+else
+ delfiles+=('libmp3lame.arm64.so' 'ffmpegaac.arm64.so')
+fi
+
+for n in "${delfiles[@]}"
+do
+ echo "Deleting $n"
+ rm $FOLDER_EXEC/$n
+done
+
+FOLDER_ICON=$DEB_DIR/usr/share/icons/hicolor/scalable/apps/
+echo "Icon dir: $FOLDER_ICON"
+
+FOLDER_DESKTOP=$DEB_DIR/usr/share/applications
+echo "Desktop dir: $FOLDER_DESKTOP"
+
+FOLDER_DEBIAN=$DEB_DIR/DEBIAN
+echo "Debian dir: $FOLDER_DEBIAN"
+
+mkdir -p $FOLDER_ICON
+mkdir -p $FOLDER_DESKTOP
+mkdir -p $FOLDER_DEBIAN
+
echo "Copying icon..."
-cp "$FOLDER_EXEC/glass-with-glow_256.svg" "$FOLDER_ICON/libation.svg"
+cp $FOLDER_EXEC/libation_glass.svg $FOLDER_ICON/libation.svg
echo "Copying desktop file..."
-cp "$FOLDER_EXEC/Libation.desktop" "$FOLDER_DESKTOP/Libation.desktop"
-
-echo "Workaround for desktop file..."
-sed -i '/^Exec=Libation/c\Exec=/usr/bin/libation' "$FOLDER_DESKTOP/Libation.desktop"
+cp $FOLDER_EXEC/Libation.desktop $FOLDER_DESKTOP/Libation.desktop
echo "Creating pre-install file..."
echo "#!/bin/bash
@@ -81,20 +94,16 @@ echo "#!/bin/bash
echo \"Removing previously created symlinks...\"
rm /usr/bin/libation
-rm /usr/bin/Libation
rm /usr/bin/hangover
-rm /usr/bin/Hangover
rm /usr/bin/libationcli
-rm /usr/bin/LibationCli
echo \"Removing previously installed Libation files...\"
rm -r /usr/lib/libation
-rm -r /usr/lib/Libation
# making sure it won't stop installation
exit 0
-" >> "$FOLDER_DEBIAN/preinst"
+" >> $FOLDER_DEBIAN/preinst
echo "Creating post-install file..."
echo "#!/bin/bash
@@ -114,29 +123,30 @@ 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
-" >> "$FOLDER_DEBIAN/postinst"
+" >> $FOLDER_DEBIAN/postinst
echo "Creating control file..."
echo "Package: Libation
Version: $VERSION
-Architecture: all
+Architecture: $ARCH
Essential: no
Priority: optional
Maintainer: github.com/rmcrackan
Description: liberate your audiobooks
-" >> "$FOLDER_DEBIAN/control"
+" >> $FOLDER_DEBIAN/control
echo "Changing permissions for pre- and post-install files..."
chmod +x "$FOLDER_DEBIAN/preinst"
chmod +x "$FOLDER_DEBIAN/postinst"
-echo "Creating .deb file..."
-dpkg-deb -Zxz --build $FOLDER_MAIN
+DEB_FILE=Libation.${VERSION}-linux-chardonnay-${ARCH}.deb
+echo "Creating $DEB_FILE"
+dpkg-deb -Zxz --build $DEB_DIR ./$DEB_FILE
+echo "moving to ./bundle/$DEB_FILE"
mkdir bundle
-echo "moving to ./bundle/$FOLDER_MAIN.deb"
-mv "$FOLDER_MAIN.deb" "./bundle/$FOLDER_MAIN.deb"
+mv $DEB_FILE ./bundle/$DEB_FILE
-rm -r "$FOLDER_MAIN"
+rm -r "$BIN_DIR"
echo "Done!"
diff --git a/Scripts/Bundle_MacOS.sh b/Scripts/Bundle_MacOS.sh
new file mode 100644
index 00000000..d9bd31b9
--- /dev/null
+++ b/Scripts/Bundle_MacOS.sh
@@ -0,0 +1,108 @@
+#!/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 macos 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
+
+BUNDLE=./Libation.app
+echo "Bundle dir: $BUNDLE"
+
+if [[ -d $BUNDLE ]]
+then
+ echo "$BUNDLE directory already exists, aborting."
+ exit
+fi
+
+BUNDLE_CONTENTS=$BUNDLE/Contents
+echo "Bundle Contents dir: $BUNDLE_CONTENTS"
+
+BUNDLE_RESOURCES=$BUNDLE_CONTENTS/Resources
+echo "Resources dir: $BUNDLE_RESOURCES"
+
+BUNDLE_MACOS=$BUNDLE_CONTENTS/MacOS
+echo "MacOS dir: $BUNDLE_MACOS"
+
+mkdir -p $BUNDLE_CONTENTS
+mkdir -p $BUNDLE_RESOURCES
+mkdir -p $BUNDLE_MACOS
+
+mv "${BIN_DIR}/"* $BUNDLE_MACOS
+
+if [ $? -ne 0 ]
+ then echo "Error moving ${BIN_DIR} files"
+ exit
+fi
+
+echo "Moving icon..."
+mv $BUNDLE_MACOS/libation.icns $BUNDLE_RESOURCES/libation.icns
+
+echo "Moving Info.plist file..."
+mv $BUNDLE_MACOS/Info.plist $BUNDLE_CONTENTS/Info.plist
+
+PLIST_ARCH=$(echo $ARCH | sed 's/x64/x86_64/')
+echo "Set LSArchitecturePriority to $PLIST_ARCH"
+sed -i -e "s/ARCHITECTURE_STRING/$PLIST_ARCH/" $BUNDLE_CONTENTS/Info.plist
+
+echo "Set CFBundleVersion to $VERSION"
+sed -i -e "s/VERSION_STRING/$VERSION/" $BUNDLE_CONTENTS/Info.plist
+
+
+delfiles=( 'libmp3lame.arm64.so' 'libmp3lame.x64.so' 'libmp3lame.x64.dll' 'libmp3lame.x86.dll' 'ffmpegaac.arm64.so' 'ffmpegaac.x64.so' 'ffmpegaac.x64.dll' 'ffmpegaac.x86.dll' 'MacOSConfigApp' 'MacOSConfigApp.deps.json' 'MacOSConfigApp.runtimeconfig.json')
+if [[ "$ARCH" == "arm64" ]]
+then
+ delfiles+=('libmp3lame.x64.dylib' 'ffmpegaac.x64.dylib')
+else
+ delfiles+=('libmp3lame.arm64.dylib' 'ffmpegaac.arm64.dylib')
+fi
+
+
+for n in "${delfiles[@]}"
+do
+ echo "Deleting $n"
+ rm $BUNDLE_MACOS/$n
+done
+
+APP_FILE=Libation.${VERSION}-macOS-chardonnay-${ARCH}.tgz
+
+echo "Creating app bundle: $APP_FILE"
+tar -czvf $APP_FILE $BUNDLE
+
+mkdir bundle
+echo "moving to ./bundle/$APP_FILE"
+mv $APP_FILE ./bundle/$APP_FILE
+
+rm -r $BUNDLE
+
+echo "Done!"
diff --git a/Scripts/targz2macosbundle.sh b/Scripts/targz2macosbundle.sh
deleted file mode 100644
index e772ed31..00000000
--- a/Scripts/targz2macosbundle.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/bash
-
-FILE=$1; shift
-VERSION=$1; shift
-
-if [ -z "$FILE" ]
-then
- echo "This script must be called with a the Libation macos bin zip file as an argument."
- exit
-fi
-
-if [ ! -f "$FILE" ]
-then
- echo "The file \"$FILE\" 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
-
-contains() { case "$1" in *"$2"*) true ;; *) false ;; esac }
-
-if ! contains "$FILE" "$VERSION"
-then
- echo "This script must be called with a Libation version number that is present in the filename passed."
- exit
-fi
-
-BUNDLE="Libation.app"
-echo "Bundle dir: $BUNDLE"
-
-if [[ -d "$BUNDLE" ]]
-then
- echo "$BUNDLE directory already exists, aborting."
- exit
-fi
-
-BUNDLE_CONTENTS="$BUNDLE/Contents"
-echo "Bundle Contents dir: $BUNDLE_CONTENTS"
-
-BUNDLE_RESOURCES="$BUNDLE_CONTENTS/Resources"
-echo "Resources dir: $BUNDLE_RESOURCES"
-
-BUNDLE_MACOS="$BUNDLE_CONTENTS/MacOS"
-echo "MacOS dir: $BUNDLE_MACOS"
-
-mkdir -p "$BUNDLE_CONTENTS"
-mkdir -p "$BUNDLE_RESOURCES"
-mkdir -p "$BUNDLE_MACOS"
-
-echo "Extracting $FILE to $BUNDLE_MACOS..."
-tar -xzf ${FILE} -C ${BUNDLE_MACOS}
-
-if [ $? -ne 0 ]
- then echo "Error extracting ${FILE}"
- exit
-fi
-
-echo "Copying icon..."
-cp "$BUNDLE_MACOS/libation.icns" "$BUNDLE_RESOURCES/libation.icns"
-
-echo "Copying Info.plist file..."
-cp "$BUNDLE_MACOS/Info.plist" "$BUNDLE_CONTENTS/Info.plist"
-
-echo "Set Libation version number..."
-sed -i -e "s/VERSION_STRING/$VERSION/" "$BUNDLE_CONTENTS/Info.plist"
-
-echo "deleting unneeded files.."
-delfiles=("libmp3lame.x64.so" "ffmpegaac.x64.so" "libation.icns" "Info.plist")
-for n in "${delfiles[@]}"; do rm "$BUNDLE_MACOS/$n"; done
-
-echo "Creating app bundle: $BUNDLE-$VERSION.tar.gz"
-tar -czvf "$BUNDLE-$VERSION.tar.gz" "$BUNDLE"
-
-mkdir bundle
-echo "moving to ./bundle/$BUNDLE-$VERSION.tar.gz"
-mv "$BUNDLE-$VERSION.tar.gz" "./bundle/$BUNDLE-macOS-x64-$VERSION.tgz"
-
-rm -r "$BUNDLE"
-
-echo "Done!"
diff --git a/Source/AaxDecrypter/AaxDecrypter.csproj b/Source/AaxDecrypter/AaxDecrypter.csproj
index 8b8bd394..ea3d58f3 100644
--- a/Source/AaxDecrypter/AaxDecrypter.csproj
+++ b/Source/AaxDecrypter/AaxDecrypter.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/Source/AaxDecrypter/AudiobookDownloadBase.cs b/Source/AaxDecrypter/AudiobookDownloadBase.cs
index 43ceb5e0..fbbb3716 100644
--- a/Source/AaxDecrypter/AudiobookDownloadBase.cs
+++ b/Source/AaxDecrypter/AudiobookDownloadBase.cs
@@ -3,6 +3,7 @@ using Dinah.Core.Net.Http;
using Dinah.Core.StepRunner;
using FileManager;
using System;
+using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
@@ -58,7 +59,7 @@ namespace AaxDecrypter
{
BytesReceived = 0,
ProgressPercentage = 0,
- TotalBytesToReceive = InputFileStream.Length
+ TotalBytesToReceive = 0
};
OnDecryptProgressUpdate(zeroProgress);
@@ -66,6 +67,7 @@ namespace AaxDecrypter
public async Task RunAsync()
{
+ await InputFileStream.BeginDownloadingAsync();
var progressTask = Task.Run(reportProgress);
AsyncSteps[$"Cleanup"] = CleanupAsync;
diff --git a/Source/AaxDecrypter/NetworkFileStream.cs b/Source/AaxDecrypter/NetworkFileStream.cs
index c7283ee8..53f1753e 100644
--- a/Source/AaxDecrypter/NetworkFileStream.cs
+++ b/Source/AaxDecrypter/NetworkFileStream.cs
@@ -136,10 +136,10 @@ namespace AaxDecrypter
/// Begins downloading to in a background thread.
/// The downloader
- private Task BeginDownloading()
+ public async Task BeginDownloadingAsync()
{
if (ContentLength != 0 && WritePosition == ContentLength)
- return Task.CompletedTask;
+ return;
if (ContentLength != 0 && WritePosition > ContentLength)
throw new WebException($"Specified write position (0x{WritePosition:X10}) is larger than {nameof(ContentLength)} (0x{ContentLength:X10}).");
@@ -149,7 +149,7 @@ namespace AaxDecrypter
foreach (var header in RequestHeaders)
request.Headers.Add(header.Key, header.Value);
- var response = new HttpClient().Send(request, HttpCompletionOption.ResponseHeadersRead, _cancellationSource.Token);
+ var response = await new HttpClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _cancellationSource.Token);
if (response.StatusCode != HttpStatusCode.PartialContent)
throw new WebException($"Server at {Uri.Host} responded with unexpected status code: {response.StatusCode}.");
@@ -159,11 +159,11 @@ namespace AaxDecrypter
if (WritePosition == 0)
ContentLength = response.Content.Headers.ContentLength.GetValueOrDefault();
- var networkStream = response.Content.ReadAsStream(_cancellationSource.Token);
+ var networkStream = await response.Content.ReadAsStreamAsync(_cancellationSource.Token);
_downloadedPiece = new EventWaitHandle(false, EventResetMode.AutoReset);
//Download the file in the background.
- return Task.Run(() => DownloadFile(networkStream), _cancellationSource.Token);
+ _backgroundDownloadTask = Task.Run(() => DownloadFile(networkStream), _cancellationSource.Token);
}
/// Download to .
@@ -251,7 +251,8 @@ namespace AaxDecrypter
{
get
{
- _backgroundDownloadTask ??= BeginDownloading();
+ if (_backgroundDownloadTask is null)
+ throw new InvalidOperationException($"Background downloader must first be started by calling {nameof(BeginDownloadingAsync)}");
return ContentLength;
}
}
@@ -274,7 +275,8 @@ namespace AaxDecrypter
public override int Read(byte[] buffer, int offset, int count)
{
- _backgroundDownloadTask ??= BeginDownloading();
+ if (_backgroundDownloadTask is null)
+ throw new InvalidOperationException($"Background downloader must first be started by calling {nameof(BeginDownloadingAsync)}");
var toRead = Math.Min(count, Length - Position);
WaitToPosition(Position + toRead);
diff --git a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs
index ad9144ee..138c8ea0 100644
--- a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs
+++ b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs
@@ -26,8 +26,7 @@ namespace AaxDecrypter
protected override async Task Step_DownloadAndDecryptAudiobookAsync()
{
- // MUST put InputFileStream.Length first, because it starts background downloader.
- while (InputFileStream.Length > InputFilePosition && !InputFileStream.IsCancelled)
+ while (InputFilePosition < InputFileStream.Length && !InputFileStream.IsCancelled)
await Task.Delay(200);
if (IsCanceled)
diff --git a/Source/AppScaffolding/LibationScaffolding.cs b/Source/AppScaffolding/LibationScaffolding.cs
index a44fe2d3..1756da77 100644
--- a/Source/AppScaffolding/LibationScaffolding.cs
+++ b/Source/AppScaffolding/LibationScaffolding.cs
@@ -9,7 +9,7 @@ using Dinah.Core;
using Dinah.Core.IO;
using Dinah.Core.Logging;
using LibationFileManager;
-using Microsoft.EntityFrameworkCore;
+using System.Runtime.InteropServices;
using Newtonsoft.Json.Linq;
using Serilog;
@@ -18,14 +18,22 @@ namespace AppScaffolding
public enum ReleaseIdentifier
{
None,
- WindowsClassic,
- WindowsAvalonia,
- LinuxAvalonia,
- MacOSAvalonia
+ WindowsClassic = OS.Windows | Variety.Classic | Architecture.X64,
+ WindowsAvalonia = OS.Windows | Variety.Chardonnay | Architecture.X64,
+ LinuxAvalonia = OS.Linux | Variety.Chardonnay | Architecture.X64,
+ MacOSAvalonia = OS.MacOS | Variety.Chardonnay | Architecture.X64,
+ LinuxAvalonia_Arm64 = OS.Linux | Variety.Chardonnay | Architecture.Arm64,
+ MacOSAvalonia_Arm64 = OS.MacOS | Variety.Chardonnay | Architecture.Arm64
}
// I know I'm taking the wine metaphor a bit far by naming this "Variety", but I don't know what else to call it
- public enum VarietyType { None, Classic, Chardonnay }
+ [Flags]
+ public enum Variety
+ {
+ None,
+ Classic = 0x10000,
+ Chardonnay = 0x20000,
+ }
public static class LibationScaffolding
{
@@ -33,13 +41,22 @@ namespace AppScaffolding
public const string WebsiteUrl = "ht" + "tps://getlibation.com";
public const string RepositoryLatestUrl = "ht" + "tps://github.com/rmcrackan/Libation/releases/latest";
public static ReleaseIdentifier ReleaseIdentifier { get; private set; }
- public static VarietyType Variety
- => ReleaseIdentifier == ReleaseIdentifier.WindowsClassic ? VarietyType.Classic
- : ReleaseIdentifier.In(ReleaseIdentifier.WindowsAvalonia, ReleaseIdentifier.LinuxAvalonia, ReleaseIdentifier.MacOSAvalonia) ? VarietyType.Chardonnay
- : VarietyType.None;
+ public static Variety Variety { get; private set; }
- public static void SetReleaseIdentifier(ReleaseIdentifier releaseID)
- => ReleaseIdentifier = releaseID;
+ public static void SetReleaseIdentifier(Variety varietyType)
+ {
+ Variety = Enum.IsDefined(varietyType) ? varietyType : Variety.None;
+
+ var releaseID = (ReleaseIdentifier)((int)varietyType | (int)Configuration.OS | (int)RuntimeInformation.ProcessArchitecture);
+
+ if (Enum.IsDefined(releaseID))
+ ReleaseIdentifier = releaseID;
+ else
+ {
+ ReleaseIdentifier = ReleaseIdentifier.None;
+ Serilog.Log.Logger.Warning("Unknown release identifier @{DebugInfo}", new { Variety = varietyType, Configuration.OS, RuntimeInformation.ProcessArchitecture });
+ }
+ }
// AppScaffolding
private static Assembly _executingAssembly;
@@ -296,8 +313,8 @@ namespace AppScaffolding
}
private static async System.Threading.Tasks.Task<(Octokit.Release, Octokit.ReleaseAsset)> getLatestRelease()
{
- var ownerAccount = "rmcrackan";
- var repoName = "Libation";
+ const string ownerAccount = "rmcrackan";
+ const string repoName = "Libation";
var gitHubClient = new Octokit.GitHubClient(new Octokit.ProductHeaderValue(repoName));
@@ -305,12 +322,11 @@ namespace AppScaffolding
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());
-
- // https://octokitnet.readthedocs.io/en/latest/releases/
- var releases = await gitHubClient.Repository.Release.GetAll(ownerAccount, repoName);
-
var regex = new System.Text.RegularExpressions.Regex(regexPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
- var latestRelease = releases.FirstOrDefault(r => !r.Draft && !r.Prerelease && r.Assets.Any(a => regex.IsMatch(a.Name)));
+
+ //https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#get-the-latest-release
+ var latestRelease = await gitHubClient.Repository.Release.GetLatest(ownerAccount, repoName);
+
return (latestRelease, latestRelease?.Assets?.FirstOrDefault(a => regex.IsMatch(a.Name)));
}
}
diff --git a/Source/AppScaffolding/OSConfigBase.cs b/Source/AppScaffolding/OSConfigBase.cs
deleted file mode 100644
index d07eb639..00000000
--- a/Source/AppScaffolding/OSConfigBase.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-
-namespace AppScaffolding
-{
- public abstract class OSConfigBase
- {
- public abstract Type InteropFunctionsType { get; }
- public virtual Type[] ReferencedTypes { get; } = new Type[0];
-
- public void Run()
- {
- //Each of these types belongs to a different windows-only assembly that's needed by
- //the WinInterop methods. By referencing these types in main we force the runtime to
- //load their assemblies before execution reaches inside main. This allows the calling
- //process to find these assemblies in its module list.
- _ = ReferencedTypes;
- _ = InteropFunctionsType;
-
- //Wait for the calling process to be ready to read the WriteLine()
- Console.ReadLine();
-
- // Signal the calling process that execution has reached inside main, and that all referenced assemblies have been loaded.
- Console.WriteLine();
-
- // Wait for the calling process to finish reading the process module list, then exit.
- Console.ReadLine();
- }
- }
-}
diff --git a/Source/FileLiberator/Processable.cs b/Source/FileLiberator/Processable.cs
index 37a971b2..49ecd61b 100644
--- a/Source/FileLiberator/Processable.cs
+++ b/Source/FileLiberator/Processable.cs
@@ -54,6 +54,8 @@ namespace FileLiberator
= (await ProcessAsync(libraryBook))
?? new StatusHandler { "Processable should never return a null status" };
+ GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
+
return status;
}
diff --git a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs
index 658e0e0b..f5ff3746 100644
--- a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs
+++ b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs
@@ -133,7 +133,7 @@ namespace LibationAvalonia.Controls
: Configuration.GetKnownDirectoryPath(directorySelectControl.SelectedDirectory);
selectedDir ??= string.Empty;
- Directory = customStates.CustomChecked ? selectedDir : System.IO.Path.Combine(selectedDir, SubDirectory);
+ Directory = customStates.CustomChecked ? selectedDir : System.IO.Path.Combine(selectedDir, SubDirectory ?? "");
}
private void DirectoryOrCustomSelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
diff --git a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs
index 41967e0d..da450e0d 100644
--- a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs
+++ b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs
@@ -117,10 +117,13 @@ namespace LibationAvalonia.Dialogs
{
Title = $"Select the audible-cli [account].json file",
AllowMultiple = false,
- SuggestedStartLocation = new BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)),
FileTypeFilter = new FilePickerFileType[]
{
- new("JSON files (*.json)") { Patterns = new[] { "*.json" } },
+ new("JSON files (*.json)")
+ {
+ Patterns = new[] { "*.json" },
+ AppleUniformTypeIdentifiers = new[] { "public.json" }
+ }
}
};
@@ -274,13 +277,16 @@ namespace LibationAvalonia.Dialogs
var options = new FilePickerSaveOptions
{
Title = $"Save Sover Image",
- SuggestedStartLocation = new BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)),
SuggestedFileName = $"{acc.AccountId}.json",
DefaultExtension = "json",
ShowOverwritePrompt = true,
FileTypeChoices = new FilePickerFileType[]
{
- new("JSON files (*.json)") { Patterns = new[] { "*.json" } },
+ new("JSON files (*.json)")
+ {
+ Patterns = new[] { "*.json" },
+ AppleUniformTypeIdentifiers = new[] { "public.json" }
+ }
}
};
diff --git a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs
index 57b9961b..fc4a4012 100644
--- a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs
+++ b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs
@@ -153,10 +153,22 @@ namespace LibationAvalonia.Dialogs
ShowOverwritePrompt = true,
FileTypeChoices = new FilePickerFileType[]
{
- new("Excel Workbook (*.xlsx)") { Patterns = new[] { "*.xlsx" } },
- new("CSV files (*.csv)") { Patterns = new[] { "*.csv" } },
- new("JSON files (*.json)") { Patterns = new[] { "*.json" } },
- new("All files (*.*)") { Patterns = new[] { "*" } }
+ new("Excel Workbook (*.xlsx)")
+ {
+ Patterns = new[] { "*.xlsx" },
+ AppleUniformTypeIdentifiers = new[] { "org.openxmlformats.spreadsheetml.sheet" }
+ },
+ new("CSV files (*.csv)")
+ {
+ Patterns = new[] { "*.csv" },
+ AppleUniformTypeIdentifiers = new[] { "public.comma-separated-values-text" }
+ },
+ new("JSON files (*.json)")
+ {
+ Patterns = new[] { "*.json" },
+ AppleUniformTypeIdentifiers = new[] { "public.json" }
+ },
+ new("All files (*.*)") { Patterns = new[] { "*" } },
}
});
diff --git a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs
index f6014fe2..c42cabce 100644
--- a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs
+++ b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs
@@ -56,7 +56,11 @@ namespace LibationAvalonia.Dialogs
ShowOverwritePrompt = true,
FileTypeChoices = new FilePickerFileType[]
{
- new("Jpeg (*.jpg)") { Patterns = new[] { "jpg" } }
+ new("Jpeg (*.jpg)")
+ {
+ Patterns = new[] { "jpg" },
+ AppleUniformTypeIdentifiers = new[] { "public.jpeg" }
+ }
}
};
diff --git a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml
index 04e1b655..c2b17889 100644
--- a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml
+++ b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml
@@ -365,7 +365,6 @@
Text="{Binding DownloadDecryptSettings.InProgressDescriptionText}" />
diff --git a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml.cs
index f3c721de..dea9cd97 100644
--- a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml.cs
+++ b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml.cs
@@ -141,7 +141,7 @@ namespace LibationAvalonia.Dialogs
public void LoadSettings(Configuration config)
{
- BooksDirectory = config.Books;
+ BooksDirectory = config.Books.PathWithoutPrefix;
SavePodcastsToParentFolder = config.SavePodcastsToParentFolder;
LoggingLevel = config.LogLevel;
BetaOptIn = config.BetaOptIn;
diff --git a/Source/LibationAvalonia/Program.cs b/Source/LibationAvalonia/Program.cs
index 4c5f861b..6481df23 100644
--- a/Source/LibationAvalonia/Program.cs
+++ b/Source/LibationAvalonia/Program.cs
@@ -2,9 +2,9 @@ using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Reflection;
using System.Threading.Tasks;
using ApplicationServices;
+using AppScaffolding;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.ReactiveUI;
@@ -23,18 +23,15 @@ namespace LibationAvalonia
//We can do this because we're already executing inside the sandbox.
//Any process created in the sandbox executes in the same sandbox.
//Unfortunately, all sandbox files are read/execute, so no writing!
-
- Assembly asm = Assembly.GetExecutingAssembly();
- string path = Path.GetDirectoryName(asm.Location);
- Process.Start("Hangover" + (Configuration.IsWindows ? ".exe" : ""));
+ Process.Start("Hangover");
return;
}
if (Configuration.IsMacOs && args?.Length > 0 && args[0] == "cli")
{
//Open a new Terminal in the sandbox
- Assembly asm2 = Assembly.GetExecutingAssembly();
- string libationProgramFiles = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- Process.Start("/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", $"\"{libationProgramFiles}\"");
+ Process.Start(
+ "/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal",
+ $"\"{Configuration.ProcessDirectory}\"");
return;
}
@@ -44,7 +41,7 @@ namespace LibationAvalonia
// //
//***********************************************//
// Migrations which must occur before configuration is loaded for the first time. Usually ones which alter the Configuration
- var config = AppScaffolding.LibationScaffolding.RunPreConfigMigrations();
+ var config = LibationScaffolding.RunPreConfigMigrations();
App.SetupRequired = !config.LibationSettingsAreValid;
@@ -52,13 +49,10 @@ namespace LibationAvalonia
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
var appBuilderTask = Task.Run(BuildAvaloniaApp);
- if (Configuration.IsWindows)
- AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.WindowsAvalonia);
- else if (Configuration.IsLinux)
- AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.LinuxAvalonia);
- else if (Configuration.IsMacOs)
- AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.MacOSAvalonia);
- else return;
+ LibationScaffolding.SetReleaseIdentifier(Variety.Chardonnay);
+
+ if (LibationScaffolding.ReleaseIdentifier is ReleaseIdentifier.None)
+ return;
if (!App.SetupRequired)
@@ -85,8 +79,8 @@ namespace LibationAvalonia
try
{
// most migrations go in here
- AppScaffolding.LibationScaffolding.RunPostConfigMigrations(config);
- AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config);
+ LibationScaffolding.RunPostConfigMigrations(config);
+ LibationScaffolding.RunPostMigrationScaffolding(config);
return true;
}
diff --git a/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs b/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs
index f4597741..ef7fe809 100644
--- a/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs
+++ b/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs
@@ -7,6 +7,7 @@ using AudibleApi;
using AudibleApi.Common;
using Avalonia.Media;
using Avalonia.Media.Imaging;
+using Avalonia.Threading;
using DataLayer;
using Dinah.Core;
using Dinah.Core.ErrorHandling;
@@ -60,12 +61,12 @@ namespace LibationAvalonia.ViewModels
#region Properties exposed to the view
public ProcessBookResult Result { get => _result; set { this.RaiseAndSetIfChanged(ref _result, value); this.RaisePropertyChanged(nameof(StatusText)); } }
public ProcessBookStatus Status { get => _status; set { this.RaiseAndSetIfChanged(ref _status, value); this.RaisePropertyChanged(nameof(BackgroundColor)); this.RaisePropertyChanged(nameof(IsFinished)); this.RaisePropertyChanged(nameof(IsDownloading)); this.RaisePropertyChanged(nameof(Queued)); } }
- public string Narrator { get => _narrator; set { this.RaiseAndSetIfChanged(ref _narrator, value); } }
- public string Author { get => _author; set { this.RaiseAndSetIfChanged(ref _author, value); } }
- public string Title { get => _title; set { this.RaiseAndSetIfChanged(ref _title, value); } }
- public int Progress { get => _progress; private set { this.RaiseAndSetIfChanged(ref _progress, value); } }
- public string ETA { get => _eta; private set { this.RaiseAndSetIfChanged(ref _eta, value); } }
- public Bitmap Cover { get => _cover; private set { this.RaiseAndSetIfChanged(ref _cover, value); } }
+ public string Narrator { get => _narrator; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _narrator, value)); }
+ public string Author { get => _author; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _author, value)); }
+ public string Title { get => _title; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _title, value)); }
+ public int Progress { get => _progress; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _progress, value)); }
+ public string ETA { get => _eta; private set => Dispatcher.UIThread.Post(() =>this.RaiseAndSetIfChanged(ref _eta, value)); }
+ public Bitmap Cover { get => _cover; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _cover, value)); }
public bool IsFinished => Status is not ProcessBookStatus.Queued and not ProcessBookStatus.Working;
public bool IsDownloading => Status is ProcessBookStatus.Working;
public bool Queued => Status is ProcessBookStatus.Queued;
@@ -131,6 +132,7 @@ namespace LibationAvalonia.ViewModels
public async Task ProcessOneAsync()
{
string procName = CurrentProcessable.Name;
+ ProcessBookResult result = ProcessBookResult.None;
try
{
LinkProcessable(CurrentProcessable);
@@ -138,32 +140,34 @@ namespace LibationAvalonia.ViewModels
var statusHandler = await CurrentProcessable.ProcessSingleAsync(LibraryBook, validate: true);
if (statusHandler.IsSuccess)
- return Result = ProcessBookResult.Success;
+ result = ProcessBookResult.Success;
else if (statusHandler.Errors.Contains("Cancelled"))
{
Logger.Info($"{procName}: Process was cancelled - {LibraryBook.Book}");
- return Result = ProcessBookResult.Cancelled;
+ result = ProcessBookResult.Cancelled;
}
else if (statusHandler.Errors.Contains("Validation failed"))
{
Logger.Info($"{procName}: Validation failed - {LibraryBook.Book}");
- return Result = ProcessBookResult.ValidationFail;
+ result = ProcessBookResult.ValidationFail;
+ }
+ else
+ {
+ foreach (var errorMessage in statusHandler.Errors)
+ Logger.Error($"{procName}: {errorMessage}");
}
-
- foreach (var errorMessage in statusHandler.Errors)
- Logger.Error($"{procName}: {errorMessage}");
}
catch (ContentLicenseDeniedException ldex)
{
if (ldex.AYCL?.RejectionReason is null or RejectionReason.GenericError)
{
Logger.Info($"{procName}: Content license was denied, but this error appears to be caused by a temporary interruption of service. - {LibraryBook.Book}");
- return Result = ProcessBookResult.LicenseDeniedPossibleOutage;
+ result = ProcessBookResult.LicenseDeniedPossibleOutage;
}
else
{
Logger.Info($"{procName}: Content license denied. Check your Audible account to see if you have access to this title. - {LibraryBook.Book}");
- return Result = ProcessBookResult.LicenseDenied;
+ result = ProcessBookResult.LicenseDenied;
}
}
catch (Exception ex)
@@ -172,18 +176,21 @@ namespace LibationAvalonia.ViewModels
}
finally
{
- if (Result == ProcessBookResult.None)
- Result = await showRetry(LibraryBook);
+ if (result == ProcessBookResult.None)
+ result = await showRetry(LibraryBook);
- Status = Result switch
+ var status = result switch
{
ProcessBookResult.Success => ProcessBookStatus.Completed,
ProcessBookResult.Cancelled => ProcessBookStatus.Cancelled,
_ => ProcessBookStatus.Failed,
};
+
+ await Dispatcher.UIThread.InvokeAsync(() => Status = status);
}
- return Result;
+ await Dispatcher.UIThread.InvokeAsync(() => Result = result);
+ return result;
}
public async Task CancelAsync()
@@ -294,9 +301,9 @@ namespace LibationAvalonia.ViewModels
#region Processable event handlers
- private void Processable_Begin(object sender, LibraryBook libraryBook)
+ private async void Processable_Begin(object sender, LibraryBook libraryBook)
{
- Status = ProcessBookStatus.Working;
+ await Dispatcher.UIThread.InvokeAsync(() => Status = ProcessBookStatus.Working);
Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}");
diff --git a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs
index 2083768c..db25a07e 100644
--- a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs
+++ b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs
@@ -10,8 +10,6 @@ using ApplicationServices;
using AudibleUtilities;
using LibationAvalonia.Dialogs.Login;
using Avalonia.Collections;
-using LibationSearchEngine;
-using Octokit.Internal;
namespace LibationAvalonia.ViewModels
{
@@ -62,6 +60,7 @@ namespace LibationAvalonia.ViewModels
{
var existingSeriesEntries = SOURCE.SeriesEntries().ToList();
+ FilteredInGridEntries?.Clear();
SOURCE.Clear();
SOURCE.AddRange(CreateGridEntries(dbBooks));
@@ -164,7 +163,7 @@ namespace LibationAvalonia.ViewModels
return FilteredInGridEntries.Contains(item);
}
- private static List QueryResults(List entries, string searchString)
+ private static List QueryResults(IEnumerable entries, string searchString)
{
if (string.IsNullOrEmpty(searchString)) return null;
diff --git a/Source/LibationAvalonia/Views/MainWindow.Export.cs b/Source/LibationAvalonia/Views/MainWindow.Export.cs
index 6759b09d..3e8cdc87 100644
--- a/Source/LibationAvalonia/Views/MainWindow.Export.cs
+++ b/Source/LibationAvalonia/Views/MainWindow.Export.cs
@@ -26,10 +26,23 @@ namespace LibationAvalonia.Views
ShowOverwritePrompt = true,
FileTypeChoices = new FilePickerFileType[]
{
- new("Excel Workbook (*.xlsx)") { Patterns = new[] { "*.xlsx" } },
- new("CSV files (*.csv)") { Patterns = new[] { "*.csv" } },
- new("JSON files (*.json)") { Patterns = new[] { "*.json" } },
- new("All files (*.*)") { Patterns = new[] { "*" } },
+ new("Excel Workbook (*.xlsx)")
+ {
+ Patterns = new[] { "*.xlsx" },
+ //https://gist.github.com/RhetTbull/7221ef3cfd9d746f34b2550d4419a8c2
+ AppleUniformTypeIdentifiers = new[] { "org.openxmlformats.spreadsheetml.sheet" }
+ },
+ new("CSV files (*.csv)")
+ {
+ Patterns = new[] { "*.csv" },
+ AppleUniformTypeIdentifiers = new[] { "public.comma-separated-values-text" }
+ },
+ new("JSON files (*.json)")
+ {
+ Patterns = new[] { "*.json" },
+ AppleUniformTypeIdentifiers = new[] { "public.json" }
+ },
+ new("All files (*.*)") { Patterns = new[] { "*" } }
}
};
diff --git a/Source/LibationAvalonia/Views/MainWindow.Update.cs b/Source/LibationAvalonia/Views/MainWindow.Update.cs
index a6858d94..e466c41a 100644
--- a/Source/LibationAvalonia/Views/MainWindow.Update.cs
+++ b/Source/LibationAvalonia/Views/MainWindow.Update.cs
@@ -20,13 +20,16 @@ namespace LibationAvalonia.Views
{
if (upgradeProperties.ZipUrl is null)
{
- Serilog.Log.Logger.Information("Download link for new version not found");
+ 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();
@@ -55,6 +58,9 @@ namespace LibationAvalonia.Views
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)
@@ -68,7 +74,9 @@ namespace LibationAvalonia.Views
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)
{
diff --git a/Source/LibationFileManager/Configuration.Environment.cs b/Source/LibationFileManager/Configuration.Environment.cs
index f18c9944..1eec30a1 100644
--- a/Source/LibationFileManager/Configuration.Environment.cs
+++ b/Source/LibationFileManager/Configuration.Environment.cs
@@ -6,16 +6,25 @@ using System.Threading.Tasks;
namespace LibationFileManager
{
- public partial class Configuration
+ [Flags]
+ public enum OS
+ {
+ Unknown,
+ Windows = 0x100000,
+ Linux = 0x200000,
+ MacOS = 0x400000,
+ }
+
+ public partial class Configuration
{
- public static bool IsWindows { get; } = OperatingSystem.IsWindows();
+ public static bool IsWindows { get; } = OperatingSystem.IsWindows();
public static bool IsLinux { get; } = OperatingSystem.IsLinux();
public static bool IsMacOs { get; } = OperatingSystem.IsMacOS();
- public static string OS { get; }
- = IsLinux ? "Linux"
- : IsMacOs ? "MacOS"
- : IsWindows ? "Windows"
- : "UNKNOWN_OS";
+ public static OS OS { get; }
+ = IsLinux ? OS.Linux
+ : IsMacOs ? OS.MacOS
+ : IsWindows ? OS.Windows
+ : OS.Unknown;
}
}
diff --git a/Source/LibationFileManager/Configuration.KnownDirectories.cs b/Source/LibationFileManager/Configuration.KnownDirectories.cs
index 5ad6a75e..d989b616 100644
--- a/Source/LibationFileManager/Configuration.KnownDirectories.cs
+++ b/Source/LibationFileManager/Configuration.KnownDirectories.cs
@@ -9,8 +9,9 @@ namespace LibationFileManager
{
public partial class Configuration
{
- public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
- public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Exe.FileLocationOnDisk), LIBATION_FILES_KEY));
+ public static string ProcessDirectory { get; } = Path.GetDirectoryName(Exe.FileLocationOnDisk);
+ public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
+ public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(ProcessDirectory, LIBATION_FILES_KEY));
public static string MyDocs => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
public static string WinTemp => Path.GetFullPath(Path.Combine(Path.GetTempPath(), "Libation"));
public static string UserProfile => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Libation"));
diff --git a/Source/LibationFileManager/Configuration.LibationFiles.cs b/Source/LibationFileManager/Configuration.LibationFiles.cs
index 7608f6c8..553c6a5f 100644
--- a/Source/LibationFileManager/Configuration.LibationFiles.cs
+++ b/Source/LibationFileManager/Configuration.LibationFiles.cs
@@ -76,7 +76,7 @@ namespace LibationFileManager
//Possible appsettings.json locations, in order of preference.
string[] possibleAppsettingsFiles = new[]
{
- Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), appsettings_filename),
+ Path.Combine(ProcessDirectory, appsettings_filename),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Libation", appsettings_filename),
Path.Combine(UserProfile, appsettings_filename),
Path.Combine(Path.GetTempPath(), "Libation", appsettings_filename)
diff --git a/Source/LibationFileManager/InteropFactory.cs b/Source/LibationFileManager/InteropFactory.cs
index ac2928a9..eddc97be 100644
--- a/Source/LibationFileManager/InteropFactory.cs
+++ b/Source/LibationFileManager/InteropFactory.cs
@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
-using System.Threading;
using Dinah.Core;
namespace LibationFileManager
@@ -25,21 +23,29 @@ namespace LibationFileManager
instance ??=
InteropFunctionsType is null
? new NullInteropFunctions()
- //: values is null || values.Length == 0 ? Activator.CreateInstance(InteropFunctionsType) as IInteropFunctions
: Activator.CreateInstance(InteropFunctionsType, values) as IInteropFunctions;
return instance;
}
- #region load types
+ #region load types
- public static Func MatchesOS { get; }
+ private const string CONFIG_APP_ENDING = "ConfigApp.dll";
+
+ public static Func MatchesOS { get; }
= Configuration.IsWindows ? a => Path.GetFileName(a).StartsWithInsensitive("win")
: Configuration.IsLinux ? a => Path.GetFileName(a).StartsWithInsensitive("linux")
: Configuration.IsMacOs ? a => Path.GetFileName(a).StartsWithInsensitive("mac") || Path.GetFileName(a).StartsWithInsensitive("osx")
: _ => false;
- private const string CONFIG_APP_ENDING = "ConfigApp.dll";
- private static List ModuleList { get; } = new();
+ private static readonly EnumerationOptions enumerationOptions = new()
+ {
+ MatchType = MatchType.Simple,
+ MatchCasing = MatchCasing.CaseInsensitive,
+ IgnoreInaccessible = true,
+ RecurseSubdirectories = false,
+ ReturnSpecialDirectories = false
+ };
+
static InteropFactory()
{
// searches file names for potential matches; doesn't run anything
@@ -52,94 +58,36 @@ namespace LibationFileManager
return;
}
- /*
- * Commented code used to locate assemblies from the *ConfigApp.exe's module list.
- * Use this method to locate dependencies when they are not in Libation's program files directory.
-#if DEBUG
-
- // runs the exe and gets the exe's loaded modules
- ModuleList = LoadModuleList(Path.GetFileNameWithoutExtension(configApp))
- .OrderBy(x => x.ModuleName)
- .ToList();
-#endif
- */
-
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
var configAppAssembly = Assembly.LoadFrom(configApp);
var type = typeof(IInteropFunctions);
InteropFunctionsType = configAppAssembly
.GetTypes()
- .FirstOrDefault(t => type.IsAssignableFrom(t));
+ .FirstOrDefault(type.IsAssignableFrom);
}
private static string getOSConfigApp()
{
- var here = Path.GetDirectoryName(Environment.ProcessPath);
-
// find '*ConfigApp.dll' files
var appName =
- Directory.EnumerateFiles(here, $"*{CONFIG_APP_ENDING}", SearchOption.TopDirectoryOnly)
- // sanity check. shouldn't ever be true
- .Except(new[] { Environment.ProcessPath })
+ Directory.EnumerateFiles(Configuration.ProcessDirectory, $"*{CONFIG_APP_ENDING}", enumerationOptions)
.FirstOrDefault(exe => MatchesOS(exe));
return appName;
}
- /*
- * Use this method to locate dependencies when they are not in Libation's program files directory.
- *
- private static List LoadModuleList(string exeName)
- {
- var proc = new Process
- {
- StartInfo = new()
- {
- FileName = exeName,
- RedirectStandardInput = true,
- RedirectStandardOutput = true,
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- UseShellExecute = false
- }
- };
-
- var waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
-
- proc.OutputDataReceived += (_, _) => waitHandle.Set();
- proc.Start();
- proc.BeginOutputReadLine();
-
- //Let the win process know we're ready to receive its standard output
- proc.StandardInput.WriteLine();
-
- if (!waitHandle.WaitOne(2000))
- throw new Exception("Failed to start program");
-
- //The win process has finished loading and is now waiting inside Main().
- //Copy it process module list.
- var modules = proc.Modules.Cast().ToList();
-
- //Let the win process know we're done reading its module list
- proc.StandardInput.WriteLine();
-
- return modules;
- }
- */
-
private static Dictionary lowEffortCache { get; } = new();
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
- // e.g. "System.Windows.Forms, Version=6.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
- var asmName = args.Name.Split(',')[0] + ".dll";
- var here = Path.GetDirectoryName(Environment.ProcessPath);
+ var asmName = new AssemblyName(args.Name);
+ var here = Configuration.ProcessDirectory;
var key = $"{asmName}|{here}";
if (lowEffortCache.TryGetValue(key, out var value))
return value;
- var assembly = CurrentDomain_AssemblyResolve_internal(asmName: asmName, here: here);
+ var assembly = CurrentDomain_AssemblyResolve_internal(asmName, here: here);
lowEffortCache[key] = assembly;
//Let the runtime handle any dll not found exceptions.
@@ -149,27 +97,22 @@ namespace LibationFileManager
return assembly;
}
- private static Assembly CurrentDomain_AssemblyResolve_internal(string asmName, string here)
+ private static Assembly CurrentDomain_AssemblyResolve_internal(AssemblyName asmName, string here)
{
/*
- * Commented code used to locate assemblies from the *ConfigApp.exe's module list.
- * Use this method to locate dependencies when they are not in Libation's program files directory.
- #if DEBUG
-
- var modulePath = ModuleList.SingleOrDefault(m => m.ModuleName.EqualsInsensitive(asmName))?.FileName;
- #else
- */
-
- // find the requested assembly in the program files directory
+ * Find the requested assembly in the program files directory.
+ * Assumes that all assemblies are in this application's directory.
+ * If they're not (e.g. the app is not self-contained), you will need
+ * to located them. The original way of doing this was to execute the
+ * config app, wait for the runtime to load all dependencies, and
+ * then seach the Process.Modules for the assembly name. Code for
+ * this approach is still in the _Demos projects.
+ */
var modulePath =
- Directory.EnumerateFiles(here, asmName, SearchOption.TopDirectoryOnly)
+ Directory.EnumerateFiles(here, $"{asmName.Name}.dll", enumerationOptions)
.SingleOrDefault();
-//#endif
- if (modulePath is null)
- return null;
-
- return Assembly.LoadFrom(modulePath);
+ return modulePath is null ? null : Assembly.LoadFrom(modulePath);
}
#endregion
diff --git a/Source/LibationWinForms/Program.cs b/Source/LibationWinForms/Program.cs
index 333114e3..28148e21 100644
--- a/Source/LibationWinForms/Program.cs
+++ b/Source/LibationWinForms/Program.cs
@@ -30,7 +30,7 @@ namespace LibationWinForms
ApplicationConfiguration.Initialize();
- AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.WindowsClassic);
+ AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.Variety.Classic);
//***********************************************//
// //
diff --git a/Source/LoadByOS/LinuxConfigApp/Libation.desktop b/Source/LoadByOS/LinuxConfigApp/Libation.desktop
index 0b935ce8..2890481c 100644
--- a/Source/LoadByOS/LinuxConfigApp/Libation.desktop
+++ b/Source/LoadByOS/LinuxConfigApp/Libation.desktop
@@ -1,6 +1,6 @@
[Desktop Entry]
Name=Libation
-Exec=libation
+Exec=/usr/bin/libation
Icon=libation
Comment=Liberate your Audiobooks
Terminal=false
diff --git a/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj b/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj
index 9d9a1b3e..855b7034 100644
--- a/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj
+++ b/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj
@@ -35,27 +35,15 @@
-
- True
- True
- Resources.resx
-
-
-
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
-
-
-
-
-
+
Always
Always
+
+ Always
+
-
\ No newline at end of file
+
diff --git a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs
index 7c36dda8..bd6458b0 100644
--- a/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs
+++ b/Source/LoadByOS/LinuxConfigApp/LinuxInterop.cs
@@ -3,41 +3,40 @@ using System.Diagnostics;
namespace LinuxConfigApp
{
- internal class LinuxInterop : IInteropFunctions
- {
+ internal class LinuxInterop : IInteropFunctions
+ {
//Different terminal apps possibly installed on a linux system
// [0] console executable
// [1] argument to set the concole's title
// [2] argument to pass a command to be executed to the terminal
static readonly string[][] consoleCommands =
- {
- new[] {"konsole", "--title", "-e"},
- new[] {"gnome-terminal", "--title", "--"},
- new[] {"mate-terminal", "--title", "-x"},
- new[] {"xterm", "-T", "-e"},
- };
+ {
+ new[] {"konsole", "--title", "-e"},
+ new[] {"gnome-terminal", "--title", "--"},
+ new[] {"mate-terminal", "--title", "-x"},
+ new[] {"xterm", "-T", "-e"},
+ };
public LinuxInterop() { }
- public LinuxInterop(params object[] values) { }
+ public LinuxInterop(params object[] values) { }
- public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
- public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
+ public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
+ public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
- //only run the audo updater is 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)
+ //only run the auto updater 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)
{
RunAsRoot("apt", $"install '{updateBundle}'");
}
public Process RunAsRoot(string exe, string args)
- {
+ {
//cribbed this script from VirtualBox's guest additions installer.
//It's designed to launch the system's gui superuser password
//prompt across multiple distributions and desktop environments.
- const string runasroot = "/tmp/runasroot.sh";
- File.WriteAllBytes(runasroot, Properties.Resources.runasroot);
+ const string runasroot = "runasroot.sh";
string command = $"{exe ?? ""} {args ?? ""}".Trim();
@@ -50,24 +49,23 @@ namespace LinuxConfigApp
ArgumentList =
{
console[1],
- $"Running '{exe}' as root",
+ $"Running '{exe}' as root", // console title
console[2],
"/bin/sh",
- runasroot,
- "Installing libation.deb",
- command,
- $"Please run '{command}' manually"
+ Path.Combine(Configuration.ProcessDirectory, runasroot), //script file
+ "Installing libation.deb", //command title
+ command, // command to execute vis /bin/sh
+ $"Please run '{command}' manually" // error message to display in the terminal
}
};
-
try
{
return Process.Start(psi);
}
catch { }
}
- return null;
+ throw new PlatformNotSupportedException($"Could not start any of the supported terminals: {string.Join(", ", consoleCommands.Select(c => c[0]))}");
}
}
}
diff --git a/Source/LoadByOS/LinuxConfigApp/Program.cs b/Source/LoadByOS/LinuxConfigApp/Program.cs
index e98c8ff9..81d89b03 100644
--- a/Source/LoadByOS/LinuxConfigApp/Program.cs
+++ b/Source/LoadByOS/LinuxConfigApp/Program.cs
@@ -1,11 +1,7 @@
-using AppScaffolding;
-
-namespace LinuxConfigApp
+namespace LinuxConfigApp
{
- class Program : OSConfigBase
+ class Program
{
- public override Type InteropFunctionsType => typeof(LinuxInterop);
-
- static void Main() => new Program().Run();
+ static void Main() { }
}
}
diff --git a/Source/LoadByOS/LinuxConfigApp/Properties/Resources.Designer.cs b/Source/LoadByOS/LinuxConfigApp/Properties/Resources.Designer.cs
deleted file mode 100644
index b48060ab..00000000
--- a/Source/LoadByOS/LinuxConfigApp/Properties/Resources.Designer.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace LinuxConfigApp.Properties {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LinuxConfigApp.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized resource of type System.Byte[].
- ///
- internal static byte[] runasroot {
- get {
- object obj = ResourceManager.GetObject("runasroot", resourceCulture);
- return ((byte[])(obj));
- }
- }
- }
-}
diff --git a/Source/LoadByOS/LinuxConfigApp/Properties/Resources.resx b/Source/LoadByOS/LinuxConfigApp/Properties/Resources.resx
deleted file mode 100644
index ef269a0f..00000000
--- a/Source/LoadByOS/LinuxConfigApp/Properties/Resources.resx
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
-
- ..\Resources\runasroot.sh;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/Source/LoadByOS/LinuxConfigApp/glass-with-glow_256.svg b/Source/LoadByOS/LinuxConfigApp/glass-with-glow_256.svg
deleted file mode 100644
index df935c14..00000000
--- a/Source/LoadByOS/LinuxConfigApp/glass-with-glow_256.svg
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
diff --git a/Source/LoadByOS/LinuxConfigApp/libation_glass.svg b/Source/LoadByOS/LinuxConfigApp/libation_glass.svg
new file mode 100644
index 00000000..db08f07f
--- /dev/null
+++ b/Source/LoadByOS/LinuxConfigApp/libation_glass.svg
@@ -0,0 +1,28 @@
+
diff --git a/Source/LoadByOS/LinuxConfigApp/Resources/runasroot.sh b/Source/LoadByOS/LinuxConfigApp/runasroot.sh
similarity index 100%
rename from Source/LoadByOS/LinuxConfigApp/Resources/runasroot.sh
rename to Source/LoadByOS/LinuxConfigApp/runasroot.sh
diff --git a/Source/LoadByOS/MacOSConfigApp/Info.plist b/Source/LoadByOS/MacOSConfigApp/Info.plist
index 90cbcaf1..b9d85062 100644
--- a/Source/LoadByOS/MacOSConfigApp/Info.plist
+++ b/Source/LoadByOS/MacOSConfigApp/Info.plist
@@ -7,6 +7,10 @@
Libation
CFBundleName
Libation
+ LSArchitecturePriority
+ ARCHITECTURE_STRING
+ LSMinimumSystemVersion
+ 10.15.0
CFBundleIdentifier
org.libation.macos
NSHighResolutionCapable
diff --git a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs
index 8bf6f9a3..a8993345 100644
--- a/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs
+++ b/Source/LoadByOS/MacOSConfigApp/MacOSInterop.cs
@@ -21,7 +21,7 @@ namespace MacOSConfigApp
Serilog.Log.Information($"Extracting update bundle to {AppPath}");
//tar wil overwrite existing without elevated privileges
- Process.Start("tar", $"-xzf \"{updateBundle}\" -C \"/Applications\"").WaitForExit();
+ Process.Start("tar", $"-xf \"{updateBundle}\" -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/MacOSConfigApp/Program.cs b/Source/LoadByOS/MacOSConfigApp/Program.cs
index bab9d875..dcc8ffbc 100644
--- a/Source/LoadByOS/MacOSConfigApp/Program.cs
+++ b/Source/LoadByOS/MacOSConfigApp/Program.cs
@@ -1,11 +1,7 @@
-using AppScaffolding;
-
-namespace MacOSConfigApp
+namespace MacOSConfigApp
{
- class Program : OSConfigBase
+ class Program
{
- public override Type InteropFunctionsType => typeof(MacOSInterop);
-
- static void Main() => new Program().Run();
+ static void Main() { }
}
}
diff --git a/Source/LoadByOS/WindowsConfigApp/Program.cs b/Source/LoadByOS/WindowsConfigApp/Program.cs
index 1786ce7b..72473a31 100644
--- a/Source/LoadByOS/WindowsConfigApp/Program.cs
+++ b/Source/LoadByOS/WindowsConfigApp/Program.cs
@@ -1,18 +1,7 @@
-using AppScaffolding;
-
namespace WindowsConfigApp
{
- class Program : OSConfigBase
+ class Program
{
- public override Type InteropFunctionsType => typeof(WinInterop);
- public override Type[] ReferencedTypes => new Type[]
- {
- typeof(Bitmap),
- typeof(Dinah.Core.WindowsDesktop.GitClient),
- typeof(Accessibility.IAccIdentity),
- typeof(Microsoft.Win32.SystemEvents)
- };
-
- static void Main() => new Program().Run();
+ static void Main() { }
}
}
\ No newline at end of file