mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-01-03 11:28:30 -05:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdd5f229d3 | ||
|
|
29edfb7c3f | ||
|
|
c0cb454d45 | ||
|
|
970a77c9e9 | ||
|
|
3488c8e0f5 | ||
|
|
5133720cc8 | ||
|
|
4150746f45 | ||
|
|
3a95e1e72f | ||
|
|
c74e26e1af | ||
|
|
ae54c95d46 | ||
|
|
56e6bd164b | ||
|
|
9b28bdceaa | ||
|
|
b7f7d9004d | ||
|
|
1be0991e62 | ||
|
|
ff8a2e59c5 | ||
|
|
453904261b | ||
|
|
f9340db90a | ||
|
|
5cd329dd26 | ||
|
|
b2a882b79d | ||
|
|
75df78a2f7 | ||
|
|
3ad52cbecc | ||
|
|
27b2fe741c | ||
|
|
d19fe2250c | ||
|
|
d16d0c8de2 | ||
|
|
c213d5d9f6 | ||
|
|
c73a023572 | ||
|
|
67389917fd | ||
|
|
b3264d5f42 | ||
|
|
962d9b550f | ||
|
|
91ce7272ae | ||
|
|
2f64ca6856 | ||
|
|
cfe5db436c | ||
|
|
3653fc8094 | ||
|
|
662c0ec871 | ||
|
|
44d39eabdb | ||
|
|
a0550d5c97 | ||
|
|
14eaca6d45 | ||
|
|
93ccc206ef | ||
|
|
d3f0fd711e | ||
|
|
3f4604e877 | ||
|
|
c316709af8 | ||
|
|
221d5c7f1c | ||
|
|
5a86a1a27b | ||
|
|
dc5e55de68 | ||
|
|
ee37864a42 | ||
|
|
efe347667c | ||
|
|
f27a18bdbb | ||
|
|
d1834659d9 | ||
|
|
7842b521d7 | ||
|
|
0822f0229d | ||
|
|
26aee4d29d | ||
|
|
17a80a23a8 | ||
|
|
e26fc9ca62 | ||
|
|
a03ccf1143 | ||
|
|
bb8dd615db | ||
|
|
9022a2889f | ||
|
|
ef049a3b02 | ||
|
|
77409750aa | ||
|
|
1702130b01 | ||
|
|
b6d1a7e3ba | ||
|
|
2907ba5c13 | ||
|
|
6df6c79ac8 | ||
|
|
3a9ca5d827 | ||
|
|
e1e663e327 | ||
|
|
4b00d5fd84 | ||
|
|
02dbf8aad0 | ||
|
|
8326389f5c | ||
|
|
34535b3ce1 | ||
|
|
7e5366ab95 | ||
|
|
690de9bc5c | ||
|
|
c976aa2bb2 | ||
|
|
27f659285d | ||
|
|
423a5e7720 | ||
|
|
9152e12fe1 | ||
|
|
f471c53139 | ||
|
|
66d055bb90 | ||
|
|
2bbb35363a | ||
|
|
1d3687cf9e | ||
|
|
daf925157f | ||
|
|
40eec9e674 | ||
|
|
6f0782053e | ||
|
|
04ad033ba0 | ||
|
|
f12f8ba3ee | ||
|
|
cc6feb21ff | ||
|
|
c4f2ec428d | ||
|
|
59689cb647 | ||
|
|
d7f3758ebc | ||
|
|
49775b019c | ||
|
|
e55e969349 | ||
|
|
42a93bfac1 | ||
|
|
f86c77a546 | ||
|
|
88c35e2a56 | ||
|
|
b405e8b6b2 | ||
|
|
92d283187d | ||
|
|
51b8cfe71f | ||
|
|
c80da5357b | ||
|
|
736d7c4a5f |
8
.github/workflows/build-linux.yml
vendored
8
.github/workflows/build-linux.yml
vendored
@@ -33,7 +33,7 @@ on:
|
||||
|
||||
env:
|
||||
DOTNET_CONFIGURATION: 'Release'
|
||||
DOTNET_VERSION: '7.0.x'
|
||||
DOTNET_VERSION: '8.0.x'
|
||||
RELEASE_NAME: 'chardonnay'
|
||||
|
||||
jobs:
|
||||
@@ -41,9 +41,9 @@ jobs:
|
||||
name: '${{ inputs.OS }}-${{ inputs.architecture }}'
|
||||
runs-on: ${{ inputs.runs_on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
env:
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
echo "artifact=${artifact}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Publish bundle
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.bundle.outputs.artifact }}
|
||||
path: ./Source/bin/Publish/bundle/${{ steps.bundle.outputs.artifact }}
|
||||
|
||||
8
.github/workflows/build-windows.yml
vendored
8
.github/workflows/build-windows.yml
vendored
@@ -18,7 +18,7 @@ on:
|
||||
|
||||
env:
|
||||
DOTNET_CONFIGURATION: 'Release'
|
||||
DOTNET_VERSION: '7.0.x'
|
||||
DOTNET_VERSION: '8.0.x'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -35,9 +35,9 @@ jobs:
|
||||
release_name: classic
|
||||
prefix: Classic-
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
env:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
Compress-Archive -Path "${bin_dir}*" -DestinationPath "$artifact.zip"
|
||||
|
||||
- name: Publish artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.zip.outputs.artifact }}.zip
|
||||
path: ./Source/bin/Publish/${{ steps.zip.outputs.artifact }}.zip
|
||||
|
||||
10
.github/workflows/docker.yml
vendored
10
.github/workflows/docker.yml
vendored
@@ -24,22 +24,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.docker_username }}
|
||||
password: ${{ secrets.docker_token }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
build-args: 'FOLDER_NAME=Linux-chardonnay'
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -39,21 +39,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Release
|
||||
id: release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
name: Libation v${{ needs.prerelease.outputs.version }}
|
||||
name: Libation ${{ needs.prerelease.outputs.version }}
|
||||
body: <Put a body here>
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
- name: Upload release assets
|
||||
uses: dwenegar/upload-release-assets@v1
|
||||
uses: dwenegar/upload-release-assets@v2
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
with:
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Dockerfile
|
||||
FROM mcr.microsoft.com/dotnet/sdk:7.0 as build-env
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 as build-env
|
||||
|
||||
COPY Source /Source
|
||||
RUN dotnet publish -c Release -o /Source/bin/Publish/Linux-chardonnay /Source/LibationCli/LibationCli.csproj -p:PublishProfile=/Source/LibationCli/Properties/PublishProfiles/LinuxProfile.pubxml
|
||||
COPY Docker/liberate.sh /Source/bin/Publish/Linux-chardonnay
|
||||
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/runtime:7.0
|
||||
FROM mcr.microsoft.com/dotnet/runtime:8.0
|
||||
|
||||
ENV SLEEP_TIME "30m"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
### Disclaimer
|
||||
|
||||
# Disclaimer
|
||||
The docker image is provided as-is. We hope it can be useful to you but it is not officially supported.
|
||||
|
||||
### Setup
|
||||
@@ -12,7 +13,15 @@ In order to use the docker image, you'll need to provide it with a copy of the `
|
||||
|
||||
In Settings.json, make the following changes:
|
||||
* Change `Books` to `/data`
|
||||
* Change `InProgress` to `/tmp`
|
||||
* Change `InProgress` to `/tmp` *
|
||||
|
||||
*You may have to paste the following at the end of your your Settings.json file if `InProgess` is not present:
|
||||
|
||||
```
|
||||
"InProgress": "/tmp"
|
||||
```
|
||||

|
||||
|
||||
|
||||
### Running
|
||||
Once the configuration files are copied and edited, the docker image can be run with the following command.
|
||||
|
||||
38
Documentation/FrequentlyAskedQuestions.md
Normal file
38
Documentation/FrequentlyAskedQuestions.md
Normal file
@@ -0,0 +1,38 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
## Q: What's the difference between 'Classic' and 'Chardonnay'?
|
||||
|
||||
**A:** First and most importantly: Classic and Chardonnay have the exact same features.
|
||||
|
||||
* **Classic** is Windows only. Its older 'grey boxes' look has a compact design which allows for more information on the screen. Notably, Classic was written using an older, more mature technology which has built-in support for screenreaders.
|
||||
|
||||
* **Chardonnay** is available for Windows, Mac, and Linux. Its modern design has a more open look and feel.
|
||||
|
||||
## Q: Now that I've downloaded my books, how can I listen to them?
|
||||
|
||||
**A:** You can use any app which plays m4b files (or mp3 files if you used that setting). Here are just a few ideas. Disclaimer: I have no affiliation with any of these companies:
|
||||
|
||||
* iOS: [BookPlayer](https://apps.apple.com/us/app/bookplayer/id1138219998)
|
||||
* iOS: [Bound](https://apps.apple.com/us/app/bound-audiobook-player/id1041727137)
|
||||
* Android: [Smart AudioBook Player](https://play.google.com/store/apps/details?id=ak.alizandro.smartaudiobookplayer&hl=en_US&gl=US)
|
||||
* Android: [Listen](https://play.google.com/store/apps/details?id=ru.litres.android.audio&hl=en_US&gl=US)
|
||||
* Desktop: [VLC](https://www.videolan.org/)
|
||||
* Windows Desktop: [Audibly](https://github.com/rstewa/Audibly) -- a desktop player build specifically for audiobooks
|
||||
|
||||
Self-hosting online:
|
||||
|
||||
* [audiobookshelf](https://www.audiobookshelf.org). On [reddit](https://www.reddit.com/r/audiobookshelf/)
|
||||
* [plex](https://www.plex.tv/). Listen with [Prologue](https://prologue.audio/) (iOS)
|
||||
|
||||
## Q: How do I use Libation with a South Africa account?
|
||||
|
||||
**A:** Like many countries, amazon gives South Africa it's own amazon site. [Unlike many other regions](https://www.audible.com/ep/country-selector) there is not South Africa specific audible site. Use `US` for your region -- ie: audible.com.
|
||||
|
||||
(Not exactly a *frequently* asked question but it's come up more than once)
|
||||
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
### Install and Run Libation on Ubuntu
|
||||
|
||||
# Install and Run Libation on Ubuntu
|
||||
|
||||
New Libation releases are automatically packed into .deb and .rpm package and are available from the Libation repository's releases page.
|
||||
|
||||
|
||||
Run this command in your terminal to dowbnload and install Libation, replacing the url with the latest Libation package url:
|
||||
Run this command in your terminal to download and install Libation, replacing the url with the latest Libation package url:
|
||||
|
||||
- Debian
|
||||
```Console
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
# Run Libation on MacOS
|
||||
This walkthrough should get you up and running with Libation on your Mac.
|
||||
|
||||
@@ -15,13 +16,40 @@ This walkthrough should get you up and running with Libation on your Mac.
|
||||
- Apple Silicon (M1, M2, ...): `Libation.9.4.2-macOS-chardonnay-`**arm64**`.tgz`
|
||||
- Intel: `Libation.x.x.x-macOS-chardonnay-`**x64**`.tgz`
|
||||
- Move the extracted Libation app bundle to your applications folder.
|
||||
- Open a terminal (Go > Utilities > Terminal)
|
||||
- Copy/paste/run the following command (you'll be prompted to enter your password)
|
||||
|
||||
- Right-click on Libation and then click on open
|
||||
- The first time, it will not immediately show you an option to open it. Just dismiss the dialog and do the same thing again (right-click -> open) then you will get an option to run the unsigned application. This takes about 10 seconds.
|
||||
|
||||
## If this doesn't work
|
||||
|
||||
You can add Libation as a safe app without touching Gatekeeper.
|
||||
|
||||
- Copy/paste/run the following command. Adjust the file path to the Libation.app on your computer if necessary.
|
||||
|
||||
```Console
|
||||
xattr -r -d com.apple.quarantine ~/Downloads/Libation.app
|
||||
```
|
||||
- Close the terminal and use Libation!
|
||||
|
||||
## If this still doesn't work
|
||||
|
||||
- Copy/paste/run the following command (you'll be prompted to enter your Mac password)
|
||||
|
||||
```Console
|
||||
sudo spctl --master-disable && sudo spctl --add --label "Libation" /Applications/Libation.app && open /Applications/Libation.app && sudo spctl --master-enable
|
||||
```
|
||||
- Close the terminal and use Libation!
|
||||
```
|
||||
|
||||
* Close the terminal and use Libation!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If Libation fails to start after completing the above steps, try the following:
|
||||
|
||||
1. Right-click the Libation app in your applications folder and select _Show Package Contents_
|
||||
2. Open the `Contents` folder and then the `MacOS` folder.
|
||||
3. Find the file named `Libation`, right-click it, and then select _Open_.
|
||||
|
||||
Libation _should_ launch, and you should now be able to open Libation by just double-clicking the app bundle in your applications folder.
|
||||
|
||||
|
||||
## Running Hangover
|
||||
|
||||
@@ -30,7 +58,7 @@ Libation comes with a recovery app called Hangover. You can start it by running
|
||||
open /Applications/Libation.app --args hangover
|
||||
```
|
||||
|
||||
## Runnign LibationCli
|
||||
## Running LibationCli
|
||||
|
||||
Libation comes with a command-line interface. Unfortunately, due to the way apps are sandboxed on mac, its use is somewhat limited. To open a new sandboxed terminal in LibationCli's directory, run the following command:
|
||||
```Console
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
# Naming Templates
|
||||
File and Folder names can be customized using Libation's built-in tag template naming engine. To edit how folder and file names are created, go to Settings \> Download/Decrypt and edit the naming templates. If you're splitting your audiobook into multiple files by chapter, you can also use a custom template to set each chapter's title metadata tag by editing the template in Settings \> Audio File Options.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
# Table of Contents
|
||||
|
||||
- [Audible audiobook manager](#audible-audiobook-manager)
|
||||
@@ -31,6 +33,7 @@
|
||||
- [Custom File Naming](Documentation/NamingTemplates.md)
|
||||
- [Command Line Interface](Documentation/Advanced.md#command-line-interface)
|
||||
- [Docker](Documentation/Docker.md)
|
||||
- [Frequently Asked Questions](Documentation/FrequentlyAskedQuestions.md)
|
||||
|
||||
## Getting started
|
||||
|
||||
|
||||
@@ -109,9 +109,6 @@ ln -s /usr/lib/libation/LibationCli /usr/bin/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
|
||||
# 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
|
||||
|
||||
echo "Creating control file..."
|
||||
|
||||
@@ -94,8 +94,6 @@ install * %{buildroot}%{_libdir}/%{name}/
|
||||
|
||||
if [ \$1 -eq 1 ] ; then
|
||||
# Initial installation
|
||||
touch %{_libdir}/%{name}/appsettings.json
|
||||
chmod 666 %{_libdir}/%{name}/appsettings.json
|
||||
|
||||
ln -s %{_libdir}/%{name}/Libation %{_bindir}/libation
|
||||
ln -s %{_libdir}/%{name}/Hangover %{_bindir}/hangover
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@@ -13,7 +13,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AAXClean.Codecs" Version="1.1.0" />
|
||||
<PackageReference Include="AAXClean.Codecs" Version="1.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>11.0.0.1</Version>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>11.3.15.1</Version>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Octokit" Version="7.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.ZipFile" Version="1.0.1" />
|
||||
<PackageReference Include="Octokit" Version="11.0.1" />
|
||||
<!-- Do not remove unused Serilog.Sinks -->
|
||||
<!-- Only ZipFile sink is currently used. By user request (June 2024) others packages are included for experimental use. -->
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.ZipFile" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" />
|
||||
@@ -19,4 +23,4 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -9,9 +9,9 @@ using Dinah.Core;
|
||||
using Dinah.Core.IO;
|
||||
using Dinah.Core.Logging;
|
||||
using LibationFileManager;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serilog;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AppScaffolding
|
||||
{
|
||||
@@ -124,6 +124,9 @@ namespace AppScaffolding
|
||||
{ "MinimumLevel", "Information" },
|
||||
{ "WriteTo", new JArray
|
||||
{
|
||||
// ABOUT SINKS
|
||||
// Only ZipFile sink is currently used. By user request (June 2024) others packages are included for experimental use.
|
||||
|
||||
// new JObject { {"Name", "Console" } }, // this has caused more problems than it's solved
|
||||
new JObject
|
||||
{
|
||||
@@ -227,12 +230,20 @@ namespace AppScaffolding
|
||||
|
||||
// begin logging session with a form feed
|
||||
Log.Logger.Information("\r\n\f");
|
||||
Log.Logger.Information("Begin. {@DebugInfo}", new
|
||||
|
||||
static int fileCount(FileManager.LongPath longPath)
|
||||
{
|
||||
try { return FileManager.FileUtility.SaferEnumerateFiles(longPath).Count(); }
|
||||
catch { return -1; }
|
||||
}
|
||||
|
||||
Log.Logger.Information("Begin. {@DebugInfo}", new
|
||||
{
|
||||
AppName = EntryAssembly.GetName().Name,
|
||||
Version = BuildVersion.ToString(),
|
||||
ReleaseIdentifier,
|
||||
Configuration.OS,
|
||||
Environment.OSVersion,
|
||||
InteropFactory.InteropFunctionsType,
|
||||
Mode = mode,
|
||||
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
|
||||
@@ -242,6 +253,7 @@ namespace AppScaffolding
|
||||
LogLevel_Error_Enabled = Log.Logger.IsErrorEnabled(),
|
||||
LogLevel_Fatal_Enabled = Log.Logger.IsFatalEnabled(),
|
||||
|
||||
config.AutoScan,
|
||||
config.BetaOptIn,
|
||||
config.UseCoverAsFolderIcon,
|
||||
config.LibationFiles,
|
||||
@@ -250,10 +262,12 @@ namespace AppScaffolding
|
||||
config.InProgress,
|
||||
|
||||
AudibleFileStorage.DownloadsInProgressDirectory,
|
||||
DownloadsInProgressFiles = FileManager.FileUtility.SaferEnumerateFiles(AudibleFileStorage.DownloadsInProgressDirectory).Count(),
|
||||
DownloadsInProgressFiles = fileCount(AudibleFileStorage.DownloadsInProgressDirectory),
|
||||
|
||||
AudibleFileStorage.DecryptInProgressDirectory,
|
||||
DecryptInProgressFiles = FileManager.FileUtility.SaferEnumerateFiles(AudibleFileStorage.DecryptInProgressDirectory).Count(),
|
||||
DecryptInProgressFiles = fileCount(AudibleFileStorage.DecryptInProgressDirectory),
|
||||
|
||||
disableIPv6 = AppContext.TryGetSwitch("System.Net.DisableIPv6", out bool disableIPv6Value),
|
||||
});
|
||||
|
||||
if (InteropFactory.InteropFunctionsType is null)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
||||
<PackageReference Include="NPOI" Version="2.6.1" />
|
||||
<PackageReference Include="CsvHelper" Version="32.0.3" />
|
||||
<PackageReference Include="NPOI" Version="2.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -119,9 +119,15 @@ namespace ApplicationServices
|
||||
|
||||
logTime($"pre {nameof(scanAccountsAsync)} all");
|
||||
var libraryOptions = new LibraryOptions
|
||||
{
|
||||
ResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS,
|
||||
ImageSizes = LibraryOptions.ImageSizeOptions._500 | LibraryOptions.ImageSizeOptions._1215
|
||||
{
|
||||
ResponseGroups
|
||||
= LibraryOptions.ResponseGroupOptions.Rating | LibraryOptions.ResponseGroupOptions.Media
|
||||
| LibraryOptions.ResponseGroupOptions.Relationships | LibraryOptions.ResponseGroupOptions.ProductDesc
|
||||
| LibraryOptions.ResponseGroupOptions.Contributors | LibraryOptions.ResponseGroupOptions.ProvidedReview
|
||||
| LibraryOptions.ResponseGroupOptions.ProductPlans | LibraryOptions.ResponseGroupOptions.Series
|
||||
| LibraryOptions.ResponseGroupOptions.CategoryLadders | LibraryOptions.ResponseGroupOptions.ProductExtendedAttrs
|
||||
| LibraryOptions.ResponseGroupOptions.PdfUrl | LibraryOptions.ResponseGroupOptions.OriginAsin,
|
||||
ImageSizes = LibraryOptions.ImageSizeOptions._500 | LibraryOptions.ImageSizeOptions._1215
|
||||
};
|
||||
var importItems = await scanAccountsAsync(apiExtendedfunc, accounts, libraryOptions);
|
||||
logTime($"post {nameof(scanAccountsAsync)} all");
|
||||
|
||||
@@ -207,7 +207,11 @@ namespace AudibleUtilities
|
||||
try
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var items = await Api.GetCatalogProductsAsync(asins, CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);
|
||||
var items = await Api.GetCatalogProductsAsync(asins, CatalogOptions.ResponseGroupOptions.Rating | CatalogOptions.ResponseGroupOptions.Media
|
||||
| CatalogOptions.ResponseGroupOptions.Relationships | CatalogOptions.ResponseGroupOptions.ProductDesc
|
||||
| CatalogOptions.ResponseGroupOptions.Contributors | CatalogOptions.ResponseGroupOptions.ProvidedReview
|
||||
| CatalogOptions.ResponseGroupOptions.ProductPlans | CatalogOptions.ResponseGroupOptions.Series
|
||||
| CatalogOptions.ResponseGroupOptions.CategoryLadders | CatalogOptions.ResponseGroupOptions.ProductExtendedAttrs);
|
||||
sw.Stop();
|
||||
|
||||
Serilog.Log.Logger.Debug($"Batch {batchNum} End: Retrieved {items.Count} items in {sw.ElapsedMilliseconds} ms");
|
||||
|
||||
@@ -90,8 +90,10 @@ namespace AudibleUtilities
|
||||
var distinct = items.GetSeriesDistinct();
|
||||
if (distinct.Any(s => s.SeriesId is null))
|
||||
exceptions.Add(new ArgumentException($"Collection contains {nameof(Item.Series)} with null {nameof(Series.SeriesId)}", nameof(items)));
|
||||
if (distinct.Any(s => s.SeriesName is null))
|
||||
exceptions.Add(new ArgumentException($"Collection contains {nameof(Item.Series)} with null {nameof(Series.SeriesName)}", nameof(items)));
|
||||
|
||||
//// unfortunately, a user has a series with no name
|
||||
//if (distinct.Any(s => s.SeriesName is null))
|
||||
// exceptions.Add(new ArgumentException($"Collection contains {nameof(Item.Series)} with null {nameof(Series.SeriesName)}", nameof(items)));
|
||||
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AudibleApi" Version="8.4.3.1" />
|
||||
<PackageReference Include="AudibleApi" Version="9.1.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -10,14 +10,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dinah.Core" Version="7.3.0.1" />
|
||||
<PackageReference Include="Dinah.EntityFrameworkCore" Version="7.3.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
||||
<PackageReference Include="Dinah.Core" Version="8.0.0.1" />
|
||||
<PackageReference Include="Dinah.EntityFrameworkCore" Version="8.0.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -23,9 +23,11 @@ namespace DtoImporterService
|
||||
loadLocal_categories();
|
||||
|
||||
// upsert
|
||||
//Import item may not have no (null) categories
|
||||
var categoryLadders = importItems
|
||||
.Where(i => i.DtoItem.CategoryLadders is not null)
|
||||
.SelectMany(i => i.DtoItem.CategoryLadders)
|
||||
.Select(cl => cl.Ladder)
|
||||
.Select(cl => cl?.Ladder)
|
||||
.Where(l => l?.Length > 0)
|
||||
.ToList();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
|
||||
@@ -60,6 +60,11 @@ namespace FileLiberator
|
||||
config.LameMatchSourceBR,
|
||||
chapters);
|
||||
|
||||
if (m4bBook.AppleTags.Tracks is (int trackNum, int trackCount))
|
||||
{
|
||||
lameConfig.ID3.Track = trackCount > 0 ? $"{trackNum}/{trackCount}" : trackNum.ToString();
|
||||
}
|
||||
|
||||
using var mp3File = File.Open(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.ReadWrite);
|
||||
try
|
||||
{
|
||||
|
||||
@@ -375,8 +375,15 @@ namespace FileLiberator
|
||||
{
|
||||
if (Configuration.Instance.AllowLibationFixup)
|
||||
{
|
||||
e = OnRequestCoverArt();
|
||||
abDownloader.SetCoverArt(e);
|
||||
try
|
||||
{
|
||||
e = OnRequestCoverArt();
|
||||
abDownloader.SetCoverArt(e);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "Failed to retrieve cover art from server.");
|
||||
}
|
||||
}
|
||||
|
||||
if (e is not null)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace FileManager
|
||||
lock (fsCacheLocker)
|
||||
{
|
||||
fsCache.Clear();
|
||||
fsCache.AddRange(FileUtility.SaferEnumerateFiles(RootDirectory, SearchPattern, SearchOption));
|
||||
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace FileManager
|
||||
Stop();
|
||||
|
||||
lock (fsCacheLocker)
|
||||
fsCache.AddRange(FileUtility.SaferEnumerateFiles(RootDirectory, SearchPattern, SearchOption));
|
||||
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
|
||||
|
||||
directoryChangesEvents = new BlockingCollection<FileSystemEventArgs>();
|
||||
fileSystemWatcher = new FileSystemWatcher(RootDirectory)
|
||||
@@ -152,11 +152,23 @@ namespace FileManager
|
||||
if (Path.GetFileName(path).Contains("LibationContext.db") || !File.Exists(path) && !Directory.Exists(path))
|
||||
return;
|
||||
if (File.GetAttributes(path).HasFlag(FileAttributes.Directory))
|
||||
AddUniqueFiles(FileUtility.SaferEnumerateFiles(path, SearchPattern, SearchOption));
|
||||
AddUniqueFiles(SafestEnumerateFiles(path));
|
||||
else
|
||||
AddUniqueFile(path);
|
||||
}
|
||||
|
||||
private IEnumerable<LongPath> SafestEnumerateFiles(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return FileUtility.SaferEnumerateFiles(path, SearchPattern, SearchOption);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private void AddUniqueFiles(IEnumerable<LongPath> newFiles)
|
||||
{
|
||||
foreach (var file in newFiles)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dinah.Core" Version="7.3.0.1" />
|
||||
<PackageReference Include="Polly" Version="7.2.4" />
|
||||
<PackageReference Include="Dinah.Core" Version="8.0.0.1" />
|
||||
<PackageReference Include="Polly" Version="8.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<!--Avalonia doesen't support TrimMode=link currently,but we are working on that https://github.com/AvaloniaUI/Avalonia/issues/6892 -->
|
||||
<TrimMode>copyused</TrimMode>
|
||||
@@ -21,9 +21,13 @@
|
||||
<ApplicationIcon>hangover.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<RuntimeHostConfigurationOption Include="System.Net.DisableIPv6" Value="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\bin\Avalonia\Debug</OutputPath>
|
||||
@@ -67,13 +71,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
<PackageReference Include="Avalonia" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.5" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.3" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HangoverBase\HangoverBase.csproj" />
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\Linux-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\MacOS-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\Windows-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<AssemblyName>Hangover</AssemblyName>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
@@ -15,6 +15,10 @@
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<RuntimeHostConfigurationOption Include="System.Net.DisableIPv6" Value="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\classic</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solution Items", "{03C8835F-936C-4AF7-87AE-FF92BDBE8B9B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
REFERENCE.txt = REFERENCE.txt
|
||||
Upgrading dotnet version.txt = Upgrading dotnet version.txt
|
||||
_ARCHITECTURE NOTES.txt = _ARCHITECTURE NOTES.txt
|
||||
_AvaloniaUI Primer.txt = _AvaloniaUI Primer.txt
|
||||
_DB_NOTES.txt = _DB_NOTES.txt
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace LibationAvalonia.Controls
|
||||
|
||||
if (known is Configuration.KnownDirectories.None)
|
||||
{
|
||||
directoryState.CustomDir = noSubDir;
|
||||
directoryState.CustomDir = directory;
|
||||
directoryState.CustomChecked = true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
Margin="5"
|
||||
Padding="30,3,30,3"
|
||||
Content="Save"
|
||||
Command="{Binding SaveButtonAsync}" />
|
||||
Click="Save_Click" />
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationFileManager;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class LibationFilesDialog : Window
|
||||
public partial class LibationFilesDialog : DialogWindow
|
||||
{
|
||||
private class DirSelectOptions
|
||||
{
|
||||
@@ -18,28 +16,26 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
public string Directory { get; set; } = Configuration.GetKnownDirectoryPath(Configuration.KnownDirectories.UserProfile);
|
||||
}
|
||||
private DirSelectOptions dirSelectOptions;
|
||||
|
||||
private readonly DirSelectOptions dirSelectOptions;
|
||||
public string SelectedDirectory => dirSelectOptions.Directory;
|
||||
public DialogResult DialogResult { get; private set; }
|
||||
public LibationFilesDialog()
|
||||
|
||||
public LibationFilesDialog() : base(saveAndRestorePosition: false)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
DataContext = dirSelectOptions = new();
|
||||
}
|
||||
|
||||
public async Task SaveButtonAsync()
|
||||
public async void Save_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var libationDir = dirSelectOptions.Directory;
|
||||
|
||||
if (!System.IO.Directory.Exists(libationDir))
|
||||
if (!System.IO.Directory.Exists(SelectedDirectory))
|
||||
{
|
||||
await MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error, saveAndRestorePosition: false);
|
||||
await MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + SelectedDirectory, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error, saveAndRestorePosition: false);
|
||||
return;
|
||||
}
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close(DialogResult);
|
||||
await SaveAndCloseAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
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="900" d:DesignHeight="750"
|
||||
MinWidth="900" MinHeight="750"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="200"
|
||||
MinWidth="900" MinHeight="200"
|
||||
Width="900" Height="750"
|
||||
x:Class="LibationAvalonia.Dialogs.SettingsDialog"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
@@ -46,39 +46,41 @@
|
||||
</TabControl.Styles>
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="Important Settings"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<settings:Important DataContext="{CompiledBinding ImportantSettings}" />
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<settings:Important DataContext="{CompiledBinding ImportantSettings}" />
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="Import Library"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<settings:Import DataContext="{CompiledBinding ImportSettings}" />
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<settings:Import DataContext="{CompiledBinding ImportSettings}" />
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="Download/Decrypt"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<settings:DownloadDecrypt DataContext="{CompiledBinding DownloadDecryptSettings}" />
|
||||
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<settings:DownloadDecrypt DataContext="{CompiledBinding DownloadDecryptSettings}" />
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="Audio File Settings"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<settings:Audio DataContext="{CompiledBinding AudioSettings}" />
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<settings:Audio DataContext="{CompiledBinding AudioSettings}" />
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationIcon>Assets/libation.ico</ApplicationIcon>
|
||||
@@ -14,6 +14,10 @@
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<RuntimeHostConfigurationOption Include="System.Net.DisableIPv6" Value="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
@@ -69,14 +73,14 @@
|
||||
<UpToDateCheckInput Remove="Controls\GroupBox.axaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.3" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.3" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.3" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.5" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\Linux-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\MacOS-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\Windows-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -265,7 +265,7 @@ namespace LibationAvalonia.ViewModels
|
||||
private byte[] AudioDecodable_RequestCoverArt(object sender, EventArgs e)
|
||||
{
|
||||
var quality
|
||||
= Configuration.Instance.FileDownloadQuality == Configuration.DownloadQuality.High
|
||||
= Configuration.Instance.FileDownloadQuality == Configuration.DownloadQuality.High && LibraryBook.Book.PictureLarge is not null
|
||||
? new PictureDefinition(LibraryBook.Book.PictureLarge, PictureSize.Native)
|
||||
: new PictureDefinition(LibraryBook.Book.PictureId, PictureSize._500x500);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
@@ -11,6 +11,10 @@
|
||||
<IsPublishable>True</IsPublishable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<RuntimeHostConfigurationOption Include="System.Net.DisableIPv6" Value="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\Linux-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\MacOS-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\Windows-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,126 +8,136 @@ using Dinah.Core;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using FileManager;
|
||||
using AaxDecrypter;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager
|
||||
{
|
||||
public abstract class AudibleFileStorage
|
||||
{
|
||||
protected abstract LongPath? GetFilePathCustom(string productId);
|
||||
protected abstract List<LongPath> GetFilePathsCustom(string productId);
|
||||
public abstract class AudibleFileStorage
|
||||
{
|
||||
protected abstract LongPath? GetFilePathCustom(string productId);
|
||||
protected abstract List<LongPath> GetFilePathsCustom(string productId);
|
||||
|
||||
#region static
|
||||
public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
||||
public static LongPath DecryptInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName;
|
||||
#region static
|
||||
public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName;
|
||||
public static LongPath DecryptInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName;
|
||||
|
||||
static AudibleFileStorage()
|
||||
static AudibleFileStorage()
|
||||
{
|
||||
//Clean up any partially-decrypted files from previous Libation instances.
|
||||
//Do no clean DownloadsInProgressDirectory because those files are resumable
|
||||
foreach (var tempFile in Directory.EnumerateFiles(DecryptInProgressDirectory))
|
||||
FileUtility.SaferDelete(tempFile);
|
||||
//Do not clean DownloadsInProgressDirectory. Those files are resumable.
|
||||
try
|
||||
{
|
||||
foreach (var tempFile in FileUtility.SaferEnumerateFiles(DecryptInProgressDirectory))
|
||||
FileUtility.SaferDelete(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// since this is a static constructor which may be called very early, do not assume Serilog is initialized
|
||||
try { Serilog.Log.Error(ex, "Error cleaning up partially-decrypted files"); }
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static AaxcFileStorage AAXC { get; } = new AaxcFileStorage();
|
||||
public static bool AaxcExists(string productId) => AAXC.Exists(productId);
|
||||
public static bool AaxcExists(string productId) => AAXC.Exists(productId);
|
||||
|
||||
public static AudioFileStorage Audio { get; } = new AudioFileStorage();
|
||||
public static AudioFileStorage Audio { get; } = new AudioFileStorage();
|
||||
|
||||
public static LongPath BooksDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Configuration.Instance.Books))
|
||||
Configuration.Instance.Books = Path.Combine(Configuration.UserProfile, "Books");
|
||||
return Directory.CreateDirectory(Configuration.Instance.Books).FullName;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
public static LongPath BooksDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Configuration.Instance.Books))
|
||||
Configuration.Instance.Books = Path.Combine(Configuration.UserProfile, "Books");
|
||||
return Directory.CreateDirectory(Configuration.Instance.Books).FullName;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region instance
|
||||
private FileType FileType { get; }
|
||||
private string regexTemplate { get; }
|
||||
#region instance
|
||||
private FileType FileType { get; }
|
||||
private string regexTemplate { get; }
|
||||
|
||||
protected AudibleFileStorage(FileType fileType)
|
||||
{
|
||||
FileType = fileType;
|
||||
protected AudibleFileStorage(FileType fileType)
|
||||
{
|
||||
FileType = fileType;
|
||||
|
||||
var extAggr = FileTypes.GetExtensions(FileType).Aggregate((a, b) => $"{a}|{b}");
|
||||
regexTemplate = $@"{{0}}.*?\.({extAggr})$";
|
||||
}
|
||||
var extAggr = FileTypes.GetExtensions(FileType).Aggregate((a, b) => $"{a}|{b}");
|
||||
regexTemplate = $@"{{0}}.*?\.({extAggr})$";
|
||||
}
|
||||
|
||||
protected LongPath? GetFilePath(string productId)
|
||||
{
|
||||
// primary lookup
|
||||
var cachedFile = FilePathCache.GetFirstPath(productId, FileType);
|
||||
if (cachedFile is not null && File.Exists(cachedFile))
|
||||
return cachedFile;
|
||||
protected LongPath? GetFilePath(string productId)
|
||||
{
|
||||
// primary lookup
|
||||
var cachedFile = FilePathCache.GetFirstPath(productId, FileType);
|
||||
if (cachedFile is not null && File.Exists(cachedFile))
|
||||
return cachedFile;
|
||||
|
||||
// secondary lookup attempt
|
||||
var firstOrNull = GetFilePathCustom(productId);
|
||||
if (firstOrNull is not null)
|
||||
FilePathCache.Insert(productId, firstOrNull);
|
||||
// secondary lookup attempt
|
||||
var firstOrNull = GetFilePathCustom(productId);
|
||||
if (firstOrNull is not null)
|
||||
FilePathCache.Insert(productId, firstOrNull);
|
||||
|
||||
return firstOrNull;
|
||||
}
|
||||
return firstOrNull;
|
||||
}
|
||||
|
||||
public List<LongPath> GetPaths(string productId)
|
||||
=> GetFilePathsCustom(productId);
|
||||
public List<LongPath> GetPaths(string productId)
|
||||
=> GetFilePathsCustom(productId);
|
||||
|
||||
protected Regex GetBookSearchRegex(string productId)
|
||||
{
|
||||
var pattern = string.Format(regexTemplate, productId);
|
||||
return new Regex(pattern, RegexOptions.IgnoreCase);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
protected Regex GetBookSearchRegex(string productId)
|
||||
{
|
||||
var pattern = string.Format(regexTemplate, productId);
|
||||
return new Regex(pattern, RegexOptions.IgnoreCase);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal class AaxcFileStorage : AudibleFileStorage
|
||||
{
|
||||
internal AaxcFileStorage() : base(FileType.AAXC) { }
|
||||
internal class AaxcFileStorage : AudibleFileStorage
|
||||
{
|
||||
internal AaxcFileStorage() : base(FileType.AAXC) { }
|
||||
|
||||
protected override LongPath? GetFilePathCustom(string productId)
|
||||
=> GetFilePathsCustom(productId).FirstOrDefault();
|
||||
protected override LongPath? GetFilePathCustom(string productId)
|
||||
=> GetFilePathsCustom(productId).FirstOrDefault();
|
||||
|
||||
protected override List<LongPath> GetFilePathsCustom(string productId)
|
||||
{
|
||||
var regex = GetBookSearchRegex(productId);
|
||||
return FileUtility
|
||||
.SaferEnumerateFiles(DownloadsInProgressDirectory, "*.*", SearchOption.AllDirectories)
|
||||
.Where(s => regex.IsMatch(s)).ToList();
|
||||
}
|
||||
protected override List<LongPath> GetFilePathsCustom(string productId)
|
||||
{
|
||||
var regex = GetBookSearchRegex(productId);
|
||||
return FileUtility
|
||||
.SaferEnumerateFiles(DownloadsInProgressDirectory, "*.*", SearchOption.AllDirectories)
|
||||
.Where(s => regex.IsMatch(s)).ToList();
|
||||
}
|
||||
|
||||
public bool Exists(string productId) => GetFilePath(productId) is not null;
|
||||
}
|
||||
public bool Exists(string productId) => GetFilePath(productId) is not null;
|
||||
}
|
||||
|
||||
public class AudioFileStorage : AudibleFileStorage
|
||||
{
|
||||
internal AudioFileStorage() : base(FileType.Audio)
|
||||
=> BookDirectoryFiles ??= newBookDirectoryFiles();
|
||||
public class AudioFileStorage : AudibleFileStorage
|
||||
{
|
||||
internal AudioFileStorage() : base(FileType.Audio)
|
||||
=> BookDirectoryFiles ??= newBookDirectoryFiles();
|
||||
|
||||
private static BackgroundFileSystem? BookDirectoryFiles { get; set; }
|
||||
private static object bookDirectoryFilesLocker { get; } = new();
|
||||
private static BackgroundFileSystem? BookDirectoryFiles { get; set; }
|
||||
private static object bookDirectoryFilesLocker { get; } = new();
|
||||
private static EnumerationOptions enumerationOptions { get; } = new()
|
||||
{
|
||||
RecurseSubdirectories = true,
|
||||
IgnoreInaccessible = true,
|
||||
MatchCasing = MatchCasing.CaseInsensitive
|
||||
AttributesToSkip = FileAttributes.Hidden,
|
||||
};
|
||||
|
||||
protected override LongPath? GetFilePathCustom(string productId)
|
||||
=> GetFilePathsCustom(productId).FirstOrDefault();
|
||||
=> GetFilePathsCustom(productId).FirstOrDefault();
|
||||
|
||||
private static BackgroundFileSystem newBookDirectoryFiles()
|
||||
=> new BackgroundFileSystem(BooksDirectory, "*.*", SearchOption.AllDirectories);
|
||||
private static BackgroundFileSystem newBookDirectoryFiles()
|
||||
=> new BackgroundFileSystem(BooksDirectory, "*.*", SearchOption.AllDirectories);
|
||||
|
||||
protected override List<LongPath> GetFilePathsCustom(string productId)
|
||||
{
|
||||
// If user changed the BooksDirectory: reinitialize
|
||||
lock (bookDirectoryFilesLocker)
|
||||
if (BooksDirectory != BookDirectoryFiles?.RootDirectory)
|
||||
BookDirectoryFiles = newBookDirectoryFiles();
|
||||
{
|
||||
// If user changed the BooksDirectory: reinitialize
|
||||
lock (bookDirectoryFilesLocker)
|
||||
if (BooksDirectory != BookDirectoryFiles?.RootDirectory)
|
||||
BookDirectoryFiles = newBookDirectoryFiles();
|
||||
|
||||
var regex = GetBookSearchRegex(productId);
|
||||
|
||||
@@ -135,44 +145,60 @@ namespace LibationFileManager
|
||||
//using both the file system and the file path cache
|
||||
return
|
||||
FilePathCache
|
||||
.GetFiles(productId)
|
||||
.Where(c => c.fileType == FileType.Audio && File.Exists(c.path))
|
||||
.Select(c => c.path)
|
||||
.Union(BookDirectoryFiles.FindFiles(regex))
|
||||
.ToList();
|
||||
}
|
||||
.GetFiles(productId)
|
||||
.Where(c => c.fileType == FileType.Audio && File.Exists(c.path))
|
||||
.Select(c => c.path)
|
||||
.Union(BookDirectoryFiles.FindFiles(regex))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
if (BookDirectoryFiles is null)
|
||||
lock (bookDirectoryFilesLocker)
|
||||
BookDirectoryFiles = newBookDirectoryFiles();
|
||||
else
|
||||
BookDirectoryFiles?.RefreshFiles();
|
||||
}
|
||||
public void Refresh()
|
||||
{
|
||||
if (BookDirectoryFiles is null)
|
||||
lock (bookDirectoryFilesLocker)
|
||||
BookDirectoryFiles = newBookDirectoryFiles();
|
||||
else
|
||||
BookDirectoryFiles?.RefreshFiles();
|
||||
}
|
||||
|
||||
public LongPath? GetPath(string productId) => GetFilePath(productId);
|
||||
public LongPath? GetPath(string productId) => GetFilePath(productId);
|
||||
|
||||
public static async IAsyncEnumerable<FilePathCache.CacheEntry> FindAudiobooksAsync(LongPath searchDirectory, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(searchDirectory, nameof(searchDirectory));
|
||||
|
||||
foreach (LongPath path in Directory.EnumerateFiles(searchDirectory, "*.M4B", enumerationOptions))
|
||||
foreach (LongPath path in Directory.EnumerateFiles(searchDirectory, "*.*", enumerationOptions))
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
yield break;
|
||||
|
||||
if (getFormatByExtension(path) is not OutputFormat format)
|
||||
continue;
|
||||
|
||||
FilePathCache.CacheEntry? audioFile = default;
|
||||
|
||||
try
|
||||
{
|
||||
using var fileStream = File.OpenRead(path);
|
||||
if (format is OutputFormat.M4b)
|
||||
{
|
||||
var tags = await Task.Run(() => AAXClean.AppleTags.FromFile(path));
|
||||
|
||||
var mp4File = await Task.Run(() => new AAXClean.Mp4File(fileStream), cancellationToken);
|
||||
if (tags?.Asin is not null)
|
||||
audioFile = new FilePathCache.CacheEntry(tags.Asin, FileType.Audio, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var fileStream = File.OpenRead(path);
|
||||
var id3 = await Task.Run(() => NAudio.Lame.ID3.Id3Tag.Create(fileStream));
|
||||
|
||||
if (mp4File?.AppleTags?.Asin is not null)
|
||||
audioFile = new FilePathCache.CacheEntry(mp4File.AppleTags.Asin, FileType.Audio, path);
|
||||
var asin = id3?.Children
|
||||
.OfType<NAudio.Lame.ID3.TXXXFrame>()
|
||||
.FirstOrDefault(f => f.FieldName == "AUDIBLE_ASIN")
|
||||
?.FieldValue;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(asin))
|
||||
audioFile = new FilePathCache.CacheEntry(asin, FileType.Audio, path);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -186,6 +212,15 @@ namespace LibationFileManager
|
||||
if (audioFile is not null)
|
||||
yield return audioFile;
|
||||
}
|
||||
|
||||
static OutputFormat? getFormatByExtension(string path)
|
||||
{
|
||||
var ext = Path.GetExtension(path).ToLower();
|
||||
|
||||
return ext == ".mp3" ? OutputFormat.Mp3
|
||||
: ext == ".m4b" ? OutputFormat.M4b
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Dinah.Core.Logging;
|
||||
using System.Diagnostics;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager
|
||||
@@ -75,17 +76,19 @@ namespace LibationFileManager
|
||||
const string appsettings_filename = "appsettings.json";
|
||||
|
||||
//Possible appsettings.json locations, in order of preference.
|
||||
string[] possibleAppsettingsFiles = new[]
|
||||
string[] possibleAppsettingsDirectories = new[]
|
||||
{
|
||||
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)
|
||||
ProcessDirectory,
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Libation"),
|
||||
UserProfile,
|
||||
Path.Combine(Path.GetTempPath(), "Libation")
|
||||
};
|
||||
|
||||
//Try to find and validate appsettings.json in each folder
|
||||
foreach (var appsettingsFile in possibleAppsettingsFiles)
|
||||
foreach (var dir in possibleAppsettingsDirectories)
|
||||
{
|
||||
var appsettingsFile = Path.Combine(dir, appsettings_filename);
|
||||
|
||||
if (File.Exists(appsettingsFile))
|
||||
{
|
||||
try
|
||||
@@ -104,10 +107,13 @@ namespace LibationFileManager
|
||||
|
||||
//Valid appsettings.json not found. Try to create it in each folder.
|
||||
var endingContents = new JObject { { LIBATION_FILES_KEY, UserProfile } }.ToString(Formatting.Indented);
|
||||
foreach (var appsettingsFile in possibleAppsettingsFiles)
|
||||
foreach (var dir in possibleAppsettingsDirectories)
|
||||
{
|
||||
var appsettingsFile = Path.Combine(dir, appsettings_filename);
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllText(appsettingsFile, endingContents);
|
||||
return appsettingsFile;
|
||||
}
|
||||
@@ -121,16 +127,56 @@ namespace LibationFileManager
|
||||
}
|
||||
|
||||
private static string getLibationFilesSettingFromJson()
|
||||
{
|
||||
// do not check whether directory exists. special/meta directory (eg: AppDir) is valid
|
||||
// verify from live file. no try/catch. want failures to be visible
|
||||
var jObjFinal = JObject.Parse(File.ReadAllText(AppsettingsJsonFile));
|
||||
{
|
||||
// do not check whether directory exists. special/meta directory (eg: AppDir) is valid
|
||||
// verify from live file. no try/catch. want failures to be visible
|
||||
var jObjFinal = JObject.Parse(File.ReadAllText(AppsettingsJsonFile));
|
||||
|
||||
if (jObjFinal[LIBATION_FILES_KEY]?.Value<string>() is not string valueFinal)
|
||||
throw new InvalidDataException($"{LIBATION_FILES_KEY} not found in {AppsettingsJsonFile}");
|
||||
|
||||
return valueFinal;
|
||||
}
|
||||
if (IsWindows)
|
||||
{
|
||||
valueFinal = Environment.ExpandEnvironmentVariables(valueFinal);
|
||||
}
|
||||
else
|
||||
{
|
||||
//If the shell command fails and returns null, proceed with the verbatim
|
||||
//LIBATION_FILES_KEY path and hope for the best. If Libation can't find
|
||||
//anything at this path it will set LIBATION_FILES_KEY to UserProfile
|
||||
valueFinal = runShellCommand("echo " + valueFinal) ?? valueFinal;
|
||||
}
|
||||
|
||||
return valueFinal;
|
||||
|
||||
static string? runShellCommand(string command)
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "/bin/sh",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
ArgumentList =
|
||||
{
|
||||
"-c",
|
||||
command
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var proc = Process.Start(psi);
|
||||
proc?.WaitForExit();
|
||||
return proc?.StandardOutput?.ReadToEnd()?.Trim();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Serilog.Log.Error(e, "Failed to run shell command. {Arguments}", psi.ArgumentList);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLibationFiles(string directory)
|
||||
{
|
||||
|
||||
@@ -95,9 +95,9 @@ namespace LibationFileManager
|
||||
var assembly = CurrentDomain_AssemblyResolve_internal(asmName, here: here);
|
||||
lowEffortCache[key] = assembly;
|
||||
|
||||
//Let the runtime handle any dll not found exceptions.
|
||||
// Let the runtime handle any dll not found exceptions
|
||||
if (assembly is null)
|
||||
Serilog.Log.Logger.Error($"Unable to load module {args.Name}");
|
||||
Serilog.Log.Logger.Warning($"Unable to load module {args.Name}");
|
||||
|
||||
return assembly;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageReference Include="NameParserSharp" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LuceneNet303r2" Version="3.0.3.5" />
|
||||
<PackageReference Include="LuceneNet303r2" Version="3.0.3.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace LibationUiBase.GridView
|
||||
private string GetTooltip()
|
||||
{
|
||||
if (IsSeries)
|
||||
return Expanded ? "Click to Collpase" : "Click to Expand";
|
||||
return Expanded ? "Click to Collapse" : "Click to Expand";
|
||||
|
||||
if (IsUnavailable)
|
||||
return "This book cannot be downloaded\nbecause it wasn't found during\nthe most recent library scan";
|
||||
|
||||
@@ -95,20 +95,4 @@ public class GridContextMenu
|
||||
|
||||
return TemplateEditor<T>.CreateFilenameEditor(Configuration.Instance.Books, existingTemplate, folderDto, fileDto);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
class Command : ICommand
|
||||
{
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Execute(object parameter)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPublishable>true</IsPublishable>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
@@ -9,7 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
46
Source/LibationWinForms/AccessibleDataGridViewButtonCell.cs
Normal file
46
Source/LibationWinForms/AccessibleDataGridViewButtonCell.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
public class AccessibleDataGridViewButtonCell : DataGridViewButtonCell
|
||||
{
|
||||
private string accessibilityDescription;
|
||||
|
||||
protected string AccessibilityName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or set description for accessibility. eg: screen readers. Also sets the ToolTipText
|
||||
/// </summary>
|
||||
protected string AccessibilityDescription
|
||||
{
|
||||
get => accessibilityDescription;
|
||||
set
|
||||
{
|
||||
accessibilityDescription = value;
|
||||
ToolTipText = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override AccessibleObject CreateAccessibilityInstance() => new ButtonCellAccessibilityObject(this, name: AccessibilityName, description: AccessibilityDescription);
|
||||
|
||||
public AccessibleDataGridViewButtonCell(string accessibilityName) : base()
|
||||
{
|
||||
AccessibilityName = accessibilityName;
|
||||
}
|
||||
|
||||
protected class ButtonCellAccessibilityObject : DataGridViewButtonCellAccessibleObject
|
||||
{
|
||||
private string _name;
|
||||
public override string Name => _name;
|
||||
|
||||
private string _description;
|
||||
public override string Description => _description;
|
||||
|
||||
public ButtonCellAccessibilityObject(DataGridViewCell owner, string name, string description) : base(owner)
|
||||
{
|
||||
_name = name;
|
||||
_description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Source/LibationWinForms/AccessibleDataGridViewTextBoxCell.cs
Normal file
46
Source/LibationWinForms/AccessibleDataGridViewTextBoxCell.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
internal class AccessibleDataGridViewTextBoxCell : DataGridViewTextBoxCell
|
||||
{
|
||||
private string accessibilityDescription;
|
||||
|
||||
protected string AccessibilityName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get or set description for accessibility. eg: screen readers. Also sets the ToolTipText
|
||||
/// </summary>
|
||||
protected string AccessibilityDescription
|
||||
{
|
||||
get => accessibilityDescription;
|
||||
set
|
||||
{
|
||||
accessibilityDescription = value;
|
||||
ToolTipText = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected override AccessibleObject CreateAccessibilityInstance() => new TextBoxCellAccessibilityObject(this, name: AccessibilityName, description: AccessibilityDescription);
|
||||
|
||||
public AccessibleDataGridViewTextBoxCell(string accessibilityName) : base()
|
||||
{
|
||||
AccessibilityName = accessibilityName;
|
||||
}
|
||||
|
||||
protected class TextBoxCellAccessibilityObject : DataGridViewTextBoxCellAccessibleObject
|
||||
{
|
||||
private string _name;
|
||||
public override string Name => _name;
|
||||
|
||||
private string _description;
|
||||
public override string Description => _description;
|
||||
|
||||
public TextBoxCellAccessibilityObject(DataGridViewCell owner, string name, string description) : base(owner)
|
||||
{
|
||||
_name = name;
|
||||
_description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,13 +51,13 @@ namespace LibationWinForms.Dialogs
|
||||
|
||||
private void AddAccountToGrid(Account account)
|
||||
{
|
||||
int row = dataGridView1.Rows.Add(
|
||||
"X",
|
||||
"Export",
|
||||
account.LibraryScan,
|
||||
account.AccountId,
|
||||
account.Locale.Name,
|
||||
account.AccountName);
|
||||
var row = dataGridView1.Rows.Add(
|
||||
"X",
|
||||
"Export",
|
||||
account.LibraryScan,
|
||||
account.AccountId,
|
||||
account.Locale.Name,
|
||||
account.AccountName);
|
||||
|
||||
dataGridView1[COL_Export, row].ToolTipText = "Export account authorization to audible-cli";
|
||||
}
|
||||
|
||||
@@ -24,23 +24,39 @@ namespace LibationWinForms.Dialogs
|
||||
private const string COL_MoveUp = nameof(MoveUp);
|
||||
private const string COL_MoveDown = nameof(MoveDown);
|
||||
|
||||
internal class DisableButtonCell : DataGridViewButtonCell
|
||||
{
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
internal class DisableButtonCell : AccessibleDataGridViewButtonCell
|
||||
{
|
||||
private int LastRowIndex => DataGridView.Rows[^1].IsNewRow ? DataGridView.Rows[^1].Index - 1 : DataGridView.Rows[^1].Index;
|
||||
|
||||
public DisableButtonCell() : base("Edit Filter button") { }
|
||||
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
{
|
||||
if ((OwningColumn.Name == COL_MoveUp && rowIndex == 0)
|
||||
|| (OwningColumn.Name == COL_MoveDown && rowIndex == LastRowIndex)
|
||||
|| OwningRow.IsNewRow)
|
||||
{
|
||||
var isMoveUp = OwningColumn.Name == COL_MoveUp;
|
||||
var isMoveDown = OwningColumn.Name == COL_MoveDown;
|
||||
var isDelete = OwningColumn.Name == COL_Delete;
|
||||
var isNewRow = OwningRow.IsNewRow;
|
||||
|
||||
if (isNewRow
|
||||
|| (isMoveUp && rowIndex == 0)
|
||||
|| (isMoveDown && rowIndex == LastRowIndex))
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts ^ (DataGridViewPaintParts.ContentBackground | DataGridViewPaintParts.ContentForeground | DataGridViewPaintParts.SelectionBackground));
|
||||
|
||||
ButtonRenderer.DrawButton(graphics, cellBounds, value as string, cellStyle.Font, false, System.Windows.Forms.VisualStyles.PushButtonState.Disabled);
|
||||
}
|
||||
}
|
||||
else
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
}
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
|
||||
int LastRowIndex => DataGridView.Rows[^1].IsNewRow ? DataGridView.Rows[^1].Index - 1 : DataGridView.Rows[^1].Index;
|
||||
if (isMoveUp)
|
||||
AccessibilityDescription = "Move up";
|
||||
else if (isMoveDown)
|
||||
AccessibilityDescription = "Move down";
|
||||
else if (isDelete)
|
||||
AccessibilityDescription = "Delete";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EditQuickFilters()
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
badBookGb.Controls.Add(badBookAskRb);
|
||||
badBookGb.Location = new System.Drawing.Point(7, 6);
|
||||
badBookGb.Name = "badBookGb";
|
||||
badBookGb.Size = new System.Drawing.Size(836, 76);
|
||||
badBookGb.Size = new System.Drawing.Size(841, 76);
|
||||
badBookGb.TabIndex = 13;
|
||||
badBookGb.TabStop = false;
|
||||
badBookGb.Text = "[bad book desc]";
|
||||
@@ -397,6 +397,7 @@
|
||||
//
|
||||
// tab1ImportantSettings
|
||||
//
|
||||
tab1ImportantSettings.AutoScroll = true;
|
||||
tab1ImportantSettings.Controls.Add(groupBox1);
|
||||
tab1ImportantSettings.Controls.Add(booksGb);
|
||||
tab1ImportantSettings.Controls.Add(logsBtn);
|
||||
@@ -412,6 +413,7 @@
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
groupBox1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
groupBox1.Controls.Add(applyDisplaySettingsBtn);
|
||||
groupBox1.Controls.Add(gridScaleFactorLbl);
|
||||
groupBox1.Controls.Add(gridScaleFactorTbar);
|
||||
@@ -426,6 +428,7 @@
|
||||
//
|
||||
// applyDisplaySettingsBtn
|
||||
//
|
||||
applyDisplaySettingsBtn.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
applyDisplaySettingsBtn.Location = new System.Drawing.Point(689, 26);
|
||||
applyDisplaySettingsBtn.Name = "applyDisplaySettingsBtn";
|
||||
applyDisplaySettingsBtn.Size = new System.Drawing.Size(148, 34);
|
||||
@@ -554,6 +557,7 @@
|
||||
//
|
||||
// tab2ImportLibrary
|
||||
//
|
||||
tab2ImportLibrary.AutoScroll = true;
|
||||
tab2ImportLibrary.Controls.Add(autoDownloadEpisodesCb);
|
||||
tab2ImportLibrary.Controls.Add(autoScanCb);
|
||||
tab2ImportLibrary.Controls.Add(showImportedStatsCb);
|
||||
@@ -599,6 +603,7 @@
|
||||
//
|
||||
// tab3DownloadDecrypt
|
||||
//
|
||||
tab3DownloadDecrypt.AutoScroll = true;
|
||||
tab3DownloadDecrypt.Controls.Add(saveMetadataToFileCbox);
|
||||
tab3DownloadDecrypt.Controls.Add(useCoverAsFolderIconCb);
|
||||
tab3DownloadDecrypt.Controls.Add(inProgressFilesGb);
|
||||
@@ -764,6 +769,7 @@
|
||||
//
|
||||
// tab4AudioFileOptions
|
||||
//
|
||||
tab4AudioFileOptions.AutoScroll = true;
|
||||
tab4AudioFileOptions.Controls.Add(fileDownloadQualityCb);
|
||||
tab4AudioFileOptions.Controls.Add(fileDownloadQualityLbl);
|
||||
tab4AudioFileOptions.Controls.Add(combineNestedChapterTitlesCbox);
|
||||
@@ -845,7 +851,7 @@
|
||||
audiobookFixupsGb.Controls.Add(stripAudibleBrandingCbox);
|
||||
audiobookFixupsGb.Location = new System.Drawing.Point(6, 200);
|
||||
audiobookFixupsGb.Name = "audiobookFixupsGb";
|
||||
audiobookFixupsGb.Size = new System.Drawing.Size(403, 182);
|
||||
audiobookFixupsGb.Size = new System.Drawing.Size(385, 182);
|
||||
audiobookFixupsGb.TabIndex = 19;
|
||||
audiobookFixupsGb.TabStop = false;
|
||||
audiobookFixupsGb.Text = "Audiobook Fix-ups";
|
||||
@@ -872,6 +878,7 @@
|
||||
//
|
||||
// chapterTitleTemplateGb
|
||||
//
|
||||
chapterTitleTemplateGb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
chapterTitleTemplateGb.Controls.Add(chapterTitleTemplateBtn);
|
||||
chapterTitleTemplateGb.Controls.Add(chapterTitleTemplateTb);
|
||||
chapterTitleTemplateGb.Location = new System.Drawing.Point(3, 388);
|
||||
@@ -903,6 +910,7 @@
|
||||
//
|
||||
// lameOptionsGb
|
||||
//
|
||||
lameOptionsGb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
lameOptionsGb.Controls.Add(label20);
|
||||
lameOptionsGb.Controls.Add(label21);
|
||||
lameOptionsGb.Controls.Add(encoderQualityCb);
|
||||
@@ -912,9 +920,9 @@
|
||||
lameOptionsGb.Controls.Add(label1);
|
||||
lameOptionsGb.Controls.Add(lameQualityGb);
|
||||
lameOptionsGb.Controls.Add(groupBox2);
|
||||
lameOptionsGb.Location = new System.Drawing.Point(415, 6);
|
||||
lameOptionsGb.Location = new System.Drawing.Point(397, 6);
|
||||
lameOptionsGb.Name = "lameOptionsGb";
|
||||
lameOptionsGb.Size = new System.Drawing.Size(433, 376);
|
||||
lameOptionsGb.Size = new System.Drawing.Size(450, 376);
|
||||
lameOptionsGb.TabIndex = 14;
|
||||
lameOptionsGb.TabStop = false;
|
||||
lameOptionsGb.Text = "Mp3 Encoding Options";
|
||||
@@ -930,6 +938,7 @@
|
||||
//
|
||||
// label21
|
||||
//
|
||||
label21.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label21.AutoSize = true;
|
||||
label21.Location = new System.Drawing.Point(239, 89);
|
||||
label21.Name = "label21";
|
||||
@@ -939,33 +948,37 @@
|
||||
//
|
||||
// encoderQualityCb
|
||||
//
|
||||
encoderQualityCb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
encoderQualityCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
encoderQualityCb.FormattingEnabled = true;
|
||||
encoderQualityCb.Location = new System.Drawing.Point(337, 86);
|
||||
encoderQualityCb.Name = "encoderQualityCb";
|
||||
encoderQualityCb.Size = new System.Drawing.Size(90, 23);
|
||||
encoderQualityCb.Size = new System.Drawing.Size(107, 23);
|
||||
encoderQualityCb.TabIndex = 2;
|
||||
//
|
||||
// maxSampleRateCb
|
||||
//
|
||||
maxSampleRateCb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
maxSampleRateCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
maxSampleRateCb.FormattingEnabled = true;
|
||||
maxSampleRateCb.Location = new System.Drawing.Point(119, 86);
|
||||
maxSampleRateCb.Name = "maxSampleRateCb";
|
||||
maxSampleRateCb.Size = new System.Drawing.Size(101, 23);
|
||||
maxSampleRateCb.Size = new System.Drawing.Size(76, 23);
|
||||
maxSampleRateCb.TabIndex = 2;
|
||||
//
|
||||
// lameDownsampleMonoCbox
|
||||
//
|
||||
lameDownsampleMonoCbox.Location = new System.Drawing.Point(237, 30);
|
||||
lameDownsampleMonoCbox.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
|
||||
lameDownsampleMonoCbox.Location = new System.Drawing.Point(247, 29);
|
||||
lameDownsampleMonoCbox.Name = "lameDownsampleMonoCbox";
|
||||
lameDownsampleMonoCbox.Size = new System.Drawing.Size(184, 34);
|
||||
lameDownsampleMonoCbox.Size = new System.Drawing.Size(197, 34);
|
||||
lameDownsampleMonoCbox.TabIndex = 1;
|
||||
lameDownsampleMonoCbox.Text = "Downsample stereo to mono?\r\n(Recommended)\r\n";
|
||||
lameDownsampleMonoCbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// lameBitrateGb
|
||||
//
|
||||
lameBitrateGb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
lameBitrateGb.Controls.Add(LameMatchSourceBRCbox);
|
||||
lameBitrateGb.Controls.Add(lameConstantBitrateCbox);
|
||||
lameBitrateGb.Controls.Add(label7);
|
||||
@@ -977,15 +990,16 @@
|
||||
lameBitrateGb.Controls.Add(lameBitrateTb);
|
||||
lameBitrateGb.Location = new System.Drawing.Point(6, 116);
|
||||
lameBitrateGb.Name = "lameBitrateGb";
|
||||
lameBitrateGb.Size = new System.Drawing.Size(421, 113);
|
||||
lameBitrateGb.Size = new System.Drawing.Size(438, 113);
|
||||
lameBitrateGb.TabIndex = 0;
|
||||
lameBitrateGb.TabStop = false;
|
||||
lameBitrateGb.Text = "Bitrate";
|
||||
//
|
||||
// LameMatchSourceBRCbox
|
||||
//
|
||||
LameMatchSourceBRCbox.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
|
||||
LameMatchSourceBRCbox.AutoSize = true;
|
||||
LameMatchSourceBRCbox.Location = new System.Drawing.Point(275, 76);
|
||||
LameMatchSourceBRCbox.Location = new System.Drawing.Point(254, 76);
|
||||
LameMatchSourceBRCbox.Name = "LameMatchSourceBRCbox";
|
||||
LameMatchSourceBRCbox.Size = new System.Drawing.Size(140, 19);
|
||||
LameMatchSourceBRCbox.TabIndex = 3;
|
||||
@@ -1005,6 +1019,7 @@
|
||||
//
|
||||
// label7
|
||||
//
|
||||
label7.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label7.AutoSize = true;
|
||||
label7.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
label7.Location = new System.Drawing.Point(390, 52);
|
||||
@@ -1015,6 +1030,7 @@
|
||||
//
|
||||
// label6
|
||||
//
|
||||
label6.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label6.AutoSize = true;
|
||||
label6.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
label6.Location = new System.Drawing.Point(309, 52);
|
||||
@@ -1025,6 +1041,7 @@
|
||||
//
|
||||
// label5
|
||||
//
|
||||
label5.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label5.AutoSize = true;
|
||||
label5.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
label5.Location = new System.Drawing.Point(228, 52);
|
||||
@@ -1035,6 +1052,7 @@
|
||||
//
|
||||
// label4
|
||||
//
|
||||
label4.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label4.AutoSize = true;
|
||||
label4.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
label4.Location = new System.Drawing.Point(147, 52);
|
||||
@@ -1045,6 +1063,7 @@
|
||||
//
|
||||
// label11
|
||||
//
|
||||
label11.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label11.AutoSize = true;
|
||||
label11.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
label11.Location = new System.Drawing.Point(10, 52);
|
||||
@@ -1055,6 +1074,7 @@
|
||||
//
|
||||
// label3
|
||||
//
|
||||
label3.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label3.AutoSize = true;
|
||||
label3.BackColor = System.Drawing.SystemColors.ControlLightLight;
|
||||
label3.Location = new System.Drawing.Point(71, 52);
|
||||
@@ -1071,7 +1091,7 @@
|
||||
lameBitrateTb.Maximum = 320;
|
||||
lameBitrateTb.Minimum = 16;
|
||||
lameBitrateTb.Name = "lameBitrateTb";
|
||||
lameBitrateTb.Size = new System.Drawing.Size(409, 45);
|
||||
lameBitrateTb.Size = new System.Drawing.Size(408, 45);
|
||||
lameBitrateTb.SmallChange = 8;
|
||||
lameBitrateTb.TabIndex = 0;
|
||||
lameBitrateTb.TickFrequency = 16;
|
||||
@@ -1090,6 +1110,7 @@
|
||||
//
|
||||
// lameQualityGb
|
||||
//
|
||||
lameQualityGb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
lameQualityGb.Controls.Add(label19);
|
||||
lameQualityGb.Controls.Add(label18);
|
||||
lameQualityGb.Controls.Add(label17);
|
||||
@@ -1105,13 +1126,14 @@
|
||||
lameQualityGb.Controls.Add(lameVBRQualityTb);
|
||||
lameQualityGb.Location = new System.Drawing.Point(6, 235);
|
||||
lameQualityGb.Name = "lameQualityGb";
|
||||
lameQualityGb.Size = new System.Drawing.Size(421, 109);
|
||||
lameQualityGb.Size = new System.Drawing.Size(438, 109);
|
||||
lameQualityGb.TabIndex = 0;
|
||||
lameQualityGb.TabStop = false;
|
||||
lameQualityGb.Text = "Quality";
|
||||
//
|
||||
// label19
|
||||
//
|
||||
label19.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label19.AutoSize = true;
|
||||
label19.Location = new System.Drawing.Point(349, 52);
|
||||
label19.Name = "label19";
|
||||
@@ -1121,6 +1143,7 @@
|
||||
//
|
||||
// label18
|
||||
//
|
||||
label18.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label18.AutoSize = true;
|
||||
label18.Location = new System.Drawing.Point(307, 52);
|
||||
label18.Name = "label18";
|
||||
@@ -1130,6 +1153,7 @@
|
||||
//
|
||||
// label17
|
||||
//
|
||||
label17.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label17.AutoSize = true;
|
||||
label17.Location = new System.Drawing.Point(265, 52);
|
||||
label17.Name = "label17";
|
||||
@@ -1139,6 +1163,7 @@
|
||||
//
|
||||
// label16
|
||||
//
|
||||
label16.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label16.AutoSize = true;
|
||||
label16.Location = new System.Drawing.Point(223, 52);
|
||||
label16.Name = "label16";
|
||||
@@ -1148,6 +1173,7 @@
|
||||
//
|
||||
// label12
|
||||
//
|
||||
label12.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label12.AutoSize = true;
|
||||
label12.Location = new System.Drawing.Point(182, 52);
|
||||
label12.Name = "label12";
|
||||
@@ -1157,6 +1183,7 @@
|
||||
//
|
||||
// label15
|
||||
//
|
||||
label15.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label15.AutoSize = true;
|
||||
label15.Location = new System.Drawing.Point(140, 52);
|
||||
label15.Name = "label15";
|
||||
@@ -1166,6 +1193,7 @@
|
||||
//
|
||||
// label9
|
||||
//
|
||||
label9.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label9.AutoSize = true;
|
||||
label9.Location = new System.Drawing.Point(97, 52);
|
||||
label9.Name = "label9";
|
||||
@@ -1175,6 +1203,7 @@
|
||||
//
|
||||
// label8
|
||||
//
|
||||
label8.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label8.AutoSize = true;
|
||||
label8.Location = new System.Drawing.Point(391, 52);
|
||||
label8.Name = "label8";
|
||||
@@ -1202,6 +1231,7 @@
|
||||
//
|
||||
// label14
|
||||
//
|
||||
label14.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label14.AutoSize = true;
|
||||
label14.Location = new System.Drawing.Point(56, 52);
|
||||
label14.Name = "label14";
|
||||
@@ -1211,6 +1241,7 @@
|
||||
//
|
||||
// label2
|
||||
//
|
||||
label2.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
label2.AutoSize = true;
|
||||
label2.Location = new System.Drawing.Point(14, 52);
|
||||
label2.Name = "label2";
|
||||
@@ -1225,25 +1256,27 @@
|
||||
lameVBRQualityTb.Location = new System.Drawing.Point(10, 22);
|
||||
lameVBRQualityTb.Maximum = 9;
|
||||
lameVBRQualityTb.Name = "lameVBRQualityTb";
|
||||
lameVBRQualityTb.Size = new System.Drawing.Size(405, 45);
|
||||
lameVBRQualityTb.Size = new System.Drawing.Size(404, 45);
|
||||
lameVBRQualityTb.TabIndex = 0;
|
||||
lameVBRQualityTb.Value = 9;
|
||||
//
|
||||
// groupBox2
|
||||
//
|
||||
groupBox2.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
groupBox2.Controls.Add(lameTargetQualityRb);
|
||||
groupBox2.Controls.Add(lameTargetBitrateRb);
|
||||
groupBox2.Location = new System.Drawing.Point(6, 22);
|
||||
groupBox2.Name = "groupBox2";
|
||||
groupBox2.Size = new System.Drawing.Size(214, 58);
|
||||
groupBox2.Size = new System.Drawing.Size(189, 58);
|
||||
groupBox2.TabIndex = 0;
|
||||
groupBox2.TabStop = false;
|
||||
groupBox2.Text = "Target";
|
||||
//
|
||||
// lameTargetQualityRb
|
||||
//
|
||||
lameTargetQualityRb.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
|
||||
lameTargetQualityRb.AutoSize = true;
|
||||
lameTargetQualityRb.Location = new System.Drawing.Point(139, 22);
|
||||
lameTargetQualityRb.Location = new System.Drawing.Point(118, 22);
|
||||
lameTargetQualityRb.Name = "lameTargetQualityRb";
|
||||
lameTargetQualityRb.Size = new System.Drawing.Size(63, 19);
|
||||
lameTargetQualityRb.TabIndex = 0;
|
||||
@@ -1321,7 +1354,6 @@
|
||||
Controls.Add(tabControl);
|
||||
Controls.Add(cancelBtn);
|
||||
Controls.Add(saveBtn);
|
||||
FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
MaximizeBox = false;
|
||||
MinimizeBox = false;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing"">Blue</data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms.GridView
|
||||
{
|
||||
public class DataGridViewImageButtonCell : DataGridViewButtonCell
|
||||
{
|
||||
protected void DrawButtonImage(Graphics graphics, Image image, Rectangle cellBounds)
|
||||
public class DataGridViewImageButtonCell : AccessibleDataGridViewButtonCell
|
||||
{
|
||||
public DataGridViewImageButtonCell(string accessibilityName) : base(accessibilityName) { }
|
||||
|
||||
protected void DrawButtonImage(Graphics graphics, Image image, Rectangle cellBounds)
|
||||
{
|
||||
var scaleFactor = OwningColumn is IDataGridScaleColumn scCol ? scCol.ScaleFactor : 1f;
|
||||
|
||||
|
||||
@@ -20,20 +20,33 @@ namespace LibationWinForms.GridView
|
||||
}
|
||||
|
||||
internal class EditTagsDataGridViewImageButtonCell : DataGridViewImageButtonCell
|
||||
{
|
||||
private static Image ButtonImage { get; } = Properties.Resources.edit_25x25;
|
||||
{
|
||||
public EditTagsDataGridViewImageButtonCell() : base("Edit Tags button") { }
|
||||
|
||||
private static Image ButtonImage { get; } = Properties.Resources.edit_25x25;
|
||||
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
{
|
||||
if (rowIndex >= 0 && DataGridView.GetBoundItem<IGridEntry>(rowIndex) is ISeriesEntry)
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border);
|
||||
else if (value is string tagStr && tagStr.Length == 0)
|
||||
// series
|
||||
if (rowIndex >= 0 && DataGridView.GetBoundItem<IGridEntry>(rowIndex) is ISeriesEntry)
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border);
|
||||
}
|
||||
// tag: empty
|
||||
else if (value is string tagStr && tagStr.Length == 0)
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts);
|
||||
|
||||
DrawButtonImage(graphics, ButtonImage, cellBounds);
|
||||
}
|
||||
AccessibilityDescription = "Click to edit tags";
|
||||
}
|
||||
// tag: not empty
|
||||
else
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
}
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
|
||||
AccessibilityDescription = (string)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using LibationUiBase.GridView;
|
||||
using System;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using LibationUiBase.GridView;
|
||||
|
||||
namespace LibationWinForms.GridView
|
||||
{
|
||||
@@ -21,15 +21,19 @@ namespace LibationWinForms.GridView
|
||||
}
|
||||
}
|
||||
|
||||
internal class LastDownloadedGridViewCell : DataGridViewTextBoxCell
|
||||
{
|
||||
internal class LastDownloadedGridViewCell : AccessibleDataGridViewTextBoxCell
|
||||
{
|
||||
private LastDownloadStatus LastDownload => (LastDownloadStatus)Value;
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
|
||||
public LastDownloadedGridViewCell() : base("Last Downloaded") { }
|
||||
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
{
|
||||
if (value is LastDownloadStatus lastDl)
|
||||
ToolTipText = lastDl.ToolTipText;
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
}
|
||||
|
||||
if (value is LastDownloadStatus lastDl)
|
||||
AccessibilityDescription = lastDl.ToolTipText;
|
||||
}
|
||||
|
||||
protected override void OnDoubleClick(DataGridViewCellEventArgs e)
|
||||
{
|
||||
|
||||
@@ -16,7 +16,9 @@ namespace LibationWinForms.GridView
|
||||
|
||||
internal class LiberateDataGridViewImageButtonCell : DataGridViewImageButtonCell
|
||||
{
|
||||
private static readonly Brush DISABLED_GRAY = new SolidBrush(Color.FromArgb(0x60, Color.LightGray));
|
||||
public LiberateDataGridViewImageButtonCell() : base("Liberate button") { }
|
||||
|
||||
private static readonly Brush DISABLED_GRAY = new SolidBrush(Color.FromArgb(0x60, Color.LightGray));
|
||||
private static readonly Color HiddenForeColor = Color.LightGray;
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
{
|
||||
@@ -31,7 +33,7 @@ namespace LibationWinForms.GridView
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts);
|
||||
|
||||
DrawButtonImage(graphics, (Image)status.ButtonImage, cellBounds);
|
||||
ToolTipText = status.ToolTip;
|
||||
AccessibilityDescription = status.ToolTip;
|
||||
|
||||
if (status.IsUnavailable || status.Opacity < 1)
|
||||
graphics.FillRectangle(DISABLED_GRAY, cellBounds);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using DataLayer;
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using DataLayer;
|
||||
|
||||
namespace LibationWinForms.GridView
|
||||
{
|
||||
@@ -24,14 +24,17 @@ namespace LibationWinForms.GridView
|
||||
}
|
||||
}
|
||||
|
||||
internal class MyRatingGridViewCell : DataGridViewTextBoxCell
|
||||
{
|
||||
internal class MyRatingGridViewCell : AccessibleDataGridViewTextBoxCell
|
||||
{
|
||||
private static Rating DefaultRating => new Rating(0, 0, 0);
|
||||
public override object DefaultNewRowValue => DefaultRating;
|
||||
public override Type EditType => typeof(MyRatingCellEditor);
|
||||
public override Type ValueType => typeof(Rating);
|
||||
|
||||
public MyRatingGridViewCell() { ToolTipText = ReadOnly ? "" : "Click to change ratings"; }
|
||||
public MyRatingGridViewCell() : base("My Rating")
|
||||
{
|
||||
AccessibilityDescription = ReadOnly ? "" : "Click to change ratings";
|
||||
}
|
||||
|
||||
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
|
||||
{
|
||||
@@ -46,14 +49,14 @@ namespace LibationWinForms.GridView
|
||||
{
|
||||
if (value is Rating rating)
|
||||
{
|
||||
ToolTipText = ReadOnly ? "" : "Click to change ratings";
|
||||
|
||||
var starString = rating.ToStarString();
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, starString, starString, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
}
|
||||
else
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, string.Empty, string.Empty, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
}
|
||||
|
||||
AccessibilityDescription = ReadOnly ? "" : "Click to change ratings";
|
||||
}
|
||||
else
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, string.Empty, string.Empty, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
}
|
||||
|
||||
protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
|
||||
=> value is Rating rating ? rating.ToStarString() : value?.ToString();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>libation.ico</ApplicationIcon>
|
||||
@@ -17,6 +17,10 @@
|
||||
<!-- Version is now in AppScaffolding.csproj -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<RuntimeHostConfigurationOption Include="System.Net.DisableIPv6" Value="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
@@ -37,7 +41,7 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="7.3.0.1" />
|
||||
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="8.0.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -250,7 +250,7 @@ namespace LibationWinForms.ProcessQueue
|
||||
private byte[] AudioDecodable_RequestCoverArt(object sender, EventArgs e)
|
||||
{
|
||||
var quality
|
||||
= Configuration.Instance.FileDownloadQuality == Configuration.DownloadQuality.High
|
||||
= Configuration.Instance.FileDownloadQuality == Configuration.DownloadQuality.High && LibraryBook.Book.PictureLarge is not null
|
||||
? new PictureDefinition(LibraryBook.Book.PictureLarge, PictureSize.Native)
|
||||
: new PictureDefinition(LibraryBook.Book.PictureId, PictureSize._500x500);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Publish\classic</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -13,37 +13,40 @@ namespace LibationWinForms.SeriesView
|
||||
CellTemplate.Style.WrapMode = DataGridViewTriState.True;
|
||||
}
|
||||
}
|
||||
internal class DownloadButtonColumnCell : DataGridViewButtonCell
|
||||
{
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
{
|
||||
if (value is SeriesButton sentry)
|
||||
{
|
||||
string cellValue = sentry.DisplayText;
|
||||
if (!sentry.Enabled)
|
||||
{
|
||||
//Draw disabled button
|
||||
Rectangle buttonArea = cellBounds;
|
||||
Rectangle buttonAdjustment = BorderWidths(advancedBorderStyle);
|
||||
buttonArea.X += buttonAdjustment.X;
|
||||
buttonArea.Y += buttonAdjustment.Y;
|
||||
buttonArea.Height -= buttonAdjustment.Height;
|
||||
buttonArea.Width -= buttonAdjustment.Width;
|
||||
ButtonRenderer.DrawButton(graphics, buttonArea, cellValue, cellStyle.Font, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak, focused: false, PushButtonState.Disabled);
|
||||
internal class DownloadButtonColumnCell : AccessibleDataGridViewButtonCell
|
||||
{
|
||||
public DownloadButtonColumnCell() : base("Download Series button") { }
|
||||
|
||||
}
|
||||
else if (sentry.HasButtonAction)
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, cellValue, cellValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
else
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border);
|
||||
TextRenderer.DrawText(graphics, cellValue, cellStyle.Font, cellBounds, cellStyle.ForeColor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border);
|
||||
}
|
||||
}
|
||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||
{
|
||||
if (value is not SeriesButton seriesEntry)
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border);
|
||||
return;
|
||||
}
|
||||
|
||||
string cellValue = seriesEntry.DisplayText;
|
||||
AccessibilityDescription = cellValue;
|
||||
|
||||
if (!seriesEntry.Enabled)
|
||||
{
|
||||
//Draw disabled button
|
||||
Rectangle buttonArea = cellBounds;
|
||||
Rectangle buttonAdjustment = BorderWidths(advancedBorderStyle);
|
||||
buttonArea.X += buttonAdjustment.X;
|
||||
buttonArea.Y += buttonAdjustment.Y;
|
||||
buttonArea.Height -= buttonAdjustment.Height;
|
||||
buttonArea.Width -= buttonAdjustment.Width;
|
||||
|
||||
ButtonRenderer.DrawButton(graphics, buttonArea, cellValue, cellStyle.Font, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak, focused: false, PushButtonState.Disabled);
|
||||
}
|
||||
else if (seriesEntry.HasButtonAction)
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, cellValue, cellValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
else
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border);
|
||||
TextRenderer.DrawText(graphics, cellValue, cellStyle.Font, cellBounds, cellStyle.ForeColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\..\bin\Publish\Linux-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<!--<TargetFramework>net7.0-macos</TargetFramework>-->
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<!--<TargetFramework>net8.0-macos</TargetFramework>-->
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\..\bin\Publish\MacOS-chardonnay</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\..\bin\Publish\classic</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>false</PublishSingleFile>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
@@ -26,7 +26,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1901.177" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2478.35" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
9
Source/Upgrading dotnet version.txt
Normal file
9
Source/Upgrading dotnet version.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
For reasons I cannot figure out, upgrading the major .net version breaks Libation until these were done. Update these to the same version of .net as the app and release their nugets:
|
||||
|
||||
* https://github.com/rmcrackan/Dinah.Core
|
||||
* https://github.com/rmcrackan/AudibleApi
|
||||
* https://github.com/rmcrackan/LuceneNet303r2
|
||||
|
||||
* https://github.com/Mbucari/AAXClean.Codecs
|
||||
* https://github.com/Mbucari/AAXClean
|
||||
* https://github.com/Mbucari/serilog-sinks-zipfile
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
@@ -19,7 +19,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dinah.Core" Version="7.3.0.1" />
|
||||
<PackageReference Include="Dinah.Core" Version="8.0.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
Reference in New Issue
Block a user