├── .cdmurls.json
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
└── workflows
│ ├── build-linux.yml
│ ├── build-windows.yml
│ ├── build.yml
│ ├── docker.yml
│ ├── release.yml
│ ├── validate-appstream-metainfo.yaml
│ ├── validate-desktop-file.yaml
│ └── validate.yml
├── .gitignore
├── .releaseindex.json
├── .vscode
├── launch.json
└── tasks.json
├── Docker
├── appsettings.json
└── liberate.sh
├── Dockerfile
├── Documentation
├── Advanced.md
├── Docker.md
├── FrequentlyAskedQuestions.md
├── GettingStarted.md
├── InstallOnLinux.md
├── InstallOnMac.md
├── NamingTemplates.md
├── SearchingAndFiltering.md
└── images
│ ├── Export.png
│ ├── FilterOptionsButton.png
│ ├── FiltersDefault.png
│ ├── Import1.png
│ ├── Import2.png
│ ├── Import3.png
│ ├── LiberateBook1.png
│ ├── LiberateBook2.png
│ ├── LiberateBook3.png
│ ├── LiberateBook4.png
│ ├── LiberateBook5.png
│ ├── PdfDownload2.png
│ ├── PdfDownload3.png
│ ├── PostDownload.png
│ ├── SearchExampleAutoBio.png
│ ├── SearchExampleBio.png
│ ├── SearchExampleGaimanAuthorNarrated.png
│ ├── SearchExampleHarryPotter.png
│ ├── SearchExamplePotter.png
│ ├── SearchExamplePotterNotHarry.png
│ ├── SearchExamplePotterNotHarry2.png
│ ├── Tags1.png
│ ├── Tags2.png
│ ├── Tags3.png
│ ├── alt-login1.png
│ ├── alt-login2.png
│ ├── alt-login3.png
│ ├── alt-login4.png
│ ├── v40_accounts.png
│ ├── v40_import.png
│ └── v40_locales.png
├── Images
├── Plus Minus.psd
├── Stoplight with pdf.psd
├── Stoplight.psd
├── libation_cheers.svg
├── libation_glass.svg
├── libation_hangover.svg
└── libation_slosh.svg
├── LICENSE
├── README.md
├── Scripts
├── Bundle_Debian.sh
├── Bundle_MacOS.sh
└── Bundle_Redhat.sh
└── Source
├── AaxDecrypter
├── AaxDecrypter.csproj
├── AaxcDownloadConvertBase.cs
├── AaxcDownloadMultiConverter.cs
├── AaxcDownloadSingleConverter.cs
├── AudiobookDownloadBase.cs
├── AverageSpeed.cs
├── Cue.cs
├── IDownloadOptions.cs
├── MpegUtil.cs
├── MultiConvertFileProperties.cs
├── NetworkFileStream.cs
├── NetworkFileStreamPersister.cs
└── UnencryptedAudiobookDownloader.cs
├── AppScaffolding
├── AppScaffolding.csproj
├── LibationScaffolding.cs
├── UNSAFE_MigrationHelper.cs
└── UpgradeProperties.cs
├── ApplicationServices
├── ApplicationServices.csproj
├── BulkSetDownloadStatus.cs
├── DbContexts.cs
├── LibraryCommands.cs
├── LibraryExporter.cs
├── RecordExporter.cs
└── SearchEngineCommands.cs
├── AudibleUtilities
├── Account.cs
├── AccountsSettings.cs
├── AccountsSettingsPersister.cs
├── ApiExtended.cs
├── AudibleApiStorage.cs
├── AudibleApiValidators.cs
├── AudibleUtilities.csproj
├── ImportValidationException.cs
├── Mkb79Auth.cs
├── Widevine
│ ├── Cdm.Api.cs
│ ├── Cdm.cs
│ ├── Device.cs
│ ├── Extensions.cs
│ ├── LicenseProtocol.cs
│ └── MpegDash.cs
└── _InternalsVisible.cs
├── DataLayer
├── Configurations
│ ├── BookCategoryConfig.cs
│ ├── BookConfig.cs
│ ├── BookContributorConfig.cs
│ ├── CategoryConfig.cs
│ ├── CategoryLadderConfig.cs
│ ├── ContributorConfig.cs
│ ├── LibraryBookConfig.cs
│ ├── SeriesBookConfig.cs
│ └── SeriesConfig.cs
├── DataLayer.csproj
├── EfClasses
│ ├── Book.cs
│ ├── BookCategory.cs
│ ├── BookContributor.cs
│ ├── Category.cs
│ ├── CategoryLadder.cs
│ ├── Contributor.cs
│ ├── LibraryBook.cs
│ ├── Rating.cs
│ ├── Series.cs
│ ├── SeriesBook.cs
│ ├── Supplement.cs
│ └── UserDefinedItem.cs
├── EntityExtensions.cs
├── Formatters.cs
├── LibationContext.cs
├── LibationContextFactory.cs
├── Migrations
│ ├── 20191125182309_Fresh.Designer.cs
│ ├── 20191125182309_Fresh.cs
│ ├── 20200812152646_AddLocaleAndAccount.Designer.cs
│ ├── 20200812152646_AddLocaleAndAccount.cs
│ ├── 20210619030017_AddAaxcDecryptionKeys.Designer.cs
│ ├── 20210619030017_AddAaxcDecryptionKeys.cs
│ ├── 20210622205558_RemoveAaxcDecryptionKeys.Designer.cs
│ ├── 20210622205558_RemoveAaxcDecryptionKeys.cs
│ ├── 20210727180408_AddLiberatedStatus.Designer.cs
│ ├── 20210727180408_AddLiberatedStatus.cs
│ ├── 20210821012137_RemoveUdiBookLocation.Designer.cs
│ ├── 20210821012137_RemoveUdiBookLocation.cs
│ ├── 20210901205042_BookIsEpisode.Designer.cs
│ ├── 20210901205042_BookIsEpisode.cs
│ ├── 20210902192153_RenameLibraryBooks.Designer.cs
│ ├── 20210902192153_RenameLibraryBooks.cs
│ ├── 20210922154900_AddSeriesOrderString.Designer.cs
│ ├── 20210922154900_AddSeriesOrderString.cs
│ ├── 20220510175257_AddPictureIDLargeMigration.Designer.cs
│ ├── 20220510175257_AddPictureIDLargeMigration.cs
│ ├── 20220624214932_AddAudioFormat.Designer.cs
│ ├── 20220624214932_AddAudioFormat.cs
│ ├── 20221214205106_LibraryBookIsDeleted.Designer.cs
│ ├── 20221214205106_LibraryBookIsDeleted.cs
│ ├── 20230201162454_AddBookLanguage.Designer.cs
│ ├── 20230201162454_AddBookLanguage.cs
│ ├── 20230302220539_AddLastDownloadedInfo.Designer.cs
│ ├── 20230302220539_AddLastDownloadedInfo.cs
│ ├── 20230308013410_AddAbsentFromLastScan.Designer.cs
│ ├── 20230308013410_AddAbsentFromLastScan.cs
│ ├── 20230626171442_AddBookSubtitle.Designer.cs
│ ├── 20230626171442_AddBookSubtitle.cs
│ ├── 20230718214617_AddCategoryLadder.Designer.cs
│ ├── 20230718214617_AddCategoryLadder.cs
│ ├── 20240911114741_MyComment.Designer.cs
│ ├── 20240911114741_MyComment.cs
│ └── LibationContextModelSnapshot.cs
├── QueryObjects
│ ├── BookQueries.cs
│ ├── CategoryQueries.cs
│ └── LibraryBookQueries.cs
└── migrate.json
├── DtoImporterService
├── BookImporter.cs
├── CategoryImporter.cs
├── ContributorImporter.cs
├── DtoImporterService.csproj
├── ImportItem.cs
├── ImporterBase.cs
├── LibraryBookImporter.cs
├── PerfLogger.cs
├── SeriesImporter.cs
└── _importer notes.txt
├── FileLiberator
├── AudioDecodable.cs
├── AudioFileStorageExt.cs
├── ConvertToMp3.cs
├── DownloadDecryptBook.cs
├── DownloadOptions.Factory.cs
├── DownloadOptions.cs
├── DownloadPdf.cs
├── FileLiberator.csproj
├── Processable.cs
└── UtilityExtensions.cs
├── FileManager
├── BackgroundFileSystem.cs
├── FileManager.csproj
├── FileUtility.cs
├── LogArchiver.cs
├── LongPath.cs
├── NamingTemplate
│ ├── ConditionalTagCollection[TClass].cs
│ ├── ITemplateTag.cs
│ ├── NamingTemplate.cs
│ ├── PropertyTagCollection[TClass].cs
│ ├── TagBase.cs
│ ├── TagCollection.cs
│ └── TemplatePart.cs
├── PersistentDictionary.cs
└── ReplacementCharacters.cs
├── HangoverAvalonia
├── App.axaml
├── App.axaml.cs
├── Assets
│ └── hangover.ico
├── Controls
│ ├── CheckedListBox.axaml
│ └── CheckedListBox.axaml.cs
├── HangoverAvalonia.csproj
├── Program.cs
├── Properties
│ └── PublishProfiles
│ │ ├── LinuxProfile.pubxml
│ │ ├── MacOSProfile.pubxml
│ │ └── WindowsProfile.pubxml
├── ViewLocator.cs
├── ViewModels
│ ├── CheckBoxViewModel.cs
│ ├── MainVM.Database.cs
│ ├── MainVM.Deleted.cs
│ ├── MainVM.cs
│ ├── TrashBinViewModel.cs
│ └── ViewModelBase.cs
├── Views
│ ├── MainWindow.CLI.cs
│ ├── MainWindow.Database.cs
│ ├── MainWindow.Deleted.cs
│ ├── MainWindow.axaml
│ └── MainWindow.axaml.cs
└── hangover.ico
├── HangoverBase
├── DatabaseTab.cs
└── HangoverBase.csproj
├── HangoverWinForms
├── Form1.CLI.cs
├── Form1.Database.cs
├── Form1.Deleted.cs
├── Form1.Designer.cs
├── Form1.cs
├── Form1.resx
├── HangoverWinForms.csproj
├── Program.cs
├── Properties
│ └── PublishProfiles
│ │ └── WindowsProfile.pubxml
├── Resources
│ └── Hangover icon
│ │ ├── spilled-glass-with-glow-PSD.zip
│ │ ├── spilled-glass-with-glow.ico
│ │ ├── spilled-glass-with-glow_1024.png
│ │ ├── spilled-glass-with-glow_128.png
│ │ ├── spilled-glass-with-glow_16.png
│ │ ├── spilled-glass-with-glow_20.png
│ │ ├── spilled-glass-with-glow_24.png
│ │ ├── spilled-glass-with-glow_256.png
│ │ ├── spilled-glass-with-glow_32.png
│ │ ├── spilled-glass-with-glow_40.png
│ │ ├── spilled-glass-with-glow_48.png
│ │ ├── spilled-glass-with-glow_512.png
│ │ ├── spilled-glass-with-glow_512x512.pdn
│ │ ├── spilled-glass-with-glow_64.png
│ │ └── spilled-glass-with-glow_96.png
└── hangover.ico
├── Libation.sln
├── LibationAvalonia
├── App.axaml
├── App.axaml.cs
├── Assets
│ ├── 1x1.png
│ ├── LibationVectorIcons.xaml
│ ├── MBIcons
│ │ ├── Asterisk.ico
│ │ ├── Asterisk_64.png
│ │ ├── Error.ico
│ │ ├── Error_64.png
│ │ ├── Exclamation.ico
│ │ ├── Exclamation_64.png
│ │ ├── Question.ico
│ │ └── Question_64.png
│ ├── img-coverart-prod-unavailable_300x300.jpg
│ ├── img-coverart-prod-unavailable_500x500.jpg
│ ├── img-coverart-prod-unavailable_80x80.jpg
│ └── libation.ico
├── AvaloniaUtils.cs
├── Controls
│ ├── CheckedListBox.axaml
│ ├── CheckedListBox.axaml.cs
│ ├── DataGridCheckBoxColumnExt.cs
│ ├── DataGridContextMenus.cs
│ ├── DataGridMyRatingColumn.cs
│ ├── DataGridTemplateColumnExt.cs
│ ├── DirectoryOrCustomSelectControl.axaml
│ ├── DirectoryOrCustomSelectControl.axaml.cs
│ ├── DirectorySelectControl.axaml
│ ├── DirectorySelectControl.axaml.cs
│ ├── GroupBox.axaml
│ ├── GroupBox.axaml.cs
│ ├── LinkLabel.axaml
│ ├── LinkLabel.axaml.cs
│ ├── MyRatingCellEditor.axaml
│ ├── MyRatingCellEditor.axaml.cs
│ ├── NativeWebView.cs
│ ├── Settings
│ │ ├── Audio.axaml
│ │ ├── Audio.axaml.cs
│ │ ├── DownloadDecrypt.axaml
│ │ ├── DownloadDecrypt.axaml.cs
│ │ ├── Import.axaml
│ │ ├── Import.axaml.cs
│ │ ├── Important.axaml
│ │ └── Important.axaml.cs
│ ├── ThemePreviewControl.axaml
│ ├── ThemePreviewControl.axaml.cs
│ ├── WheelComboBox.axaml
│ └── WheelComboBox.axaml.cs
├── Dialogs
│ ├── AboutDialog.axaml
│ ├── AboutDialog.axaml.cs
│ ├── AccountsDialog.axaml
│ ├── AccountsDialog.axaml.cs
│ ├── BookDetailsDialog.axaml
│ ├── BookDetailsDialog.axaml.cs
│ ├── BookRecordsDialog.axaml
│ ├── BookRecordsDialog.axaml.cs
│ ├── DescriptionDisplayDialog.axaml
│ ├── DescriptionDisplayDialog.axaml.cs
│ ├── DialogWindow.cs
│ ├── EditQuickFilters.axaml
│ ├── EditQuickFilters.axaml.cs
│ ├── EditReplacementChars.axaml
│ ├── EditReplacementChars.axaml.cs
│ ├── EditTemplateDialog.axaml
│ ├── EditTemplateDialog.axaml.cs
│ ├── ImageDisplayDialog.axaml
│ ├── ImageDisplayDialog.axaml.cs
│ ├── LibationFilesDialog.axaml
│ ├── LibationFilesDialog.axaml.cs
│ ├── LiberatedStatusBatchAutoDialog.axaml
│ ├── LiberatedStatusBatchAutoDialog.axaml.cs
│ ├── LiberatedStatusBatchManualDialog.axaml
│ ├── LiberatedStatusBatchManualDialog.axaml.cs
│ ├── LocateAudiobooksDialog.axaml
│ ├── LocateAudiobooksDialog.axaml.cs
│ ├── Login
│ │ ├── ApprovalNeededDialog.axaml
│ │ ├── ApprovalNeededDialog.axaml.cs
│ │ ├── AvaloniaLoginCallback.cs
│ │ ├── AvaloniaLoginChoiceEager.cs
│ │ ├── CaptchaDialog.axaml
│ │ ├── CaptchaDialog.axaml.cs
│ │ ├── LoginCallbackDialog.axaml
│ │ ├── LoginCallbackDialog.axaml.cs
│ │ ├── LoginChoiceEagerDialog.axaml
│ │ ├── LoginChoiceEagerDialog.axaml.cs
│ │ ├── LoginExternalDialog.axaml
│ │ ├── LoginExternalDialog.axaml.cs
│ │ ├── MfaDialog.axaml
│ │ ├── MfaDialog.axaml.cs
│ │ ├── WebLoginDialog.axaml
│ │ ├── WebLoginDialog.axaml.cs
│ │ ├── _2faCodeDialog.axaml
│ │ └── _2faCodeDialog.axaml.cs
│ ├── MessageBoxAlertAdminDialog.axaml
│ ├── MessageBoxAlertAdminDialog.axaml.cs
│ ├── MessageBoxWindow.axaml
│ ├── MessageBoxWindow.axaml.cs
│ ├── ScanAccountsDialog.axaml
│ ├── ScanAccountsDialog.axaml.cs
│ ├── SearchSyntaxDialog.axaml
│ ├── SearchSyntaxDialog.axaml.cs
│ ├── SettingsDialog.axaml
│ ├── SettingsDialog.axaml.cs
│ ├── SetupDialog.axaml
│ ├── SetupDialog.axaml.cs
│ ├── TagsBatchDialog.axaml
│ ├── TagsBatchDialog.axaml.cs
│ ├── ThemePickerDialog.axaml
│ ├── ThemePickerDialog.axaml.cs
│ ├── TrashBinDialog.axaml
│ ├── TrashBinDialog.axaml.cs
│ ├── UpgradeNotificationDialog.axaml
│ └── UpgradeNotificationDialog.axaml.cs
├── FormSaveExtension.cs
├── LibationAvalonia.csproj
├── MessageBox.cs
├── Program.cs
├── Properties
│ └── PublishProfiles
│ │ ├── LinuxProfile.pubxml
│ │ ├── MacOSProfile.pubxml
│ │ └── WindowsProfile.pubxml
├── Themes
│ ├── ChardonnayTheme.cs
│ └── ChardonnayThemePersister.cs
├── ViewLocator.cs
├── ViewModels
│ ├── AvaloniaEntryStatus.cs
│ ├── Dialogs
│ │ └── MessageBoxViewModel.cs
│ ├── LiberateStatusButtonViewModel.cs
│ ├── MainVM.BackupCounts.cs
│ ├── MainVM.Export.cs
│ ├── MainVM.Filters.cs
│ ├── MainVM.Import.cs
│ ├── MainVM.Liberate.cs
│ ├── MainVM.ProcessQueue.cs
│ ├── MainVM.ScanAuto.cs
│ ├── MainVM.Settings.cs
│ ├── MainVM.VisibleBooks.cs
│ ├── MainVM._NoUI.cs
│ ├── MainVM.cs
│ ├── ProcessBookViewModel.cs
│ ├── ProcessQueueViewModel.cs
│ ├── ProductsDisplayViewModel.cs
│ ├── RowComparer.cs
│ ├── Settings
│ │ ├── AudioSettingsVM.cs
│ │ ├── DownloadDecryptSettingsVM.cs
│ │ ├── ImportSettingsVM.cs
│ │ ├── ImportantSettingsVM.cs
│ │ └── SettingsVM.cs
│ └── ViewModelBase.cs
├── Views
│ ├── LiberateStatusButton.axaml
│ ├── LiberateStatusButton.axaml.cs
│ ├── MainWindow.axaml
│ ├── MainWindow.axaml.cs
│ ├── ProcessBookControl.axaml
│ ├── ProcessBookControl.axaml.cs
│ ├── ProcessQueueControl.axaml
│ ├── ProcessQueueControl.axaml.cs
│ ├── ProductsDisplay.axaml
│ ├── ProductsDisplay.axaml.cs
│ ├── SeriesViewDialog.axaml
│ ├── SeriesViewDialog.axaml.cs
│ ├── SeriesViewGrid.axaml
│ └── SeriesViewGrid.axaml.cs
├── Walkthrough.cs
└── app.manifest
├── LibationCli
├── ConsoleProgressBar.cs
├── HelpVerb.cs
├── LibationCli.csproj
├── Options
│ ├── ConvertOptions.cs
│ ├── ExportOptions.cs
│ ├── LiberateOptions.cs
│ ├── ScanOptions.cs
│ ├── SearchOptions.cs
│ ├── SetDownloadStatusOptions.cs
│ ├── VersionOptions.cs
│ ├── _OptionsBase.cs
│ └── _ProcessableOptionsBase.cs
├── Program.cs
├── Properties
│ └── PublishProfiles
│ │ ├── LinuxProfile.pubxml
│ │ ├── MacOSProfile.pubxml
│ │ └── WindowsProfile.pubxml
└── Setup.cs
├── LibationFileManager
├── AudibleFileStorage.cs
├── Configuration.Environment.cs
├── Configuration.HelpText.cs
├── Configuration.KnownDirectories.cs
├── Configuration.LibationFiles.cs
├── Configuration.Logging.cs
├── Configuration.PersistentSettings.cs
├── Configuration.PropertyChange.cs
├── Configuration.cs
├── FilePathCache.cs
├── FileTypes.cs
├── IInteropFunctions.cs
├── InteropFactory.cs
├── LibationFileManager.csproj
├── NullInteropFunctions.cs
├── PictureStorage.cs
├── QuickFilters.cs
├── SqliteStorage.cs
├── Templates
│ ├── ContributorDto.cs
│ ├── IListFormat[TList].cs
│ ├── LibraryBookDto.cs
│ ├── NameListFormat.cs
│ ├── SeriesDto.cs
│ ├── SeriesListFormat.cs
│ ├── TemplateEditor[T].cs
│ ├── TemplateTags.cs
│ └── Templates.cs
└── WindowsDirectory.cs
├── LibationSearchEngine
├── IndexRule.cs
├── IndexRuleCollection.cs
├── LibationSearchEngine.csproj
├── LuceneExtensions.cs
├── QuerySanitizer.cs
├── SearchEngine.cs
├── SearchResults.cs
├── _InternalsVisible.cs
└── _lucene resources.txt
├── LibationUiBase
├── BaseUtil.cs
├── EnumDisplay[T].cs
├── GridView
│ ├── EntryStatus.cs
│ ├── GridContextMenu.cs
│ ├── GridEntry[TStatus].cs
│ ├── IGridEntry.cs
│ ├── ILibraryBookEntry.cs
│ ├── ISeriesEntry.cs
│ ├── LastDownloadStatus.cs
│ ├── LibraryBookEntry[TStatus].cs
│ ├── ObjectComparer[T].cs
│ ├── QueryExtensions.cs
│ ├── RowComparerBase.cs
│ ├── SeriesEntry[TStatus].cs
│ └── SeriesOrder.cs
├── ILogForm.cs
├── IcoEncoder.cs
├── LibationContributor.cs
├── LibationUiBase.csproj
├── LogMe.cs
├── SeriesView
│ ├── AyceButton.cs
│ ├── SeriesButton.cs
│ ├── SeriesItem.cs
│ ├── SeriesOrder.cs
│ └── WishlistButton.cs
├── TrackedQueue[T].cs
└── Upgrader.cs
├── LibationWinForms
├── AccessibleDataGridViewButtonCell.cs
├── AccessibleDataGridViewTextBoxCell.cs
├── ClearableTextBox.Designer.cs
├── ClearableTextBox.cs
├── ClearableTextBox.resx
├── Dialogs
│ ├── AboutDialog.Designer.cs
│ ├── AboutDialog.cs
│ ├── AboutDialog.resx
│ ├── AccountsDialog.Designer.cs
│ ├── AccountsDialog.cs
│ ├── AccountsDialog.resx
│ ├── BookDetailsDialog.Designer.cs
│ ├── BookDetailsDialog.cs
│ ├── BookDetailsDialog.resx
│ ├── BookRecordsDialog.Designer.cs
│ ├── BookRecordsDialog.cs
│ ├── BookRecordsDialog.resx
│ ├── DirectoryOrCustomSelectControl.Designer.cs
│ ├── DirectoryOrCustomSelectControl.cs
│ ├── DirectoryOrCustomSelectControl.resx
│ ├── DirectorySelectControl.Designer.cs
│ ├── DirectorySelectControl.cs
│ ├── DirectorySelectControl.resx
│ ├── EditQuickFilters.Designer.cs
│ ├── EditQuickFilters.cs
│ ├── EditQuickFilters.resx
│ ├── EditReplacementChars.Designer.cs
│ ├── EditReplacementChars.cs
│ ├── EditReplacementChars.resx
│ ├── EditTemplateDialog.Designer.cs
│ ├── EditTemplateDialog.cs
│ ├── EditTemplateDialog.resx
│ ├── LibationFilesDialog.Designer.cs
│ ├── LibationFilesDialog.cs
│ ├── LibationFilesDialog.resx
│ ├── LiberatedStatusBatchAutoDialog.Designer.cs
│ ├── LiberatedStatusBatchAutoDialog.cs
│ ├── LiberatedStatusBatchAutoDialog.resx
│ ├── LiberatedStatusBatchManualDialog.Designer.cs
│ ├── LiberatedStatusBatchManualDialog.cs
│ ├── LiberatedStatusBatchManualDialog.resx
│ ├── LocateAudiobooksDialog.Designer.cs
│ ├── LocateAudiobooksDialog.cs
│ ├── LocateAudiobooksDialog.resx
│ ├── Login
│ │ ├── ApprovalNeededDialog.Designer.cs
│ │ ├── ApprovalNeededDialog.cs
│ │ ├── ApprovalNeededDialog.resx
│ │ ├── CaptchaDialog.Designer.cs
│ │ ├── CaptchaDialog.cs
│ │ ├── CaptchaDialog.resx
│ │ ├── LoginCallbackDialog.Designer.cs
│ │ ├── LoginCallbackDialog.cs
│ │ ├── LoginCallbackDialog.resx
│ │ ├── LoginChoiceEagerDialog.Designer.cs
│ │ ├── LoginChoiceEagerDialog.cs
│ │ ├── LoginChoiceEagerDialog.resx
│ │ ├── LoginExternalDialog.Designer.cs
│ │ ├── LoginExternalDialog.cs
│ │ ├── LoginExternalDialog.resx
│ │ ├── MfaDialog.Designer.cs
│ │ ├── MfaDialog.cs
│ │ ├── MfaDialog.resx
│ │ ├── WebLoginDialog.Designer.cs
│ │ ├── WebLoginDialog.cs
│ │ ├── WebLoginDialog.resx
│ │ ├── WinformLoginBase.cs
│ │ ├── WinformLoginCallback.cs
│ │ ├── WinformLoginChoiceEager.cs
│ │ ├── _2faCodeDialog.Designer.cs
│ │ ├── _2faCodeDialog.cs
│ │ └── _2faCodeDialog.resx
│ ├── MessageBoxAlertAdminDialog.Designer.cs
│ ├── MessageBoxAlertAdminDialog.cs
│ ├── MessageBoxAlertAdminDialog.resx
│ ├── ScanAccountsDialog.Designer.cs
│ ├── ScanAccountsDialog.cs
│ ├── ScanAccountsDialog.resx
│ ├── SearchSyntaxDialog.Designer.cs
│ ├── SearchSyntaxDialog.cs
│ ├── SearchSyntaxDialog.resx
│ ├── SettingsDialog.AudioSettings.cs
│ ├── SettingsDialog.Designer.cs
│ ├── SettingsDialog.DownloadDecrypt.cs
│ ├── SettingsDialog.ImportLibrary.cs
│ ├── SettingsDialog.Important.cs
│ ├── SettingsDialog.cs
│ ├── SettingsDialog.resx
│ ├── SetupDialog.Designer.cs
│ ├── SetupDialog.cs
│ ├── SetupDialog.resx
│ ├── TagsBatchDialog.Designer.cs
│ ├── TagsBatchDialog.cs
│ ├── TagsBatchDialog.resx
│ ├── TrashBinDialog.Designer.cs
│ ├── TrashBinDialog.cs
│ ├── TrashBinDialog.resx
│ ├── UpgradeNotificationDialog.Designer.cs
│ ├── UpgradeNotificationDialog.cs
│ └── UpgradeNotificationDialog.resx
├── Form1.BackupCounts.cs
├── Form1.Designer.cs
├── Form1.Export.cs
├── Form1.Filter.cs
├── Form1.Liberate.cs
├── Form1.ProcessQueue.cs
├── Form1.QuickFilters.cs
├── Form1.RemoveBooks.cs
├── Form1.ScanAuto.cs
├── Form1.ScanManual.cs
├── Form1.ScanNotification.cs
├── Form1.Settings.cs
├── Form1.Upgrade.cs
├── Form1.VisibleBooks.cs
├── Form1._NonUI.cs
├── Form1.cs
├── Form1.resx
├── FormSaveExtension.cs
├── FormattableLabel.cs
├── FormattableToolStripMenuItem.cs
├── FormattableToolStripStatusLabel.cs
├── GridView
│ ├── AsyncNotifyPropertyChanged.cs
│ ├── DataGridViewImageButtonCell.cs
│ ├── DescriptionDisplay.Designer.cs
│ ├── DescriptionDisplay.cs
│ ├── DescriptionDisplay.resx
│ ├── EditTagsDataGridViewImageButtonColumn.cs
│ ├── GridEntryBindingList.cs
│ ├── ImageDisplay.Designer.cs
│ ├── ImageDisplay.cs
│ ├── ImageDisplay.resx
│ ├── LastDownloadedGridViewColumn.cs
│ ├── LiberateDataGridViewImageButtonColumn.cs
│ ├── MyRatingCellEditor.Designer.cs
│ ├── MyRatingCellEditor.cs
│ ├── MyRatingCellEditor.resx
│ ├── MyRatingGridViewColumn.cs
│ ├── ProductsDisplay.Designer.cs
│ ├── ProductsDisplay.cs
│ ├── ProductsDisplay.resx
│ ├── ProductsGrid.Designer.cs
│ ├── ProductsGrid.cs
│ ├── ProductsGrid.resx
│ ├── RowComparer.cs
│ ├── SyncBindingSource.cs
│ └── WinFormsEntryStatus.cs
├── LibationWinForms.csproj
├── MessageBoxLib.cs
├── ProcessQueue
│ ├── ProcessBook.cs
│ ├── ProcessBookControl.Designer.cs
│ ├── ProcessBookControl.cs
│ ├── ProcessBookControl.resx
│ ├── ProcessBookForm.Designer.cs
│ ├── ProcessBookForm.cs
│ ├── ProcessBookForm.resx
│ ├── ProcessQueueControl.Designer.cs
│ ├── ProcessQueueControl.cs
│ ├── ProcessQueueControl.resx
│ ├── VirtualFlowControl.Designer.cs
│ ├── VirtualFlowControl.cs
│ └── VirtualFlowControl.resx
├── Program.cs
├── Properties
│ ├── DataSources
│ │ └── LibationWinForm.GridEntry.datasource
│ ├── PublishProfiles
│ │ └── WindowsProfile.pubxml
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── Resources
│ ├── Libation icon
│ │ ├── glass-with-glow-PSD.zip
│ │ ├── glass-with-glow.ico
│ │ ├── glass-with-glow_1024.png
│ │ ├── glass-with-glow_128.png
│ │ ├── glass-with-glow_16.png
│ │ ├── glass-with-glow_20.png
│ │ ├── glass-with-glow_24.png
│ │ ├── glass-with-glow_256.png
│ │ ├── glass-with-glow_32.png
│ │ ├── glass-with-glow_40.png
│ │ ├── glass-with-glow_48.png
│ │ ├── glass-with-glow_512.png
│ │ ├── glass-with-glow_512x512.pdn
│ │ ├── glass-with-glow_64.png
│ │ └── glass-with-glow_96.png
│ ├── _icon how to.txt
│ ├── cheers.png
│ ├── download-arrow.png
│ ├── edit-tags-25x25.png
│ ├── edit-tags-50x50.png
│ ├── edit_25x25.png
│ ├── edit_64x64.png
│ ├── error.png
│ ├── img-coverart-prod-unavailable_300x300.jpg
│ ├── img-coverart-prod-unavailable_500x500.jpg
│ ├── img-coverart-prod-unavailable_80x80.jpg
│ ├── import_16x16.png
│ ├── libation.ico
│ ├── liberate and pdf icons.pdn
│ ├── liberate icons.pdn
│ ├── liberate_green.png
│ ├── liberate_green_pdf_no.png
│ ├── liberate_green_pdf_yes.png
│ ├── liberate_red.png
│ ├── liberate_red_pdf_no.png
│ ├── liberate_red_pdf_yes.png
│ ├── liberate_yellow.png
│ ├── liberate_yellow_pdf_no.png
│ ├── liberate_yellow_pdf_yes.png
│ ├── minus.png
│ ├── pdf source.url
│ ├── plus.png
│ └── stoplight source.url
├── SeriesView
│ ├── DownloadButtonColumn.cs
│ ├── SeriesEntryBindingList.cs
│ ├── SeriesViewDialog.Designer.cs
│ ├── SeriesViewDialog.cs
│ └── SeriesViewDialog.resx
├── Walkthrough.cs
├── WinFormsUtil.cs
└── libation.ico
├── LoadByOS
├── LinuxConfigApp
│ ├── Libation.desktop
│ ├── LinuxConfigApp.csproj
│ ├── LinuxInterop.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── PublishProfiles
│ │ │ └── LinuxProfile.pubxml
│ ├── com.getlibation.Libation.metainfo.xml
│ ├── libation_glass.svg
│ ├── runasroot.sh
│ └── screenshots
│ │ ├── accounts.png
│ │ ├── audio-file-settings.png
│ │ ├── download-decrypt-settings.png
│ │ ├── filter.png
│ │ ├── important-settings.png
│ │ ├── main-page-dark.png
│ │ └── main-page-light.png
├── MacOSConfigApp
│ ├── Info.plist
│ ├── MacOSConfigApp.csproj
│ ├── MacOSInterop.cs
│ ├── MacWebViewAdapter.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── PublishProfiles
│ │ │ └── MacOSProfile.pubxml
│ ├── fileicon
│ └── libation.icns
├── README.txt
└── WindowsConfigApp
│ ├── FolderIcon.cs
│ ├── Program.cs
│ ├── Properties
│ └── PublishProfiles
│ │ └── WindowsProfile.pubxml
│ ├── WinInterop.cs
│ ├── WindowsConfigApp.csproj
│ ├── WindowsWebView2Adapter.cs
│ └── ZipExtractor.exe
├── REFERENCE.txt
├── Upgrading dotnet version.txt
├── _ARCHITECTURE NOTES.txt
├── _AvaloniaUI Primer.txt
├── _DB_NOTES.txt
├── _Demos
└── LoadByOS
│ ├── CrossPlatformClientExe
│ ├── CrossPlatformClientExe.csproj
│ ├── IInteropFunctions.cs
│ ├── NullInteropFunctions.cs
│ ├── OSConfigBase.cs
│ ├── OSInteropProxy.cs
│ └── Program.cs
│ ├── LinuxConfigApp
│ ├── LinuxConfigApp.csproj
│ ├── LinuxInterop.cs
│ └── Program.cs
│ └── WindowsConfigApp
│ ├── Form1.Designer.cs
│ ├── Form1.cs
│ ├── Form1.resx
│ ├── Program.cs
│ ├── WinInterop.cs
│ └── WindowsConfigApp.csproj
├── _Tests
├── AudibleUtilities.Tests
│ ├── AccountTests.cs
│ └── AudibleUtilities.Tests.csproj
├── FileLiberator.Tests
│ ├── DownloadDecryptBookTests.cs
│ └── FileLiberator.Tests.csproj
├── FileManager.Tests
│ ├── FileManager.Tests.csproj
│ ├── FileNamingTemplateTests.cs
│ └── FileUtilityTests.cs
├── LibationFileManager.Tests
│ ├── LibationFileManager.Tests.csproj
│ └── TemplatesTests.cs
└── LibationSearchEngine.Tests
│ ├── LibationSearchEngine.Tests.csproj
│ └── SearchEngineTests.cs
└── __README - COLLABORATORS.txt
/.cdmurls.json:
--------------------------------------------------------------------------------
1 | {
2 | "CdmUrls": [
3 | "https://ollj0gz40d.execute-api.us-west-2.amazonaws.com/default/AudibleCdm"
4 | ]
5 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve Libation
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 |
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Platform**
27 | [e.g. Windows 10, Windows 11, Mac, Linux (State distribution)]
28 |
29 | **Log Files**
30 | Attach your Libation log file here. Logs are typically in your `[user]\Libation` folder. (For example, on windows: `C:\my_username\Libation`) Also within Libation, on the first tab in Settings you can click the button 'Open log folder'
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 | ---
8 |
9 | **No-go ideas**
10 | There are lots of great ideas and many are beyond what we intend to do for Libation. Some good ideas which we do not intend to pursue:
11 |
12 | * comprehensive api/cli
13 | * aax/audiobook import
14 | * bulk rename of existing files
15 | * general metadata/tag editor
16 | * playback features
17 | * web gui
18 | * supporting non-audible vendors
19 | * official docker support
20 |
21 | **Is your feature request related to a problem? Please describe.**
22 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
23 |
24 | **Describe the solution you'd like**
25 | A clear and concise description of what you want to happen.
26 |
27 | **Describe alternatives you've considered**
28 | A clear and concise description of any alternative solutions or features you've considered.
29 |
30 | **Additional context**
31 | Add any other context or screenshots about the feature request here.
32 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 2
3 | updates:
4 | # Maintain dependencies for GitHub Actions
5 | - package-ecosystem: "github-actions"
6 | directory: "/"
7 | schedule:
8 | interval: "daily"
9 |
--------------------------------------------------------------------------------
/.github/workflows/validate-appstream-metainfo.yaml:
--------------------------------------------------------------------------------
1 | name: Validate MetaInfo
2 | "on":
3 | pull_request:
4 | branches: ["master"]
5 | paths:
6 | - .github/workflows/validate-appstream-metainfo.yml
7 | - Source/LoadByOS/LinuxConfigApp/com.getlibation.Libation.metainfo.xml
8 | push:
9 | branches: ["master"]
10 | paths:
11 | - .github/workflows/validate-appstream-metainfo.yml
12 | - Source/LoadByOS/LinuxConfigApp/com.getlibation.Libation.metainfo.xml
13 |
14 | jobs:
15 | validate-appstream-metainfo:
16 | runs-on: ubuntu-latest
17 | container:
18 | image: ghcr.io/flathub/flatpak-builder-lint:latest
19 | steps:
20 | - uses: actions/checkout@v4
21 | - name: Check the MetaInfo file
22 | run: flatpak-builder-lint appstream Source/LoadByOS/LinuxConfigApp/com.getlibation.Libation.metainfo.xml
23 |
--------------------------------------------------------------------------------
/.github/workflows/validate-desktop-file.yaml:
--------------------------------------------------------------------------------
1 | name: Check desktop file
2 | "on":
3 | pull_request:
4 | branches: ["master"]
5 | paths:
6 | - .github/workflows/validate-desktop-file.yml
7 | - Source/LoadByOS/LinuxConfigApp/Libation.desktop
8 | push:
9 | branches: ["master"]
10 | paths:
11 | - .github/workflows/validate-desktop-file.yml
12 | - Source/LoadByOS/LinuxConfigApp/Libation.desktop
13 |
14 | jobs:
15 | validate-desktop-file:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v4
19 | - run: sudo apt --yes install desktop-file-utils
20 | - name: Check the desktop file
21 | run: desktop-file-validate Source/LoadByOS/LinuxConfigApp/Libation.desktop
22 |
--------------------------------------------------------------------------------
/.github/workflows/validate.yml:
--------------------------------------------------------------------------------
1 | # validate.yml
2 | # Validates that Libation will build on a pull request or push to master.
3 | ---
4 | name: validate
5 |
6 | on:
7 | push:
8 | branches: [master]
9 | pull_request:
10 | branches: [master]
11 |
12 | jobs:
13 | build:
14 | uses: ./.github/workflows/build.yml
15 | docker:
16 | uses: ./.github/workflows/docker.yml
17 | with:
18 | version: ${GITHUB_SHA}
19 | release: false
20 | secrets:
21 | docker_username: ${{ secrets.DOCKERHUB_USERNAME }}
22 | docker_token: ${{ secrets.DOCKERHUB_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.releaseindex.json:
--------------------------------------------------------------------------------
1 | {
2 | "WindowsClassic": "Classic-Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-classic\\.zip",
3 | "WindowsAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-win(dows)?-chardonnay\\.zip",
4 | "LinuxAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-amd64\\.deb",
5 | "LinuxAvalonia_RPM": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-amd64\\.rpm",
6 | "MacOSAvalonia": "Libation\\.\\d+\\.\\d+\\.\\d+-macOS-chardonnay-x64\\.tgz",
7 | "LinuxAvalonia_Arm64": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-arm64\\.deb",
8 | "LinuxAvalonia_Arm64_RPM": "Libation\\.\\d+\\.\\d+\\.\\d+-linux-chardonnay-arm64\\.rpm",
9 | "MacOSAvalonia_Arm64": "Libation\\.\\d+\\.\\d+\\.\\d+-macOS-chardonnay-arm64\\.tgz"
10 | }
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 |
8 | {
9 | "name": ".NET Core Launch (console)",
10 | "type": "coreclr",
11 | "request": "launch",
12 | "preLaunchTask": "build",
13 | "program": "${workspaceFolder}/Source/bin/Avalonia/Debug/Libation.dll",
14 | "args": [],
15 | "cwd": "${workspaceFolder}",
16 | "stopAtEntry": false,
17 | "console": "internalConsole"
18 | }
19 |
20 | ]
21 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "build",
8 | "dependsOn": [
9 | "build_libation",
10 | "build_linuxconfigapp"
11 | ]
12 | },
13 | {
14 | "label": "build_libation",
15 | "type": "shell",
16 | "command": "dotnet",
17 | "args": [
18 | "build",
19 | "${workspaceFolder}/Source/LibationAvalonia/LibationAvalonia.csproj"
20 | ],
21 | "group": "build",
22 | "presentation": {
23 | //"reveal": "silent"
24 | },
25 | "problemMatcher": "$msCompile"
26 | },
27 | {
28 | "label": "build_linuxconfigapp",
29 | "type": "shell",
30 | "command": "dotnet",
31 | "args": [
32 | "build",
33 | "${workspaceFolder}/Source/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj"
34 | ],
35 | "group": "build",
36 | "presentation": {
37 | //"reveal": "silent"
38 | },
39 | "problemMatcher": "$msCompile"
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/Docker/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "LibationFiles": "/config-internal"
3 | }
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Dockerfile
2 | FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/dotnet/sdk:9.0 AS build
3 | ARG TARGETARCH
4 |
5 | COPY Source /Source
6 | RUN dotnet publish \
7 | /Source/LibationCli/LibationCli.csproj \
8 | --arch ${TARGETARCH} \
9 | --configuration Release \
10 | --output /Source/bin/Publish/Linux-chardonnay \
11 | -p:PublishProfile=/Source/LibationCli/Properties/PublishProfiles/LinuxProfile.pubxml
12 |
13 | FROM mcr.microsoft.com/dotnet/runtime:9.0
14 | ARG USER_UID=1001
15 | ARG USER_GID=1001
16 |
17 | # Set the character set that will be used for folder and filenames when liberating
18 | ENV LANG=C.UTF-8
19 | ENV LC_ALL=C.UTF-8
20 |
21 | ENV SLEEP_TIME=-1
22 | ENV LIBATION_CONFIG_INTERNAL=/config-internal
23 | ENV LIBATION_CONFIG_DIR=/config
24 | ENV LIBATION_DB_DIR=/db
25 | ENV LIBATION_DB_FILE=
26 | ENV LIBATION_CREATE_DB=true
27 | ENV LIBATION_BOOKS_DIR=/data
28 |
29 |
30 | RUN apt-get update && apt-get -y upgrade && \
31 | apt-get install -y jq && \
32 | mkdir -m777 ${LIBATION_CONFIG_INTERNAL} ${LIBATION_BOOKS_DIR}
33 |
34 | COPY --from=build /Source/bin/Publish/Linux-chardonnay /libation
35 | COPY Docker/* /libation
36 |
37 | USER ${USER_UID}:${USER_GID}
38 |
39 | CMD ["/libation/liberate.sh"]
40 |
--------------------------------------------------------------------------------
/Documentation/images/Export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/Export.png
--------------------------------------------------------------------------------
/Documentation/images/FilterOptionsButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/FilterOptionsButton.png
--------------------------------------------------------------------------------
/Documentation/images/FiltersDefault.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/FiltersDefault.png
--------------------------------------------------------------------------------
/Documentation/images/Import1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/Import1.png
--------------------------------------------------------------------------------
/Documentation/images/Import2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/Import2.png
--------------------------------------------------------------------------------
/Documentation/images/Import3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/Import3.png
--------------------------------------------------------------------------------
/Documentation/images/LiberateBook1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/LiberateBook1.png
--------------------------------------------------------------------------------
/Documentation/images/LiberateBook2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/LiberateBook2.png
--------------------------------------------------------------------------------
/Documentation/images/LiberateBook3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/LiberateBook3.png
--------------------------------------------------------------------------------
/Documentation/images/LiberateBook4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/LiberateBook4.png
--------------------------------------------------------------------------------
/Documentation/images/LiberateBook5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/LiberateBook5.png
--------------------------------------------------------------------------------
/Documentation/images/PdfDownload2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/PdfDownload2.png
--------------------------------------------------------------------------------
/Documentation/images/PdfDownload3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/PdfDownload3.png
--------------------------------------------------------------------------------
/Documentation/images/PostDownload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/PostDownload.png
--------------------------------------------------------------------------------
/Documentation/images/SearchExampleAutoBio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/SearchExampleAutoBio.png
--------------------------------------------------------------------------------
/Documentation/images/SearchExampleBio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/SearchExampleBio.png
--------------------------------------------------------------------------------
/Documentation/images/SearchExampleGaimanAuthorNarrated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/SearchExampleGaimanAuthorNarrated.png
--------------------------------------------------------------------------------
/Documentation/images/SearchExampleHarryPotter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/SearchExampleHarryPotter.png
--------------------------------------------------------------------------------
/Documentation/images/SearchExamplePotter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/SearchExamplePotter.png
--------------------------------------------------------------------------------
/Documentation/images/SearchExamplePotterNotHarry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/SearchExamplePotterNotHarry.png
--------------------------------------------------------------------------------
/Documentation/images/SearchExamplePotterNotHarry2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/SearchExamplePotterNotHarry2.png
--------------------------------------------------------------------------------
/Documentation/images/Tags1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/Tags1.png
--------------------------------------------------------------------------------
/Documentation/images/Tags2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/Tags2.png
--------------------------------------------------------------------------------
/Documentation/images/Tags3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/Tags3.png
--------------------------------------------------------------------------------
/Documentation/images/alt-login1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/alt-login1.png
--------------------------------------------------------------------------------
/Documentation/images/alt-login2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/alt-login2.png
--------------------------------------------------------------------------------
/Documentation/images/alt-login3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/alt-login3.png
--------------------------------------------------------------------------------
/Documentation/images/alt-login4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/alt-login4.png
--------------------------------------------------------------------------------
/Documentation/images/v40_accounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/v40_accounts.png
--------------------------------------------------------------------------------
/Documentation/images/v40_import.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/v40_import.png
--------------------------------------------------------------------------------
/Documentation/images/v40_locales.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Documentation/images/v40_locales.png
--------------------------------------------------------------------------------
/Images/Plus Minus.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Images/Plus Minus.psd
--------------------------------------------------------------------------------
/Images/Stoplight with pdf.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Images/Stoplight with pdf.psd
--------------------------------------------------------------------------------
/Images/Stoplight.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Images/Stoplight.psd
--------------------------------------------------------------------------------
/Images/libation_cheers.svg:
--------------------------------------------------------------------------------
1 |
33 |
--------------------------------------------------------------------------------
/Images/libation_hangover.svg:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/Images/libation_slosh.svg:
--------------------------------------------------------------------------------
1 |
34 |
--------------------------------------------------------------------------------
/Source/AaxDecrypter/AaxDecrypter.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 | embedded
9 |
10 |
11 |
12 | embedded
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Source/AaxDecrypter/MultiConvertFileProperties.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AaxDecrypter
4 | {
5 | public class MultiConvertFileProperties
6 | {
7 | public string OutputFileName { get; set; }
8 | public int PartsPosition { get; set; }
9 | public int PartsTotal { get; set; }
10 | public string Title { get; set; }
11 | public DateTime FileDate { get; } = DateTime.Now;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Source/AaxDecrypter/NetworkFileStreamPersister.cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core.IO;
2 |
3 | namespace AaxDecrypter
4 | {
5 | internal class NetworkFileStreamPersister : JsonFilePersister
6 | {
7 | /// Alias for Target
8 | public NetworkFileStream NetworkFileStream => Target;
9 |
10 | /// uses path. create file if doesn't yet exist
11 | public NetworkFileStreamPersister(NetworkFileStream networkFileStream, string path, string jsonPath = null)
12 | : base(networkFileStream, path, jsonPath) { }
13 |
14 | /// load from existing file
15 | public NetworkFileStreamPersister(string path, string jsonPath = null)
16 | : base(path, jsonPath) { }
17 |
18 | protected override void Dispose(bool disposing)
19 | {
20 | if (disposing)
21 | NetworkFileStream?.Dispose();
22 | base.Dispose(disposing);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs:
--------------------------------------------------------------------------------
1 | using FileManager;
2 | using System;
3 | using System.Threading.Tasks;
4 |
5 | namespace AaxDecrypter
6 | {
7 | public class UnencryptedAudiobookDownloader : AudiobookDownloadBase
8 | {
9 | protected override long InputFilePosition => InputFileStream.WritePosition;
10 |
11 | public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, IDownloadOptions dlLic)
12 | : base(outFileName, cacheDirectory, dlLic)
13 | {
14 | AsyncSteps.Name = "Download Unencrypted Audiobook";
15 | AsyncSteps["Step 1: Download Audiobook"] = Step_DownloadAndDecryptAudiobookAsync;
16 | AsyncSteps["Step 2: Download Clips and Bookmarks"] = Step_DownloadClipsBookmarksAsync;
17 | AsyncSteps["Step 3: Create Cue"] = Step_CreateCueAsync;
18 | }
19 |
20 | public override Task CancelAsync()
21 | {
22 | IsCanceled = true;
23 | FinalizeDownload();
24 | return Task.CompletedTask;
25 | }
26 |
27 | protected override async Task Step_DownloadAndDecryptAudiobookAsync()
28 | {
29 | await InputFileStream.DownloadTask;
30 |
31 | if (IsCanceled)
32 | return false;
33 | else
34 | {
35 | FinalizeDownload();
36 | FileUtility.SaferMove(InputFileStream.SaveFilePath, OutputFileName);
37 | OnFileCreated(OutputFileName);
38 | return true;
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Source/AppScaffolding/AppScaffolding.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | 12.4.3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | embedded
22 |
23 |
24 | embedded
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Source/AppScaffolding/UpgradeProperties.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace AppScaffolding
5 | {
6 | public partial record UpgradeProperties
7 | {
8 | public string ZipUrl { get; }
9 | public string HtmlUrl { get; }
10 | public string ZipName { get; }
11 | public Version LatestRelease { get; }
12 | public string Notes { get; }
13 |
14 | public UpgradeProperties(string zipUrl, string htmlUrl, string zipName, Version latestRelease, string notes)
15 | {
16 | ZipName = zipName;
17 | HtmlUrl = htmlUrl;
18 | ZipUrl = zipUrl;
19 | LatestRelease = latestRelease;
20 | Notes = LinkStripRegex().Replace(notes, "$1");
21 | }
22 |
23 | [GeneratedRegex(@"\[(.*)\]\(.*\)")]
24 | private static partial Regex LinkStripRegex();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/ApplicationServices/ApplicationServices.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | embedded
19 |
20 |
21 |
22 | embedded
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Source/ApplicationServices/DbContexts.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using DataLayer;
4 | using LibationFileManager;
5 |
6 | namespace ApplicationServices
7 | {
8 | public static class DbContexts
9 | {
10 | /// Use for fully functional context, incl. SaveChanges(). For query-only, use the other method
11 | public static LibationContext GetContext()
12 | => LibationContext.Create(SqliteStorage.ConnectionString);
13 |
14 | /// Use for full library querying. No lazy loading
15 | public static List GetLibrary_Flat_NoTracking(bool includeParents = false)
16 | {
17 | using var context = GetContext();
18 | return context.GetLibrary_Flat_NoTracking(includeParents);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Source/AudibleUtilities/AccountsSettingsPersister.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AudibleApi.Authorization;
3 | using Dinah.Core.IO;
4 | using Newtonsoft.Json;
5 |
6 | namespace AudibleUtilities
7 | {
8 | public class AccountsSettingsPersister : JsonFilePersister
9 | {
10 | public static event EventHandler Saving;
11 | public static event EventHandler Saved;
12 |
13 | protected override void OnSaving() => Saving?.Invoke(null, null);
14 | protected override void OnSaved() => Saved?.Invoke(null, null);
15 |
16 | /// Alias for Target
17 | public AccountsSettings AccountsSettings => Target;
18 |
19 | /// uses path. create file if doesn't yet exist
20 | public AccountsSettingsPersister(AccountsSettings target, string path, string jsonPath = null)
21 | : base(target, path, jsonPath) { }
22 |
23 | /// load from existing file
24 | public AccountsSettingsPersister(string path, string jsonPath = null)
25 | : base(path, jsonPath) { }
26 |
27 | protected override JsonSerializerSettings GetSerializerSettings()
28 | => Identity.GetJsonSerializerSettings();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Source/AudibleUtilities/AudibleUtilities.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | embedded
18 |
19 |
20 |
21 | embedded
22 |
23 |
24 |
25 |
26 | Cdm.cs
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Source/AudibleUtilities/ImportValidationException.cs:
--------------------------------------------------------------------------------
1 | using AudibleApi.Common;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | namespace AudibleUtilities
6 | {
7 | public class ImportValidationException : AggregateException
8 | {
9 | public List- Items { get; }
10 | public ImportValidationException(List
- items, IEnumerable exceptions) : base(exceptions)
11 | {
12 | Items = items;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Source/AudibleUtilities/Widevine/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | #nullable enable
4 | namespace AudibleUtilities.Widevine;
5 |
6 | internal static class Extensions
7 | {
8 | public static T[] Append(this T[] message, T[] appendData)
9 | {
10 | var origLength = message.Length;
11 | Array.Resize(ref message, origLength + appendData.Length);
12 | Array.Copy(appendData, 0, message, origLength, appendData.Length);
13 | return message;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Source/AudibleUtilities/_InternalsVisible.cs:
--------------------------------------------------------------------------------
1 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(nameof(AudibleUtilities) + ".Tests")]
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/BookCategoryConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class BookCategoryConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | entity.HasKey(bc => new { bc.BookId, bc.CategoryLadderId });
11 |
12 | entity.HasIndex(bc => bc.BookId);
13 | entity.HasIndex(bc => bc.CategoryLadderId);
14 |
15 | entity
16 | .HasOne(bc => bc.Book)
17 | .WithMany(b => b.CategoriesLink)
18 | .HasForeignKey(bc => bc.BookId);
19 |
20 | entity
21 | .HasOne(bc => bc.CategoryLadder)
22 | .WithMany(c => c.BooksLink)
23 | .HasForeignKey(bc => bc.CategoryLadderId);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/BookContributorConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class BookContributorConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | entity.HasKey(bc => new { bc.BookId, bc.ContributorId, bc.Role });
11 |
12 | entity.HasIndex(bc => bc.BookId);
13 | entity.HasIndex(bc => bc.ContributorId);
14 |
15 | entity
16 | .HasOne(bc => bc.Book)
17 | .WithMany(b => b.ContributorsLink)
18 | .HasForeignKey(bc => bc.BookId);
19 | entity
20 | .HasOne(bc => bc.Contributor)
21 | .WithMany(c => c.BooksLink)
22 | .HasForeignKey(bc => bc.ContributorId);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/CategoryConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class CategoryConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | entity.HasKey(c => c.CategoryId);
11 | entity.HasIndex(c => c.AudibleCategoryId);
12 |
13 | entity.Ignore(c => c.CategoryLadders);
14 |
15 | entity
16 | .HasMany(e => e._categoryLadders)
17 | .WithMany(e => e._categories);
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/CategoryLadderConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class CategoryLadderConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | entity.HasKey(cl => cl.CategoryLadderId);
11 |
12 | entity.Ignore(cl => cl.Categories);
13 |
14 | entity
15 | .HasMany(cl => cl._categories)
16 | .WithMany(c => c._categoryLadders);
17 |
18 | entity
19 | .Metadata
20 | .FindNavigation(nameof(CategoryLadder.BooksLink))
21 | .SetPropertyAccessMode(PropertyAccessMode.Field);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/ContributorConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class ContributorConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | entity.HasKey(c => c.ContributorId);
11 | entity.HasIndex(c => c.Name);
12 |
13 | //entity.OwnsOne(b => b.AuthorProperty);
14 | // ... in separate table
15 |
16 | entity
17 | .Metadata
18 | .FindNavigation(nameof(Contributor.BooksLink))
19 | .SetPropertyAccessMode(PropertyAccessMode.Field);
20 |
21 | // seeds go here. examples in Dinah.EntityFrameworkCore.Tests\DbContextFactoryExample.cs
22 | entity.HasData(Contributor.GetEmpty());
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/LibraryBookConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class LibraryBookConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | // to allow same book (incl region) with diff acct.s:
11 | //
12 | // this file:
13 | // - composite key:
14 | // entity.HasKey(b => new { b.BookId, b.Account });
15 | // entity.HasIndex(b => b.BookId);
16 | // entity.HasIndex(b => b.Account);
17 | // - change the below relationship since Book+LibraryBook would no longer be 1:1
18 | //
19 | // other files:
20 | // - change Book class since Book+LibraryBook would no longer be 1:1
21 | // - update LibraryBook import code
22 | // - would likely challenge assumptions throughout Libation which have been true up until now
23 |
24 | entity.HasKey(lb => lb.BookId);
25 |
26 | entity
27 | .HasOne(lb => lb.Book)
28 | .WithOne()
29 | .HasForeignKey(lb => lb.BookId);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/SeriesBookConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class SeriesBookConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | entity.HasKey(sb => new { sb.SeriesId, sb.BookId });
11 |
12 | entity.HasIndex(sb => sb.SeriesId);
13 | entity.HasIndex(sb => sb.BookId);
14 |
15 | entity
16 | .HasOne(sb => sb.Series)
17 | .WithMany(s => s.BooksLink)
18 | .HasForeignKey(sb => sb.SeriesId);
19 | entity
20 | .HasOne(sb => sb.Book)
21 | .WithMany(b => b.SeriesLink)
22 | .HasForeignKey(sb => sb.BookId);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Source/DataLayer/Configurations/SeriesConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace DataLayer.Configurations
5 | {
6 | internal class SeriesConfig : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder entity)
9 | {
10 | entity.HasKey(s => s.SeriesId);
11 | entity.HasIndex(s => s.AudibleSeriesId);
12 |
13 | entity
14 | .Metadata
15 | .FindNavigation(nameof(Series.BooksLink))
16 | .SetPropertyAccessMode(PropertyAccessMode.Field);
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Source/DataLayer/EfClasses/BookCategory.cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core;
2 |
3 | namespace DataLayer
4 | {
5 | public class BookCategory
6 | {
7 | internal int BookId { get; private set; }
8 | internal int CategoryLadderId { get; private set; }
9 |
10 | public Book Book { get; private set; }
11 | public CategoryLadder CategoryLadder { get; private set; }
12 | private BookCategory() { }
13 |
14 | internal BookCategory(Book book, CategoryLadder categoriesList)
15 | {
16 | Book = ArgumentValidator.EnsureNotNull(book, nameof(book));
17 | CategoryLadder = ArgumentValidator.EnsureNotNull(categoriesList, nameof(categoriesList));
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Source/DataLayer/EfClasses/BookContributor.cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core;
2 |
3 | namespace DataLayer
4 | {
5 | public enum Role { Author = 1, Narrator = 2, Publisher = 3 }
6 |
7 | public class BookContributor
8 | {
9 | internal int BookId { get; private set; }
10 | internal int ContributorId { get; private set; }
11 | public Role Role { get; private set; }
12 | public byte Order { get; private set; }
13 |
14 | public Book Book { get; private set; }
15 | public Contributor Contributor { get; private set; }
16 |
17 | private BookContributor() { }
18 | internal BookContributor(Book book, Contributor contributor, Role role, byte order)
19 | {
20 | ArgumentValidator.EnsureNotNull(book, nameof(book));
21 | ArgumentValidator.EnsureNotNull(contributor, nameof(contributor));
22 |
23 | Book = book;
24 | Contributor = contributor;
25 | Role = role;
26 | Order = order;
27 | }
28 |
29 | public override string ToString() => $"{Book} {Contributor} {Role} {Order}";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/DataLayer/EfClasses/LibraryBook.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Dinah.Core;
3 |
4 | namespace DataLayer
5 | {
6 | public class LibraryBook
7 | {
8 | internal int BookId { get; private set; }
9 | public Book Book { get; private set; }
10 |
11 | public DateTime DateAdded { get; private set; }
12 | public string Account { get; private set; }
13 |
14 | public bool IsDeleted { get; set; }
15 | public bool AbsentFromLastScan { get; set; }
16 |
17 | private LibraryBook() { }
18 | public LibraryBook(Book book, DateTime dateAdded, string account)
19 | {
20 | ArgumentValidator.EnsureNotNull(book, nameof(book));
21 | ArgumentValidator.EnsureNotNull(account, nameof(account));
22 |
23 | Book = book;
24 | DateAdded = dateAdded;
25 | Account = account;
26 | }
27 |
28 | public void SetAccount(string account) => Account = account;
29 |
30 | public override string ToString() => $"{DateAdded:d} {Book}";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Source/DataLayer/EfClasses/SeriesBook.cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core;
2 |
3 | namespace DataLayer
4 | {
5 | public class SeriesBook
6 | {
7 | internal int SeriesId { get; private set; }
8 | internal int BookId { get; private set; }
9 |
10 | public string Order { get; private set; }
11 | public float Index => StringLib.ExtractFirstNumber(Order);
12 |
13 | public Series Series { get; private set; }
14 | public Book Book { get; private set; }
15 |
16 | private SeriesBook() { }
17 | internal SeriesBook(Series series, Book book, string order)
18 | {
19 | ArgumentValidator.EnsureNotNull(series, nameof(series));
20 | ArgumentValidator.EnsureNotNull(book, nameof(book));
21 |
22 | Series = series;
23 | Book = book;
24 | Order = order;
25 | }
26 |
27 | public void UpdateOrder(string order)
28 | {
29 | if (!string.IsNullOrWhiteSpace(order))
30 | Order = order;
31 | }
32 |
33 | public override string ToString() => $"Series={Series} Book={Book}";
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/DataLayer/EfClasses/Supplement.cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core;
2 |
3 | namespace DataLayer
4 | {
5 | /// PDF/ZIP files only. Although book download info could be the same format, they're substantially different and subject to change
6 | public class Supplement
7 | {
8 | internal int SupplementId { get; private set; }
9 | internal int BookId { get; private set; }
10 |
11 | public Book Book { get; private set; }
12 | public string Url { get; private set; }
13 |
14 | private Supplement() { }
15 | public Supplement(Book book, string url)
16 | {
17 | ArgumentValidator.EnsureNotNull(book, nameof(book));
18 | ArgumentValidator.EnsureNotNullOrWhiteSpace(url, nameof(url));
19 |
20 | Book = book;
21 | Url = url;
22 | }
23 |
24 | public override string ToString() => $"{Book} {Url.Substring(Url.Length - 4)}";
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/DataLayer/Formatters.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace DataLayer
6 | {
7 | internal class Formatters
8 | {
9 | private static string[] _sortPrefixIgnores { get; } = { "the", "a", "an" };
10 |
11 | public static string GetSortName(string unformattedName)
12 | {
13 | var sortName = unformattedName
14 | .Replace("|", "")
15 | .Replace(":", "")
16 | .ToLowerInvariant()
17 | .Trim();
18 |
19 | if (_sortPrefixIgnores.Any(prefix => sortName.StartsWith(prefix + " ")))
20 | sortName = sortName
21 | .Substring(sortName.IndexOf(" ") + 1)
22 | .TrimStart();
23 |
24 | return sortName;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Source/DataLayer/LibationContextFactory.cs:
--------------------------------------------------------------------------------
1 | using Dinah.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace DataLayer
5 | {
6 | public class LibationContextFactory : DesignTimeDbContextFactoryBase
7 | {
8 | protected override LibationContext CreateNewInstance(DbContextOptions options) => new LibationContext(options);
9 | protected override void UseDatabaseEngine(DbContextOptionsBuilder optionsBuilder, string connectionString)
10 | => optionsBuilder.UseSqlite(connectionString, ob => ob.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20200812152646_AddLocaleAndAccount.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace DataLayer.Migrations
4 | {
5 | public partial class AddLocaleAndAccount : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.AddColumn(
10 | name: "Account",
11 | table: "Library",
12 | nullable: true);
13 |
14 | migrationBuilder.AddColumn(
15 | name: "Locale",
16 | table: "Books",
17 | nullable: true);
18 | }
19 |
20 | protected override void Down(MigrationBuilder migrationBuilder)
21 | {
22 | migrationBuilder.DropColumn(
23 | name: "Account",
24 | table: "Library");
25 |
26 | migrationBuilder.DropColumn(
27 | name: "Locale",
28 | table: "Books");
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20210619030017_AddAaxcDecryptionKeys.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace DataLayer.Migrations
4 | {
5 | public partial class AddAaxcDecryptionKeys : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.AddColumn(
10 | name: "AudibleIV",
11 | table: "Books",
12 | type: "TEXT",
13 | nullable: true);
14 |
15 | migrationBuilder.AddColumn(
16 | name: "AudibleKey",
17 | table: "Books",
18 | type: "TEXT",
19 | nullable: true);
20 | }
21 |
22 | protected override void Down(MigrationBuilder migrationBuilder)
23 | {
24 | migrationBuilder.DropColumn(
25 | name: "AudibleIV",
26 | table: "Books");
27 |
28 | migrationBuilder.DropColumn(
29 | name: "AudibleKey",
30 | table: "Books");
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20210622205558_RemoveAaxcDecryptionKeys.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace DataLayer.Migrations
4 | {
5 | public partial class RemoveAaxcDecryptionKeys : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.DropColumn(
10 | name: "AudibleIV",
11 | table: "Books");
12 |
13 | migrationBuilder.DropColumn(
14 | name: "AudibleKey",
15 | table: "Books");
16 | }
17 |
18 | protected override void Down(MigrationBuilder migrationBuilder)
19 | {
20 | migrationBuilder.AddColumn(
21 | name: "AudibleIV",
22 | table: "Books",
23 | type: "TEXT",
24 | nullable: true);
25 |
26 | migrationBuilder.AddColumn(
27 | name: "AudibleKey",
28 | table: "Books",
29 | type: "TEXT",
30 | nullable: true);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20210821012137_RemoveUdiBookLocation.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace DataLayer.Migrations
4 | {
5 | public partial class RemoveUdiBookLocation : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.DropColumn(
10 | name: "BookLocation",
11 | table: "UserDefinedItem");
12 | }
13 |
14 | protected override void Down(MigrationBuilder migrationBuilder)
15 | {
16 | migrationBuilder.AddColumn(
17 | name: "BookLocation",
18 | table: "UserDefinedItem",
19 | type: "TEXT",
20 | nullable: true);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20210901205042_BookIsEpisode.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace DataLayer.Migrations
4 | {
5 | public partial class BookIsEpisode : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.AddColumn(
10 | name: "ContentType",
11 | table: "Books",
12 | type: "INTEGER",
13 | nullable: false,
14 | defaultValue: 0);
15 | }
16 |
17 | protected override void Down(MigrationBuilder migrationBuilder)
18 | {
19 | migrationBuilder.DropColumn(
20 | name: "ContentType",
21 | table: "Books");
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20210922154900_AddSeriesOrderString.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | namespace DataLayer.Migrations
4 | {
5 | public partial class AddSeriesOrderString : Migration
6 | {
7 | protected override void Up(MigrationBuilder migrationBuilder)
8 | {
9 | migrationBuilder.DropColumn(
10 | name: "Index",
11 | table: "SeriesBook");
12 |
13 | migrationBuilder.AddColumn(
14 | name: "Order",
15 | table: "SeriesBook",
16 | type: "TEXT",
17 | nullable: true);
18 | }
19 |
20 | protected override void Down(MigrationBuilder migrationBuilder)
21 | {
22 | migrationBuilder.DropColumn(
23 | name: "Order",
24 | table: "SeriesBook");
25 |
26 | migrationBuilder.AddColumn(
27 | name: "Index",
28 | table: "SeriesBook",
29 | type: "REAL",
30 | nullable: true);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20220510175257_AddPictureIDLargeMigration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace DataLayer.Migrations
6 | {
7 | public partial class AddPictureIDLargeMigration : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.AddColumn(
12 | name: "PictureLarge",
13 | table: "Books",
14 | type: "TEXT",
15 | nullable: true);
16 | }
17 |
18 | protected override void Down(MigrationBuilder migrationBuilder)
19 | {
20 | migrationBuilder.DropColumn(
21 | name: "PictureLarge",
22 | table: "Books");
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20220624214932_AddAudioFormat.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace DataLayer.Migrations
6 | {
7 | public partial class AddAudioFormat : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.AddColumn(
12 | name: "_audioFormat",
13 | table: "Books",
14 | type: "INTEGER",
15 | nullable: false,
16 | defaultValue: 0L);
17 | }
18 |
19 | protected override void Down(MigrationBuilder migrationBuilder)
20 | {
21 | migrationBuilder.DropColumn(
22 | name: "_audioFormat",
23 | table: "Books");
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20221214205106_LibraryBookIsDeleted.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace DataLayer.Migrations
6 | {
7 | ///
8 | public partial class LibraryBookIsDeleted : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AddColumn(
14 | name: "IsDeleted",
15 | table: "LibraryBooks",
16 | type: "INTEGER",
17 | nullable: false,
18 | defaultValue: false);
19 | }
20 |
21 | ///
22 | protected override void Down(MigrationBuilder migrationBuilder)
23 | {
24 | migrationBuilder.DropColumn(
25 | name: "IsDeleted",
26 | table: "LibraryBooks");
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20230201162454_AddBookLanguage.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace DataLayer.Migrations
6 | {
7 | ///
8 | public partial class AddBookLanguage : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AddColumn(
14 | name: "Language",
15 | table: "Books",
16 | type: "TEXT",
17 | nullable: true);
18 | }
19 |
20 | ///
21 | protected override void Down(MigrationBuilder migrationBuilder)
22 | {
23 | migrationBuilder.DropColumn(
24 | name: "Language",
25 | table: "Books");
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20230302220539_AddLastDownloadedInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 |
4 | #nullable disable
5 |
6 | namespace DataLayer.Migrations
7 | {
8 | ///
9 | public partial class AddLastDownloadedInfo : Migration
10 | {
11 | ///
12 | protected override void Up(MigrationBuilder migrationBuilder)
13 | {
14 | migrationBuilder.AddColumn(
15 | name: "LastDownloaded",
16 | table: "UserDefinedItem",
17 | type: "TEXT",
18 | nullable: true);
19 |
20 | migrationBuilder.AddColumn(
21 | name: "LastDownloadedVersion",
22 | table: "UserDefinedItem",
23 | type: "TEXT",
24 | nullable: true);
25 | }
26 |
27 | ///
28 | protected override void Down(MigrationBuilder migrationBuilder)
29 | {
30 | migrationBuilder.DropColumn(
31 | name: "LastDownloaded",
32 | table: "UserDefinedItem");
33 |
34 | migrationBuilder.DropColumn(
35 | name: "LastDownloadedVersion",
36 | table: "UserDefinedItem");
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20230308013410_AddAbsentFromLastScan.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace DataLayer.Migrations
6 | {
7 | ///
8 | public partial class AddAbsentFromLastScan : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AddColumn(
14 | name: "AbsentFromLastScan",
15 | table: "LibraryBooks",
16 | type: "INTEGER",
17 | nullable: false,
18 | defaultValue: false);
19 | }
20 |
21 | ///
22 | protected override void Down(MigrationBuilder migrationBuilder)
23 | {
24 | migrationBuilder.DropColumn(
25 | name: "AbsentFromLastScan",
26 | table: "LibraryBooks");
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20230626171442_AddBookSubtitle.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace DataLayer.Migrations
6 | {
7 | ///
8 | public partial class AddBookSubtitle : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AddColumn(
14 | name: "Subtitle",
15 | table: "Books",
16 | type: "TEXT",
17 | nullable: true);
18 | }
19 |
20 | ///
21 | protected override void Down(MigrationBuilder migrationBuilder)
22 | {
23 | migrationBuilder.DropColumn(
24 | name: "Subtitle",
25 | table: "Books");
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Source/DataLayer/Migrations/20240911114741_MyComment.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Migrations;
2 |
3 | #nullable disable
4 |
5 | namespace DataLayer.Migrations
6 | {
7 | ///
8 | public partial class MyComment : Migration
9 | {
10 | ///
11 | protected override void Up(MigrationBuilder migrationBuilder)
12 | {
13 | migrationBuilder.AddColumn(
14 | name: "IsFinished",
15 | table: "UserDefinedItem",
16 | type: "INTEGER",
17 | nullable: false,
18 | defaultValue: false);
19 | }
20 |
21 | ///
22 | protected override void Down(MigrationBuilder migrationBuilder)
23 | {
24 | migrationBuilder.DropColumn(
25 | name: "IsFinished",
26 | table: "UserDefinedItem");
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/DataLayer/QueryObjects/CategoryQueries.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using System.Linq;
3 |
4 | namespace DataLayer
5 | {
6 | public static class CategoryQueries
7 | {
8 | public static IQueryable GetCategoryLadders(this LibationContext context)
9 | => context.CategoryLadders.Include(c => c._categories);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/DataLayer/migrate.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "LibationContext": "Data Source=LibationContext.db;Foreign Keys=False;"
4 | }
5 | }
--------------------------------------------------------------------------------
/Source/DtoImporterService/DtoImporterService.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 | embedded
9 |
10 |
11 |
12 | embedded
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Source/DtoImporterService/ImportItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AudibleApi.Common;
3 |
4 | namespace DtoImporterService
5 | {
6 | public class ImportItem
7 | {
8 | public Item DtoItem { get; set; }
9 | public string AccountId { get; set; }
10 | public string LocaleName { get; set; }
11 | public override string ToString()
12 | => DtoItem is null ? base.ToString() : $"[{DtoItem.ProductId}] {DtoItem.Title}";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Source/DtoImporterService/PerfLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 |
6 | namespace DtoImporterService
7 | {
8 | public record timeLogEntry(string msg, long totalElapsed, long delta);
9 | public static class PerfLogger
10 | {
11 | private static Stopwatch sw { get; } = new();
12 | private static List __log { get; } = new() { new("begin", 0, 0) };
13 |
14 | public static void logTime(string s)
15 | {
16 | var totalElapsed = sw.ElapsedMilliseconds;
17 |
18 | var prev = __log.Last().totalElapsed;
19 | var delta = totalElapsed - prev;
20 |
21 | __log.Add(new(s, totalElapsed, delta));
22 | }
23 | public static void logRestart()
24 | {
25 | __log.Clear();
26 | __log.Add(new("begin", 0, 0));
27 | sw.Restart();
28 | }
29 | public static void stop() => sw.Stop();
30 | public static string logOutput =>
31 | $"{nameof(timeLogEntry.msg)}\t{nameof(timeLogEntry.totalElapsed)}\t{nameof(timeLogEntry.delta)}\r\n"
32 | + __log.Select(t => $"{t.msg}\t{t.totalElapsed}\t{t.delta}").Aggregate((a, b) => $"{a}\r\n{b}");
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Source/FileLiberator/FileLiberator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | embedded
16 |
17 |
18 |
19 | embedded
20 |
21 |
22 |
23 |
24 | DownloadOptions.cs
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Source/FileManager/FileManager.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | embedded
14 |
15 |
16 |
17 | embedded
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Source/FileManager/NamingTemplate/ITemplateTag.cs:
--------------------------------------------------------------------------------
1 | namespace FileManager.NamingTemplate;
2 |
3 | public interface ITemplateTag
4 | {
5 | string TagName { get; }
6 | }
7 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/App.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls.ApplicationLifetimes;
3 | using Avalonia.Markup.Xaml;
4 | using HangoverAvalonia.ViewModels;
5 | using HangoverAvalonia.Views;
6 |
7 | namespace HangoverAvalonia
8 | {
9 | public partial class App : Application
10 | {
11 | public override void Initialize()
12 | {
13 | AvaloniaXamlLoader.Load(this);
14 | }
15 |
16 | public override void OnFrameworkInitializationCompleted()
17 | {
18 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
19 | {
20 | var mainWindow = new MainWindow
21 | {
22 | DataContext = new MainVM(),
23 | };
24 | desktop.MainWindow = mainWindow;
25 | mainWindow.OnLoad();
26 | }
27 |
28 | base.OnFrameworkInitializationCompleted();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Assets/hangover.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverAvalonia/Assets/hangover.ico
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Controls/CheckedListBox.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Controls/CheckedListBox.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Collections;
3 | using Avalonia.Controls;
4 | using HangoverAvalonia.ViewModels;
5 |
6 | namespace HangoverAvalonia.Controls;
7 |
8 | public partial class CheckedListBox : UserControl
9 | {
10 | public static readonly StyledProperty> ItemsProperty =
11 | AvaloniaProperty.Register>(nameof(Items));
12 |
13 | public AvaloniaList Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); }
14 |
15 | public CheckedListBox()
16 | {
17 | InitializeComponent();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Program.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.ReactiveUI;
3 | using System;
4 |
5 | namespace HangoverAvalonia
6 | {
7 | internal class Program
8 | {
9 | // Initialization code. Don't use any Avalonia, third-party APIs or any
10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
11 | // yet and stuff might break.
12 | [STAThread]
13 | public static void Main(string[] args) => BuildAvaloniaApp()
14 | .StartWithClassicDesktopLifetime(args);
15 |
16 | // Avalonia configuration, don't remove; also used by visual designer.
17 | public static AppBuilder BuildAvaloniaApp()
18 | => AppBuilder.Configure()
19 | .UsePlatformDetect()
20 | .LogToTrace()
21 | .UseReactiveUI();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Properties/PublishProfiles/LinuxProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\Linux-chardonnay
10 | FileSystem
11 | net9.0
12 | linux-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Properties/PublishProfiles/MacOSProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\MacOS-chardonnay
10 | FileSystem
11 | net9.0
12 | osx-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Properties/PublishProfiles/WindowsProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\Windows-chardonnay
10 | FileSystem
11 | net9.0
12 | win-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/ViewLocator.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using Avalonia.Controls.Templates;
3 | using HangoverAvalonia.ViewModels;
4 | using System;
5 |
6 | namespace HangoverAvalonia
7 | {
8 | public class ViewLocator : IDataTemplate
9 | {
10 | public Control Build(object data)
11 | {
12 | var name = data.GetType().FullName!.Replace("ViewModel", "View");
13 | var type = Type.GetType(name);
14 |
15 | if (type != null)
16 | {
17 | return (Control)Activator.CreateInstance(type)!;
18 | }
19 | else
20 | {
21 | return new TextBlock { Text = "Not Found: " + name };
22 | }
23 | }
24 |
25 | public bool Match(object data)
26 | {
27 | return data is ViewModelBase;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/ViewModels/CheckBoxViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace HangoverAvalonia.ViewModels;
4 |
5 | public class CheckBoxViewModel : ViewModelBase
6 | {
7 | private bool _isChecked;
8 | public bool IsChecked { get => _isChecked; set => this.RaiseAndSetIfChanged(ref _isChecked, value); }
9 | private object _bookText;
10 | public object Item { get => _bookText; set => this.RaiseAndSetIfChanged(ref _bookText, value); }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/ViewModels/MainVM.Database.cs:
--------------------------------------------------------------------------------
1 | using HangoverBase;
2 | using ReactiveUI;
3 |
4 | namespace HangoverAvalonia.ViewModels
5 | {
6 | public partial class MainVM
7 | {
8 | private DatabaseTab _tab;
9 |
10 | private string _databaseFileText;
11 | private bool _databaseFound;
12 | private string _sqlResults;
13 | public string DatabaseFileText { get => _databaseFileText; set => this.RaiseAndSetIfChanged(ref _databaseFileText, value); }
14 | public string SqlQuery { get; set; }
15 | public bool DatabaseFound { get => _databaseFound; set => this.RaiseAndSetIfChanged(ref _databaseFound, value); }
16 | public string SqlResults { get => _sqlResults; set => this.RaiseAndSetIfChanged(ref _sqlResults, value); }
17 |
18 | private void Load_databaseVM()
19 | {
20 | _tab = new(new DatabaseTabCommands(() => SqlQuery, s => SqlResults += s, s => SqlResults = s));
21 |
22 | _tab.LoadDatabaseFile();
23 | if (_tab.DbFile is null)
24 | {
25 | DatabaseFileText = $"Database file not found";
26 | DatabaseFound = false;
27 | return;
28 | }
29 |
30 | DatabaseFileText = $"Database file: {_tab.DbFile}";
31 | DatabaseFound = true;
32 | }
33 |
34 | public void ExecuteQuery() => _tab.ExecuteQuery();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/ViewModels/MainVM.Deleted.cs:
--------------------------------------------------------------------------------
1 | namespace HangoverAvalonia.ViewModels;
2 |
3 | public partial class MainVM
4 | {
5 | public TrashBinViewModel TrashBinViewModel { get; } = new();
6 |
7 | private void Load_deletedVM() { }
8 | }
9 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/ViewModels/MainVM.cs:
--------------------------------------------------------------------------------
1 | namespace HangoverAvalonia.ViewModels
2 | {
3 | public partial class MainVM : ViewModelBase
4 | {
5 | public MainVM()
6 | {
7 | Load_databaseVM();
8 | Load_deletedVM();
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/ViewModels/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace HangoverAvalonia.ViewModels
4 | {
5 | public class ViewModelBase : ReactiveObject
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Views/MainWindow.CLI.cs:
--------------------------------------------------------------------------------
1 | namespace HangoverAvalonia.Views
2 | {
3 | public partial class MainWindow
4 | {
5 | private void cliTab_VisibleChanged(bool isVisible)
6 | {
7 | if (!isVisible)
8 | return;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Views/MainWindow.Database.cs:
--------------------------------------------------------------------------------
1 | namespace HangoverAvalonia.Views
2 | {
3 | public partial class MainWindow
4 | {
5 | private void databaseTab_VisibleChanged(bool isVisible)
6 | {
7 | if (!isVisible)
8 | return;
9 | }
10 |
11 | public void Execute_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
12 | {
13 | _viewModel.ExecuteQuery();
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Views/MainWindow.Deleted.cs:
--------------------------------------------------------------------------------
1 | namespace HangoverAvalonia.Views;
2 |
3 | public partial class MainWindow
4 | {
5 | private void deletedTab_VisibleChanged(bool isVisible)
6 | {
7 | if (!isVisible)
8 | return;
9 |
10 | _viewModel.TrashBinViewModel.Reload();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/Views/MainWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using AppScaffolding;
2 | using Avalonia.Controls;
3 | using HangoverAvalonia.ViewModels;
4 |
5 | namespace HangoverAvalonia.Views
6 | {
7 | public partial class MainWindow : Window
8 | {
9 | MainVM _viewModel => DataContext as MainVM;
10 | public MainWindow()
11 | {
12 | InitializeComponent();
13 |
14 | var config = LibationScaffolding.RunPreConfigMigrations();
15 | LibationScaffolding.RunPostConfigMigrations(config);
16 | LibationScaffolding.RunPostMigrationScaffolding(Variety.Chardonnay, config);
17 | }
18 |
19 | public void OnLoad()
20 | {
21 | databaseTab.PropertyChanged += (_, e) => { if (e.Property.Name == nameof(TabItem.IsSelected)) databaseTab_VisibleChanged(databaseTab.IsSelected); };
22 | deletedTab.PropertyChanged += (_, e) => { if (e.Property.Name == nameof(TabItem.IsSelected)) deletedTab_VisibleChanged(deletedTab.IsSelected); };
23 | cliTab.PropertyChanged += (_, e) => { if (e.Property.Name == nameof(TabItem.IsSelected)) cliTab_VisibleChanged(cliTab.IsSelected); };
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/HangoverAvalonia/hangover.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverAvalonia/hangover.ico
--------------------------------------------------------------------------------
/Source/HangoverBase/HangoverBase.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | embedded
15 |
16 |
17 | embedded
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Form1.CLI.cs:
--------------------------------------------------------------------------------
1 | using AppScaffolding;
2 |
3 | namespace HangoverWinForms
4 | {
5 | public partial class Form1
6 | {
7 | private void Load_cliTab()
8 | {
9 |
10 | }
11 |
12 | private void cliTab_VisibleChanged(object sender, EventArgs e)
13 | {
14 | if (!cliTab.Visible)
15 | return;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Form1.Database.cs:
--------------------------------------------------------------------------------
1 | using HangoverBase;
2 |
3 | namespace HangoverWinForms
4 | {
5 | public partial class Form1
6 | {
7 | private DatabaseTab _tab;
8 |
9 | private void Load_databaseTab()
10 | {
11 | _tab = new(new(() => sqlTb.Text, sqlResultsTb.AppendText, s => sqlResultsTb.Text = s));
12 |
13 | _tab.LoadDatabaseFile();
14 | if (_tab.DbFile is null)
15 | {
16 | databaseFileLbl.Text = $"Database file not found";
17 | return;
18 | }
19 |
20 | databaseFileLbl.Text = $"Database file: {_tab.DbFile}";
21 | }
22 |
23 | private void databaseTab_VisibleChanged(object sender, EventArgs e)
24 | {
25 | if (!databaseTab.Visible)
26 | return;
27 | }
28 |
29 | private void sqlExecuteBtn_Click(object sender, EventArgs e) => _tab.ExecuteQuery();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Form1.cs:
--------------------------------------------------------------------------------
1 | using AppScaffolding;
2 |
3 | namespace HangoverWinForms
4 | {
5 | public partial class Form1 : Form
6 | {
7 | public Form1()
8 | {
9 | InitializeComponent();
10 |
11 | var config = LibationScaffolding.RunPreConfigMigrations();
12 | LibationScaffolding.RunPostConfigMigrations(config);
13 | LibationScaffolding.RunPostMigrationScaffolding(Variety.Classic, config);
14 |
15 | databaseTab.VisibleChanged += databaseTab_VisibleChanged;
16 | cliTab.VisibleChanged += cliTab_VisibleChanged;
17 | deletedTab.VisibleChanged += deletedTab_VisibleChanged;
18 |
19 | Load_databaseTab();
20 | Load_cliTab();
21 | Load_deletedTab();
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HangoverWinForms
2 | {
3 | internal static class Program
4 | {
5 | ///
6 | /// The main entry point for the application.
7 | ///
8 | [STAThread]
9 | static void Main()
10 | {
11 | // To customize application configuration such as set high DPI settings or default font,
12 | // see https://aka.ms/applicationconfiguration.
13 | ApplicationConfiguration.Initialize();
14 | Application.Run(new Form1());
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Properties/PublishProfiles/WindowsProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\classic
10 | FileSystem
11 | net9.0-windows
12 | win-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow-PSD.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow-PSD.zip
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow.ico
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_1024.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_128.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_16.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_20.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_24.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_256.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_32.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_40.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_48.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_512.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_512x512.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_512x512.pdn
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_64.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/Resources/Hangover icon/spilled-glass-with-glow_96.png
--------------------------------------------------------------------------------
/Source/HangoverWinForms/hangover.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/HangoverWinForms/hangover.ico
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/1x1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/1x1.png
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Asterisk.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Asterisk.ico
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Asterisk_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Asterisk_64.png
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Error.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Error.ico
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Error_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Error_64.png
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Exclamation.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Exclamation.ico
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Exclamation_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Exclamation_64.png
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Question.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Question.ico
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/MBIcons/Question_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/MBIcons/Question_64.png
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/img-coverart-prod-unavailable_300x300.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/img-coverart-prod-unavailable_300x300.jpg
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/img-coverart-prod-unavailable_500x500.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/img-coverart-prod-unavailable_500x500.jpg
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/img-coverart-prod-unavailable_80x80.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/img-coverart-prod-unavailable_80x80.jpg
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Assets/libation.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationAvalonia/Assets/libation.ico
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/CheckedListBox.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Collections;
3 | using Avalonia.Controls;
4 | using LibationAvalonia.ViewModels;
5 | using ReactiveUI;
6 |
7 | namespace LibationAvalonia.Controls
8 | {
9 | public partial class CheckedListBox : UserControl
10 | {
11 | public static readonly StyledProperty> ItemsProperty =
12 | AvaloniaProperty.Register>(nameof(Items));
13 |
14 | public AvaloniaList Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); }
15 |
16 | public CheckedListBox()
17 | {
18 | InitializeComponent();
19 | }
20 | }
21 |
22 | public class CheckBoxViewModel : ViewModelBase
23 | {
24 | private bool _isChecked;
25 | public bool IsChecked { get => _isChecked; set => this.RaiseAndSetIfChanged(ref _isChecked, value); }
26 | private object _bookText;
27 | public object Item { get => _bookText; set => this.RaiseAndSetIfChanged(ref _bookText, value); }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using LibationUiBase.GridView;
3 |
4 | namespace LibationAvalonia.Controls
5 | {
6 | public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
7 | {
8 | protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem)
9 | {
10 | //Only SeriesEntry types have three-state checks, individual LibraryEntry books are binary.
11 | var ele = base.GenerateEditingElementDirect(cell, dataItem) as CheckBox;
12 | ele.IsThreeState = dataItem is ISeriesEntry;
13 | return ele;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 |
3 | namespace LibationAvalonia.Controls
4 | {
5 | public partial class DataGridTemplateColumnExt : DataGridTemplateColumn
6 | {
7 | protected override Control GenerateElement(DataGridCell cell, object dataItem)
8 | {
9 | cell?.AttachContextMenu();
10 | return base.GenerateElement(cell, dataItem);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/GroupBox.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Styling;
4 |
5 | namespace LibationAvalonia.Controls
6 | {
7 | public partial class GroupBox : ContentControl
8 | {
9 |
10 | public static readonly StyledProperty LabelProperty =
11 | AvaloniaProperty.Register(nameof(Label));
12 | public GroupBox()
13 | {
14 | InitializeComponent();
15 | Label = "This is a groupbox label";
16 | }
17 |
18 | public string Label
19 | {
20 | get { return GetValue(LabelProperty); }
21 | set { SetValue(LabelProperty, value); }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/LinkLabel.axaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/Settings/Import.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using LibationAvalonia.ViewModels.Settings;
3 | using LibationFileManager;
4 |
5 | namespace LibationAvalonia.Controls.Settings
6 | {
7 | public partial class Import : UserControl
8 | {
9 | public Import()
10 | {
11 | InitializeComponent();
12 |
13 | if (Design.IsDesignMode)
14 | {
15 | _ = Configuration.Instance.LibationFiles;
16 | DataContext = new ImportSettingsVM(Configuration.Instance);
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/WheelComboBox.axaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using Avalonia.Input;
3 | using System;
4 |
5 | namespace LibationAvalonia.Controls
6 | {
7 | public partial class WheelComboBox : ComboBox
8 | {
9 | protected override Type StyleKeyOverride => typeof(ComboBox);
10 |
11 | public WheelComboBox()
12 | {
13 | InitializeComponent();
14 | }
15 | protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
16 | {
17 | var dir = Math.Sign(e.Delta.Y);
18 | if (dir == 1 && SelectedIndex > 0)
19 | {
20 | SelectedIndex--;
21 | e.Handled = true;
22 | }
23 | else if (dir == -1 && SelectedIndex < ItemCount - 1)
24 | {
25 | SelectedIndex++;
26 | e.Handled = true;
27 | }
28 |
29 | base.OnPointerWheelChanged(e);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
14 |
15 |
23 |
24 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
16 |
17 |
23 |
24 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml.cs:
--------------------------------------------------------------------------------
1 | namespace LibationAvalonia.Dialogs
2 | {
3 | public partial class LiberatedStatusBatchAutoDialog : DialogWindow
4 | {
5 | public bool SetDownloaded { get; set; }
6 | public bool SetNotDownloaded { get; set; }
7 |
8 | public LiberatedStatusBatchAutoDialog()
9 | {
10 | InitializeComponent();
11 | DataContext = this;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/Login/ApprovalNeededDialog.axaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
20 |
21 |
25 |
26 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/Login/ApprovalNeededDialog.axaml.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace LibationAvalonia.Dialogs.Login
4 | {
5 | public partial class ApprovalNeededDialog : DialogWindow
6 | {
7 | public ApprovalNeededDialog() : base(saveAndRestorePosition: false)
8 | {
9 | InitializeComponent();
10 | }
11 |
12 | protected override Task SaveAndCloseAsync()
13 | {
14 | Serilog.Log.Logger.Information("Approve button clicked");
15 |
16 | return base.SaveAndCloseAsync();
17 | }
18 |
19 | public async void Approve_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
20 | => await SaveAndCloseAsync();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/Login/LoginCallbackDialog.axaml.cs:
--------------------------------------------------------------------------------
1 | using AudibleUtilities;
2 | using Avalonia.Controls;
3 | using Dinah.Core;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace LibationAvalonia.Dialogs.Login
8 | {
9 | public partial class LoginCallbackDialog : DialogWindow
10 | {
11 | public Account Account { get; }
12 | public string Password { get; set; }
13 |
14 | public LoginCallbackDialog() : base(saveAndRestorePosition: false)
15 | {
16 | InitializeComponent();
17 |
18 | if (Design.IsDesignMode)
19 | {
20 | using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
21 | var accounts = persister.AccountsSettings.Accounts;
22 | Account = accounts.FirstOrDefault();
23 | DataContext = this;
24 | }
25 | }
26 | public LoginCallbackDialog(Account account) : this()
27 | {
28 | Account = account;
29 | DataContext = this;
30 | }
31 |
32 | protected override Task SaveAndCloseAsync()
33 | {
34 | Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { email = Account?.AccountId?.ToMask(), passwordLength = Password?.Length });
35 |
36 | return base.SaveAndCloseAsync();
37 | }
38 |
39 | public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
40 | => await SaveAndCloseAsync();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/Login/MfaDialog.axaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/Login/WebLoginDialog.axaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/Login/_2faCodeDialog.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using System.Threading.Tasks;
3 |
4 | namespace LibationAvalonia.Dialogs.Login
5 | {
6 | public partial class _2faCodeDialog : DialogWindow
7 | {
8 | public string Code { get; set; }
9 | public string Prompt { get; } = "For added security, please enter the One Time Password (OTP) generated by your Authenticator App";
10 |
11 |
12 | public _2faCodeDialog() : base(saveAndRestorePosition: false)
13 | {
14 | InitializeComponent();
15 | _2FABox = this.FindControl(nameof(_2FABox));
16 | }
17 |
18 | public _2faCodeDialog(string prompt) : this()
19 | {
20 | Prompt = prompt;
21 | DataContext = this;
22 | Opened += (_, _) => _2FABox.Focus();
23 | }
24 |
25 | protected override Task SaveAndCloseAsync()
26 | {
27 | Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { Code });
28 |
29 | return base.SaveAndCloseAsync();
30 | }
31 |
32 | public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
33 | => await SaveAndCloseAsync();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using LibationAvalonia.ViewModels.Settings;
3 | using LibationFileManager;
4 | using System.Threading.Tasks;
5 |
6 | namespace LibationAvalonia.Dialogs
7 | {
8 | public partial class SettingsDialog : DialogWindow
9 | {
10 | private SettingsVM settingsDisp;
11 |
12 | private readonly Configuration config = Configuration.Instance;
13 | public SettingsDialog()
14 | {
15 | if (Design.IsDesignMode)
16 | _ = Configuration.Instance.LibationFiles;
17 | InitializeComponent();
18 |
19 | DataContext = settingsDisp = new(config);
20 | }
21 |
22 | protected override async Task SaveAndCloseAsync()
23 | {
24 | #region validation
25 |
26 | if (string.IsNullOrWhiteSpace(settingsDisp.ImportantSettings.BooksDirectory))
27 | {
28 | await MessageBox.Show(this.GetParentWindow(), "Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error);
29 | return;
30 | }
31 |
32 | #endregion
33 |
34 | settingsDisp.SaveSettings(config);
35 |
36 | await MessageBox.VerboseLoggingWarning_ShowIfTrue();
37 | await base.SaveAndCloseAsync();
38 | }
39 |
40 | public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
41 | => await SaveAndCloseAsync();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/SetupDialog.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using LibationFileManager;
3 |
4 | namespace LibationAvalonia.Dialogs
5 | {
6 | public partial class SetupDialog : Window
7 | {
8 | public bool IsNewUser { get; private set; }
9 | public bool IsReturningUser { get; private set; }
10 | public ComboBoxItem SelectedTheme { get; set; }
11 | public Configuration Config { get; init; }
12 | public SetupDialog()
13 | {
14 | InitializeComponent();
15 | DataContext = this;
16 | }
17 |
18 | public void NewUser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
19 | {
20 | IsNewUser = true;
21 | Close(DialogResult.OK);
22 | }
23 |
24 | public void ReturningUser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
25 | {
26 | IsReturningUser = true;
27 | Close(DialogResult.OK);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/TagsBatchDialog.axaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
18 |
23 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Dialogs/TagsBatchDialog.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 |
3 | namespace LibationAvalonia.Dialogs
4 | {
5 | public partial class TagsBatchDialog : DialogWindow
6 | {
7 | public string NewTags { get; set; }
8 | public TagsBatchDialog()
9 | {
10 | InitializeComponent();
11 | ControlToFocusOnShow = this.FindControl(nameof(EditTagsTb));
12 |
13 | DataContext = this;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Properties/PublishProfiles/LinuxProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\Linux-chardonnay
10 | FileSystem
11 | net9.0
12 | linux-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Properties/PublishProfiles/MacOSProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\MacOS-chardonnay
10 | FileSystem
11 | net9.0
12 | osx-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Properties/PublishProfiles/WindowsProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\Windows-chardonnay
10 | FileSystem
11 | net9.0-windows
12 | win-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/ViewLocator.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using Avalonia.Controls.Templates;
3 | using LibationAvalonia.ViewModels;
4 | using System;
5 |
6 | namespace LibationAvalonia
7 | {
8 | public class ViewLocator : IDataTemplate
9 | {
10 | public Control Build(object data)
11 | {
12 | var name = data.GetType().FullName!.Replace("ViewModel", "View");
13 | var type = Type.GetType(name);
14 |
15 | if (type != null)
16 | {
17 | return (Control)Activator.CreateInstance(type)!;
18 | }
19 | else
20 | {
21 | return new TextBlock { Text = "Not Found: " + name };
22 | }
23 | }
24 |
25 | public bool Match(object data)
26 | {
27 | return data is ViewModelBase;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/ViewModels/AvaloniaEntryStatus.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Media.Imaging;
2 | using DataLayer;
3 | using LibationUiBase.GridView;
4 | using System;
5 |
6 | #nullable enable
7 | namespace LibationAvalonia.ViewModels
8 | {
9 | public class AvaloniaEntryStatus : EntryStatus, IEntryStatus, IComparable
10 | {
11 | private AvaloniaEntryStatus(LibraryBook libraryBook) : base(libraryBook) { }
12 | public static EntryStatus Create(LibraryBook libraryBook) => new AvaloniaEntryStatus(libraryBook);
13 |
14 | protected override Bitmap LoadImage(byte[] picture)
15 | => AvaloniaUtils.TryLoadImageOrDefault(picture, LibationFileManager.PictureSize._80x80);
16 |
17 | //Button icons are handled by LiberateStatusButton
18 | protected override Bitmap? GetResourceImage(string rescName) => null;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs:
--------------------------------------------------------------------------------
1 | using LibationFileManager;
2 | using LibationUiBase;
3 | using System.IO;
4 |
5 | #nullable enable
6 | namespace LibationAvalonia.ViewModels
7 | {
8 | partial class MainVM
9 | {
10 | private void Configure_NonUI()
11 | {
12 | using var ms1 = new MemoryStream();
13 | App.OpenAsset("img-coverart-prod-unavailable_80x80.jpg").CopyTo(ms1);
14 | PictureStorage.SetDefaultImage(PictureSize._80x80, ms1.ToArray());
15 |
16 | using var ms2 = new MemoryStream();
17 | App.OpenAsset("img-coverart-prod-unavailable_300x300.jpg").CopyTo(ms2);
18 | PictureStorage.SetDefaultImage(PictureSize._300x300, ms2.ToArray());
19 |
20 | using var ms3 = new MemoryStream();
21 | App.OpenAsset("img-coverart-prod-unavailable_500x500.jpg").CopyTo(ms3);
22 | PictureStorage.SetDefaultImage(PictureSize._500x500, ms3.ToArray());
23 | PictureStorage.SetDefaultImage(PictureSize.Native, ms3.ToArray());
24 |
25 | BaseUtil.SetLoadImageDelegate(AvaloniaUtils.TryLoadImageOrDefault);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/ViewModels/RowComparer.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using LibationUiBase.GridView;
3 | using System.ComponentModel;
4 | using System.Reflection;
5 |
6 | #nullable enable
7 | namespace LibationAvalonia.ViewModels
8 | {
9 | internal class RowComparer : RowComparerBase
10 | {
11 | private static readonly PropertyInfo? HeaderCellPi = typeof(DataGridColumn).GetProperty("HeaderCell", BindingFlags.NonPublic | BindingFlags.Instance);
12 | private static readonly PropertyInfo? CurrentSortingStatePi = typeof(DataGridColumnHeader).GetProperty("CurrentSortingState", BindingFlags.NonPublic | BindingFlags.Instance);
13 |
14 | private DataGridColumn? Column { get; }
15 | public override string? PropertyName { get; set; }
16 |
17 | public RowComparer(DataGridColumn? column)
18 | {
19 | Column = column;
20 | PropertyName = Column?.SortMemberPath ?? nameof(IGridEntry.DateAdded);
21 | }
22 |
23 | //Avalonia doesn't expose the column's CurrentSortingState, so we must get it through reflection
24 | protected override ListSortDirection GetSortOrder()
25 | => Column is null ? ListSortDirection.Descending
26 | : CurrentSortingStatePi?.GetValue(HeaderCellPi?.GetValue(Column)) is ListSortDirection lsd ? lsd
27 | : ListSortDirection.Descending;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/ViewModels/Settings/SettingsVM.cs:
--------------------------------------------------------------------------------
1 | using LibationFileManager;
2 |
3 | namespace LibationAvalonia.ViewModels.Settings
4 | {
5 | public class SettingsVM
6 | {
7 | public SettingsVM(Configuration config)
8 | {
9 | LoadSettings(config);
10 | }
11 |
12 | public ImportantSettingsVM ImportantSettings { get; private set; }
13 | public ImportSettingsVM ImportSettings { get; private set; }
14 | public DownloadDecryptSettingsVM DownloadDecryptSettings { get; private set; }
15 | public AudioSettingsVM AudioSettings { get; private set; }
16 |
17 | public void LoadSettings(Configuration config)
18 | {
19 | ImportantSettings = new ImportantSettingsVM(config);
20 | ImportSettings = new ImportSettingsVM(config);
21 | DownloadDecryptSettings = new DownloadDecryptSettingsVM(config);
22 | AudioSettings = new AudioSettingsVM(config);
23 | }
24 |
25 | public void SaveSettings(Configuration config)
26 | {
27 | ImportantSettings.SaveSettings(config);
28 | ImportSettings.SaveSettings(config);
29 | DownloadDecryptSettings.SaveSettings(config);
30 | AudioSettings.SaveSettings(config);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/ViewModels/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace LibationAvalonia.ViewModels
4 | {
5 | public class ViewModelBase : ReactiveObject
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Source/LibationAvalonia/Views/SeriesViewDialog.axaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
14 |
17 |
22 |
25 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Source/LibationCli/Options/ConvertOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using System.Threading.Tasks;
3 |
4 | namespace LibationCli
5 | {
6 | [Verb("convert", HelpText = "Convert mp4 to mp3.")]
7 | public class ConvertOptions : ProcessableOptionsBase
8 | {
9 | protected override Task ProcessAsync() => RunAsync(CreateProcessable());
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/LibationCli/Options/LiberateOptions.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using DataLayer;
3 | using FileLiberator;
4 | using System.Threading.Tasks;
5 |
6 | namespace LibationCli
7 | {
8 | [Verb("liberate", HelpText = "Liberate: book and pdf backups. Default: download and decrypt all un-liberated titles and download pdfs. "
9 | + "Optional: use 'pdf' flag to only download pdfs.")]
10 | public class LiberateOptions : ProcessableOptionsBase
11 | {
12 | [Option(shortName: 'p', longName: "pdf", Required = false, Default = false, HelpText = "Flag to only download pdfs")]
13 | public bool PdfOnly { get; set; }
14 |
15 | protected override Task ProcessAsync()
16 | => PdfOnly
17 | ? RunAsync(CreateProcessable())
18 | : RunAsync(CreateBackupBook());
19 |
20 | private static Processable CreateBackupBook()
21 | {
22 | var downloadPdf = CreateProcessable();
23 |
24 | //Chain pdf download on DownloadDecryptBook.Completed
25 | void onDownloadDecryptBookCompleted(object sender, LibraryBook e)
26 | {
27 | // this is fast anyway. run as sync for easy exception catching
28 | downloadPdf.TryProcessAsync(e).GetAwaiter().GetResult();
29 | }
30 |
31 | var downloadDecryptBook = CreateProcessable(onDownloadDecryptBookCompleted);
32 | return downloadDecryptBook;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/LibationCli/Options/_OptionsBase.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using System;
3 | using System.IO;
4 | using System.Reflection;
5 | using System.Threading.Tasks;
6 |
7 | namespace LibationCli
8 | {
9 | public abstract class OptionsBase
10 | {
11 | public async Task Run()
12 | {
13 | try
14 | {
15 | await ProcessAsync();
16 | }
17 | catch (Exception ex)
18 | {
19 | Environment.ExitCode = (int)ExitCode.RunTimeError;
20 | PrintVerbUsage(new string[]
21 | {
22 | "ERROR",
23 | "=====",
24 | ex.Message,
25 | "",
26 | ex.StackTrace
27 | });
28 | }
29 | }
30 |
31 | protected void PrintVerbUsage(params string[] linesBeforeUsage)
32 | {
33 | var verb = GetType().GetCustomAttribute().Name;
34 | var helpText = new HelpVerb { HelpType = verb }.GetHelpText();
35 | helpText.AddPreOptionsLines(linesBeforeUsage);
36 | helpText.AddPreOptionsLine("");
37 | helpText.AddPreOptionsLine($"{verb} Usage:");
38 | Console.Error.WriteLine(helpText);
39 | }
40 |
41 | protected static void ReplaceConsoleText(TextWriter writer, int previousLength, string newText)
42 | {
43 | writer.Write(new string('\b', previousLength));
44 | writer.Write(newText);
45 | writer.Write(new string(' ', int.Max(0, previousLength - newText.Length)));
46 | }
47 |
48 | protected abstract Task ProcessAsync();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Source/LibationCli/Properties/PublishProfiles/LinuxProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\Linux-chardonnay
10 | FileSystem
11 | net9.0
12 | linux-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LibationCli/Properties/PublishProfiles/MacOSProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\MacOS-chardonnay
10 | FileSystem
11 | net9.0
12 | osx-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LibationCli/Properties/PublishProfiles/WindowsProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\Windows-chardonnay
10 | FileSystem
11 | net9.0
12 | win-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LibationCli/Setup.cs:
--------------------------------------------------------------------------------
1 | using AppScaffolding;
2 | using CommandLine;
3 | using System;
4 | using System.Linq;
5 | using System.Reflection;
6 |
7 | namespace LibationCli
8 | {
9 | public static class Setup
10 | {
11 | public static void Initialize()
12 | {
13 | //***********************************************//
14 | // //
15 | // do not use Configuration before this line //
16 | // //
17 | //***********************************************//
18 | var config = LibationScaffolding.RunPreConfigMigrations();
19 |
20 | LibationScaffolding.RunPostConfigMigrations(config);
21 |
22 | #if classic
23 | LibationScaffolding.RunPostMigrationScaffolding(Variety.Classic, config);
24 | #else
25 | LibationScaffolding.RunPostMigrationScaffolding(Variety.Chardonnay, config);
26 | #endif
27 | }
28 |
29 | public static Type[] LoadVerbs() => Assembly.GetExecutingAssembly()
30 | .GetTypes()
31 | .Where(t => t.GetCustomAttribute() is not null)
32 | .ToArray();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/Configuration.Environment.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | #nullable enable
8 | namespace LibationFileManager
9 | {
10 | [Flags]
11 | public enum OS
12 | {
13 | Unknown,
14 | Windows = 0x100000,
15 | Linux = 0x200000,
16 | MacOS = 0x400000,
17 | }
18 |
19 | public partial class Configuration
20 | {
21 | public static bool IsWindows { get; } = OperatingSystem.IsWindows();
22 | public static bool IsLinux { get; } = OperatingSystem.IsLinux();
23 | public static bool IsMacOs { get; } = OperatingSystem.IsMacOS();
24 | public static Version? LibationVersion { get; private set; }
25 | public static void SetLibationVersion(Version version) => LibationVersion = version;
26 |
27 | public static OS OS { get; }
28 | = IsLinux ? OS.Linux
29 | : IsMacOs ? OS.MacOS
30 | : IsWindows ? OS.Windows
31 | : OS.Unknown;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/Configuration.PropertyChange.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | #nullable enable
4 | namespace LibationFileManager
5 | {
6 | public partial class Configuration
7 | {
8 | /*
9 | * Use this type in the getter for any Dictionary settings,
10 | * and be sure to clone it before returning. This allows Configuration to
11 | * accurately detect if any of the Dictionary's elements have changed.
12 | */
13 | private class EquatableDictionary : Dictionary where TKey : notnull
14 | {
15 | public EquatableDictionary() { }
16 | public EquatableDictionary(IEnumerable> keyValuePairs) : base(keyValuePairs) { }
17 | public EquatableDictionary Clone() => new(this);
18 | public override bool Equals(object? obj)
19 | {
20 | if (obj is Dictionary dic && Count == dic.Count)
21 | {
22 | foreach (var pair in this)
23 | if (!dic.TryGetValue(pair.Key, out var value) || pair.Value?.Equals(value) is not true)
24 | return false;
25 |
26 | return true;
27 | }
28 | return false;
29 | }
30 | public override int GetHashCode() => base.GetHashCode();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/FileTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 |
6 | namespace LibationFileManager
7 | {
8 | public enum FileType { Unknown, Audio, AAXC, PDF, Zip, Cue }
9 |
10 | public static class FileTypes
11 | {
12 | private static Dictionary dic => new()
13 | {
14 | ["aax"] = FileType.AAXC,
15 | ["aaxc"] = FileType.AAXC,
16 | ["cue"] = FileType.Cue,
17 | ["pdf"] = FileType.PDF,
18 | ["zip"] = FileType.Zip,
19 |
20 | ["aac"] = FileType.Audio,
21 | ["flac"] = FileType.Audio,
22 | ["m4a"] = FileType.Audio,
23 | ["m4b"] = FileType.Audio,
24 | ["mp3"] = FileType.Audio,
25 | ["mp4"] = FileType.Audio,
26 | ["ogg"] = FileType.Audio,
27 | };
28 |
29 | public static FileType GetFileTypeFromPath(string path)
30 | => dic.TryGetValue(Path.GetExtension(path).ToLower().Trim('.'), out var fileType)
31 | ? fileType
32 | : FileType.Unknown;
33 |
34 | public static List GetExtensions(FileType fileType)
35 | => dic
36 | .Where(kvp => kvp.Value == fileType)
37 | .Select(kvp => kvp.Key)
38 | .ToList();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/LibationFileManager.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Configuration.cs
21 |
22 |
23 |
24 |
25 | embedded
26 |
27 |
28 |
29 | embedded
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/NullInteropFunctions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | #nullable enable
5 | namespace LibationFileManager
6 | {
7 | public class NullInteropFunctions : IInteropFunctions
8 | {
9 |
10 | public NullInteropFunctions() { }
11 | public NullInteropFunctions(params object[] values) { }
12 |
13 | public IWebViewAdapter? CreateWebViewAdapter() => throw new PlatformNotSupportedException();
14 | public void SetFolderIcon(string image, string directory) => throw new PlatformNotSupportedException();
15 | public void DeleteFolderIcon(string directory) => throw new PlatformNotSupportedException();
16 | public bool CanUpgrade => throw new PlatformNotSupportedException();
17 | public string ReleaseIdString => throw new PlatformNotSupportedException();
18 | public Process RunAsRoot(string exe, string args) => throw new PlatformNotSupportedException();
19 | public void InstallUpgrade(string updateBundle) => throw new PlatformNotSupportedException();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/SqliteStorage.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace LibationFileManager
4 | {
5 | public static class SqliteStorage
6 | {
7 | // not customizable. don't move to config
8 | private static string databasePath => Path.Combine(Configuration.Instance.LibationFiles, "LibationContext.db");
9 | public static string ConnectionString => $"Data Source={databasePath};Foreign Keys=False;Pooling=False;";
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/Templates/SeriesDto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | #nullable enable
4 | namespace LibationFileManager.Templates;
5 |
6 | public record SeriesDto : IFormattable
7 | {
8 | public string Name { get; }
9 |
10 | public float? Number { get; }
11 | public string AudibleSeriesId { get; }
12 | public SeriesDto(string name, float? number, string audibleSeriesId)
13 | {
14 | Name = name;
15 | Number = number;
16 | AudibleSeriesId = audibleSeriesId;
17 | }
18 |
19 | public override string ToString() => Name.Trim();
20 | public string ToString(string? format, IFormatProvider? _)
21 | => string.IsNullOrWhiteSpace(format) ? ToString()
22 | : format
23 | .Replace("{N}", Name)
24 | .Replace("{#}", Number?.ToString())
25 | .Replace("{ID}", AudibleSeriesId)
26 | .Trim();
27 | }
28 |
--------------------------------------------------------------------------------
/Source/LibationFileManager/Templates/SeriesListFormat.cs:
--------------------------------------------------------------------------------
1 | using FileManager.NamingTemplate;
2 | using System.Collections.Generic;
3 | using System.Text.RegularExpressions;
4 |
5 | #nullable enable
6 | namespace LibationFileManager.Templates;
7 |
8 | internal partial class SeriesListFormat : IListFormat
9 | {
10 | public static string Formatter(ITemplateTag _, IEnumerable? series, string formatString)
11 | => series is null ? string.Empty
12 | : IListFormat.Join(formatString, series);
13 |
14 | /// Format must have at least one of the string {N}, {#}, {ID}
15 | [GeneratedRegex(@"[Ff]ormat\((.*?(?:{[N#]}|{ID})+.*?)\)")]
16 | public static partial Regex FormatRegex();
17 | }
18 |
--------------------------------------------------------------------------------
/Source/LibationSearchEngine/IndexRule.cs:
--------------------------------------------------------------------------------
1 | using DataLayer;
2 | using Dinah.Core;
3 | using System;
4 | using System.Collections.ObjectModel;
5 | using System.Linq;
6 |
7 | namespace LibationSearchEngine;
8 |
9 | public enum FieldType
10 | {
11 | Bool,
12 | String,
13 | Number,
14 | ID,
15 | Raw
16 | }
17 |
18 | public class IndexRule
19 | {
20 | public FieldType FieldType { get; }
21 | public Func GetValue { get; }
22 | public ReadOnlyCollection FieldNames { get; }
23 |
24 | public IndexRule(FieldType fieldType, Func valueGetter, params string[] fieldNames)
25 | {
26 | ArgumentValidator.EnsureNotNull(valueGetter, nameof(valueGetter));
27 | ArgumentValidator.EnsureNotNull(fieldNames, nameof(fieldNames));
28 | ArgumentValidator.EnsureGreaterThan(fieldNames.Length, $"{nameof(fieldNames)}.{nameof(fieldNames.Length)}", 0);
29 | var fieldNamesValidated
30 | = fieldNames
31 | .Select((n, i) => ArgumentValidator.EnsureNotNullOrWhiteSpace(n, $"{nameof(fieldNames)}[{i}]")
32 | .Trim());
33 |
34 | GetValue = valueGetter;
35 | FieldType = fieldType;
36 | FieldNames = new ReadOnlyCollection(fieldNamesValidated.ToList());
37 | }
38 | public override string ToString()
39 | => FieldNames.Count == 1
40 | ? $"{FieldNames.First()}"
41 | : $"{FieldNames.First()} ({string.Join(", ", FieldNames.Skip(1))})";
42 | }
43 |
--------------------------------------------------------------------------------
/Source/LibationSearchEngine/LibationSearchEngine.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | embedded
22 |
23 |
24 |
25 | embedded
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Source/LibationSearchEngine/SearchResults.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Lucene.Net.Documents;
5 |
6 | namespace LibationSearchEngine
7 | {
8 | public class SearchResultSet
9 | {
10 | public string SearchString { get; }
11 | public IEnumerable Docs { get; }
12 | public SearchResultSet(string searchString, IEnumerable docs)
13 | {
14 | SearchString = searchString;
15 | Docs = docs;
16 | }
17 | }
18 | public class ScoreDocExplicit
19 | {
20 | public Document Doc { get; }
21 | public string ProductId { get; }
22 | public float Score { get; }
23 |
24 | public ScoreDocExplicit(Document doc, float score)
25 | {
26 | Doc = doc;
27 | ProductId = doc.GetField(SearchEngine._ID_).StringValue;
28 | Score = score;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/LibationSearchEngine/_InternalsVisible.cs:
--------------------------------------------------------------------------------
1 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(nameof(LibationSearchEngine) + ".Tests")]
--------------------------------------------------------------------------------
/Source/LibationSearchEngine/_lucene resources.txt:
--------------------------------------------------------------------------------
1 | series: http://codeclimber.net.nz/archive/2009/08/27/how-to-get-started-with-lucenenet/
2 | case study: http://codeclimber.net.nz/archive/2010/02/26/lucenenet-is-powering-subtext-25-search/
3 | series: http://www.codewrecks.com/blog/index.php/2012/08/25/index-your-blog-using-tags-and-lucene-net/
4 | query syntax: http://www.lucenetutorial.com/lucene-query-syntax.html
5 | bool search: http://lucene.apache.org/core/7_3_1/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Boolean_operators
--------------------------------------------------------------------------------
/Source/LibationUiBase/BaseUtil.cs:
--------------------------------------------------------------------------------
1 | using LibationFileManager;
2 | using System;
3 |
4 | namespace LibationUiBase
5 | {
6 | public static class BaseUtil
7 | {
8 | /// A delegate that loads image bytes into the the UI framework's image format.
9 | public static Func LoadImage { get; private set; }
10 | public static void SetLoadImageDelegate(Func tryLoadImage)
11 | => LoadImage = tryLoadImage;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/EnumDisplay[T].cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core;
2 | using System;
3 |
4 | namespace LibationUiBase
5 | {
6 | public class EnumDisplay where T : Enum
7 | {
8 | public T Value { get; }
9 | public string Description { get; }
10 | public EnumDisplay(T value, string description = null)
11 | {
12 | Value = value;
13 | Description = description ?? value.GetDescription() ?? value.ToString();
14 | }
15 | public override string ToString() => Description;
16 |
17 | public override bool Equals(object obj)
18 | => (obj is EnumDisplay other && other.Value.Equals(Value)) || (obj is T value && value.Equals(Value));
19 | public override int GetHashCode() => Value.GetHashCode();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/GridView/IGridEntry.cs:
--------------------------------------------------------------------------------
1 | using DataLayer;
2 | using Dinah.Core.DataBinding;
3 | using System;
4 | using System.ComponentModel;
5 |
6 | namespace LibationUiBase.GridView
7 | {
8 | public interface IGridEntry : IMemberComparable, INotifyPropertyChanged
9 | {
10 | EntryStatus Liberate { get; }
11 | float SeriesIndex { get; }
12 | string AudibleProductId { get; }
13 | LibraryBook LibraryBook { get; }
14 | Book Book { get; }
15 | DateTime DateAdded { get; }
16 | bool? Remove { get; set; }
17 | string PurchaseDate { get; }
18 | object Cover { get; }
19 | string Length { get; }
20 | LastDownloadStatus LastDownload { get; }
21 | string Series { get; }
22 | SeriesOrder SeriesOrder { get; }
23 | string Title { get; }
24 | string Authors { get; }
25 | string Narrators { get; }
26 | string Category { get; }
27 | string Misc { get; }
28 | string Description { get; }
29 | Rating ProductRating { get; }
30 | Rating MyRating { get; set; }
31 | string BookTags { get; }
32 | void UpdateLibraryBook(LibraryBook libraryBook);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/GridView/ILibraryBookEntry.cs:
--------------------------------------------------------------------------------
1 | namespace LibationUiBase.GridView
2 | {
3 | public interface ILibraryBookEntry : IGridEntry
4 | {
5 | ISeriesEntry Parent { get; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/GridView/ISeriesEntry.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LibationUiBase.GridView
4 | {
5 | public interface ISeriesEntry : IGridEntry
6 | {
7 | List Children { get; }
8 | void ChildRemoveUpdate();
9 | void RemoveChild(ILibraryBookEntry libraryBookEntry);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/GridView/ObjectComparer[T].cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 |
4 | namespace LibationUiBase.GridView
5 | {
6 | public class ObjectComparer : IComparer where T : IComparable
7 | {
8 | public int Compare(object x, object y) => ((T)x).CompareTo(y);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/GridView/SeriesOrder.cs:
--------------------------------------------------------------------------------
1 | using DataLayer;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace LibationUiBase.GridView
7 | {
8 | public class SeriesOrder : IComparable
9 | {
10 | private float[] Orders { get; }
11 | public string OrderString { get; }
12 |
13 | public SeriesOrder(IEnumerable seriesBooks)
14 | {
15 | var orderstrings = seriesBooks
16 | .Where(s => s.Index > 0)
17 | .Select(s => s.Order == "-1" ? "-" : $"#{s.Order}")
18 | .ToList();
19 | OrderString = string.Join(", ", orderstrings);
20 |
21 | Orders = seriesBooks.Where(s => s.Index > 0).Select(s => s.Index).ToArray();
22 | }
23 | public override string ToString() => OrderString;
24 |
25 | public int CompareTo(object obj)
26 | {
27 | if (obj is not SeriesOrder other) return 1;
28 |
29 | int count = int.Min(Orders.Length, other.Orders.Length);
30 | for (int i = 0; i < count; i++)
31 | {
32 | var compare = Orders[i].CompareTo(other.Orders[i]);
33 | if (compare != 0) return compare;
34 | }
35 |
36 | if (Orders.Length < other.Orders.Length) return 1;
37 | if (Orders.Length > other.Orders.Length) return -1;
38 | return 0;
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/Source/LibationUiBase/ILogForm.cs:
--------------------------------------------------------------------------------
1 | namespace LibationUiBase
2 | {
3 | public interface ILogForm
4 | {
5 | void WriteLine(string text);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/LibationUiBase.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | true
6 | true
7 | false
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | embedded
23 |
24 |
25 | embedded
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Source/LibationUiBase/SeriesView/SeriesOrder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LibationUiBase.SeriesView
4 | {
5 | public class SeriesOrder : IComparable
6 | {
7 | public float Order { get; }
8 | public string OrderString { get; }
9 |
10 | public SeriesOrder(string orderString)
11 | {
12 | OrderString = orderString;
13 | Order = float.TryParse(orderString, out var o) ? o : -1f;
14 | }
15 | public override string ToString() => OrderString;
16 |
17 | public int CompareTo(object obj)
18 | {
19 | if (obj is not SeriesOrder other) return 1;
20 | return Order.CompareTo(other.Order);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/ClearableTextBox.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Windows.Forms;
4 |
5 | namespace LibationWinForms
6 | {
7 | public partial class ClearableTextBox : UserControl
8 | {
9 | public event EventHandler TextCleared;
10 | public override string Text { get => textBox1.Text; set => textBox1.Text = value; }
11 | public override Font Font
12 | {
13 | get => textBox1.Font;
14 | set
15 | {
16 | base.Font = textBox1.Font = button1.Font = value;
17 | OnSizeChanged(EventArgs.Empty);
18 | }
19 | }
20 |
21 | public ClearableTextBox()
22 | {
23 | InitializeComponent();
24 | textBox1.KeyDown += (_, e) => OnKeyDown(e);
25 | textBox1.KeyUp += (_, e) => OnKeyUp(e);
26 | textBox1.KeyPress += (_, e) => OnKeyPress(e);
27 | textBox1.TextChanged += (_, e) => OnTextChanged(e);
28 | }
29 |
30 | protected override void OnSizeChanged(EventArgs e)
31 | {
32 | base.OnSizeChanged(e);
33 | Height = button1.Width = button1.Height = textBox1.Height;
34 | textBox1.Width = Width - button1.Width;
35 | button1.Location = new Point(textBox1.Width, 0);
36 | }
37 |
38 | private void button1_Click(object sender, System.EventArgs e)
39 | {
40 | textBox1.Clear();
41 | TextCleared?.Invoke(this, EventArgs.Empty);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/LiberatedStatusBatchAutoDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace LibationWinForms.Dialogs
5 | {
6 | public partial class LiberatedStatusBatchAutoDialog : Form
7 | {
8 | public bool SetDownloaded { get; private set; }
9 | public bool SetNotDownloaded { get; private set; }
10 |
11 | public LiberatedStatusBatchAutoDialog()
12 | {
13 | InitializeComponent();
14 | this.SetLibationIcon();
15 | }
16 |
17 | private void okBtn_Click(object sender, EventArgs e)
18 | {
19 | SetDownloaded = this.setDownloadedCb.Checked;
20 | SetNotDownloaded = this.setNotDownloadedCb.Checked;
21 |
22 | this.DialogResult = DialogResult.OK;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/Login/ApprovalNeededDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Windows.Forms;
4 |
5 | namespace LibationWinForms.Dialogs.Login
6 | {
7 | public partial class ApprovalNeededDialog : Form
8 | {
9 | public ApprovalNeededDialog()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | private void approvedBtn_Click(object sender, EventArgs e)
15 | {
16 | Serilog.Log.Logger.Information("Submit button clicked");
17 |
18 | DialogResult = DialogResult.OK;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/Login/LoginCallbackDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using AudibleUtilities;
4 | using Dinah.Core;
5 |
6 | namespace LibationWinForms.Dialogs.Login
7 | {
8 | public partial class LoginCallbackDialog : Form
9 | {
10 | private string accountId { get; }
11 |
12 | public string Email { get; private set; }
13 | public string Password { get; private set; }
14 |
15 | public LoginCallbackDialog(Account account)
16 | {
17 | InitializeComponent();
18 |
19 | accountId = account.AccountId;
20 |
21 | // do not allow user to change login id here. if they do then jsonpath will fail
22 | this.localeLbl.Text = string.Format(this.localeLbl.Text, account.Locale.Name);
23 | this.usernameLbl.Text = string.Format(this.usernameLbl.Text, accountId);
24 | }
25 |
26 | private void submitBtn_Click(object sender, EventArgs e)
27 | {
28 | Email = accountId;
29 | Password = this.passwordTb.Text;
30 |
31 | Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { email = Email?.ToMask(), passwordLength = Password.Length });
32 |
33 | DialogResult = DialogResult.OK;
34 | // Close() not needed for AcceptButton
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/Login/WinformLoginBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace LibationWinForms.Dialogs.Login
5 | {
6 | public abstract class WinformLoginBase
7 | {
8 | private readonly IWin32Window _owner;
9 | protected WinformLoginBase(IWin32Window owner)
10 | {
11 | _owner = owner;
12 | }
13 |
14 | /// True if ShowDialog's DialogResult == OK
15 | protected bool ShowDialog(Form dialog)
16 | {
17 | var result = dialog.ShowDialog(_owner);
18 | Serilog.Log.Logger.Debug("{@DebugInfo}", new { DialogResult = result });
19 | return result == DialogResult.OK;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/Login/_2faCodeDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Windows.Forms;
4 |
5 | namespace LibationWinForms.Dialogs.Login
6 | {
7 | public partial class _2faCodeDialog : Form
8 | {
9 | public string Code { get; private set; }
10 |
11 | public _2faCodeDialog() => InitializeComponent();
12 | public _2faCodeDialog(string prompt) : this()
13 | {
14 | promptLbl.Text = prompt;
15 | }
16 |
17 | private void submitBtn_Click(object sender, EventArgs e)
18 | {
19 | Code = this.codeTb.Text.Trim();
20 |
21 | Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { Code });
22 |
23 | DialogResult = DialogResult.OK;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/SearchSyntaxDialog.cs:
--------------------------------------------------------------------------------
1 | using LibationSearchEngine;
2 | using System;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 |
6 | namespace LibationWinForms.Dialogs
7 | {
8 | public partial class SearchSyntaxDialog : Form
9 | {
10 | public SearchSyntaxDialog()
11 | {
12 | InitializeComponent();
13 |
14 | label2.Text += "\r\n\r\n" + string.Join("\r\n", SearchEngine.FieldIndexRules.StringFieldNames);
15 | label3.Text += "\r\n\r\n" + string.Join("\r\n", SearchEngine.FieldIndexRules.NumberFieldNames);
16 | label4.Text += "\r\n\r\n" + string.Join("\r\n", SearchEngine.FieldIndexRules.BoolFieldNames);
17 | label5.Text += "\r\n\r\n" + string.Join("\r\n", SearchEngine.FieldIndexRules.IdFieldNames);
18 | this.SetLibationIcon();
19 | }
20 |
21 | private void CloseBtn_Click(object sender, EventArgs e) => this.Close();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/SettingsDialog.ImportLibrary.cs:
--------------------------------------------------------------------------------
1 | using LibationFileManager;
2 | using LibationUiBase;
3 | using System;
4 | using System.Linq;
5 |
6 | namespace LibationWinForms.Dialogs
7 | {
8 | public partial class SettingsDialog
9 | {
10 | private void Load_ImportLibrary(Configuration config)
11 | {
12 | this.autoScanCb.Text = desc(nameof(config.AutoScan));
13 | this.showImportedStatsCb.Text = desc(nameof(config.ShowImportedStats));
14 | this.importEpisodesCb.Text = desc(nameof(config.ImportEpisodes));
15 | this.downloadEpisodesCb.Text = desc(nameof(config.DownloadEpisodes));
16 | this.autoDownloadEpisodesCb.Text = desc(nameof(config.AutoDownloadEpisodes));
17 |
18 | autoScanCb.Checked = config.AutoScan;
19 | showImportedStatsCb.Checked = config.ShowImportedStats;
20 | importEpisodesCb.Checked = config.ImportEpisodes;
21 | downloadEpisodesCb.Checked = config.DownloadEpisodes;
22 | autoDownloadEpisodesCb.Checked = config.AutoDownloadEpisodes;
23 | }
24 | private void Save_ImportLibrary(Configuration config)
25 | {
26 | config.AutoScan = autoScanCb.Checked;
27 | config.ShowImportedStats = showImportedStatsCb.Checked;
28 | config.ImportEpisodes = importEpisodesCb.Checked;
29 | config.DownloadEpisodes = downloadEpisodesCb.Checked;
30 | config.AutoDownloadEpisodes = autoDownloadEpisodesCb.Checked;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/SetupDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Windows.Forms;
4 |
5 | namespace LibationWinForms.Dialogs
6 | {
7 | public partial class SetupDialog : Form
8 | {
9 | public bool IsNewUser { get; private set; }
10 | public bool IsReturningUser { get; private set; }
11 |
12 | public SetupDialog() => InitializeComponent();
13 |
14 | private void newUserBtn_Click(object sender, EventArgs e)
15 | {
16 | IsNewUser = true;
17 |
18 | this.DialogResult = DialogResult.OK;
19 | Close();
20 | }
21 |
22 | private void returningUserBtn_Click(object sender, EventArgs e)
23 | {
24 | IsReturningUser = true;
25 |
26 | this.DialogResult = DialogResult.OK;
27 | Close();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Dialogs/TagsBatchDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | namespace LibationWinForms.Dialogs
12 | {
13 | public partial class TagsBatchDialog : Form
14 | {
15 | public string NewTags { get; private set; }
16 |
17 | public TagsBatchDialog()
18 | {
19 | InitializeComponent();
20 | this.SetLibationIcon();
21 | }
22 |
23 | private void saveBtn_Click(object sender, EventArgs e)
24 | {
25 | NewTags = this.newTagsTb.Text;
26 | this.DialogResult = DialogResult.OK;
27 | }
28 |
29 | private void cancelBtn_Click(object sender, EventArgs e)
30 | {
31 | this.DialogResult = DialogResult.Cancel;
32 | this.Close();
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Form1.Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using LibationWinForms.Dialogs;
4 |
5 | namespace LibationWinForms
6 | {
7 | public partial class Form1
8 | {
9 | private void Configure_Settings() { }
10 |
11 | private void accountsToolStripMenuItem_Click(object sender, EventArgs e) => new AccountsDialog().ShowDialog();
12 |
13 | private void basicSettingsToolStripMenuItem_Click(object sender, EventArgs e) => new SettingsDialog().ShowDialog();
14 |
15 | private void aboutToolStripMenuItem_Click(object sender, EventArgs e) => new AboutDialog().ShowDialog(this);
16 | private async void tourToolStripMenuItem_Click(object sender, EventArgs e)
17 | => await new Walkthrough(this).RunAsync();
18 |
19 | private void launchHangoverToolStripMenuItem_Click(object sender, EventArgs e)
20 | {
21 | try
22 | {
23 | System.Diagnostics.Process.Start("Hangover.exe");
24 | }
25 | catch (Exception ex)
26 | {
27 | Serilog.Log.Logger.Error(ex, "Failed to launch Hangover");
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Form1.Upgrade.cs:
--------------------------------------------------------------------------------
1 | using LibationUiBase;
2 | using LibationWinForms.Dialogs;
3 | using System.Threading.Tasks;
4 |
5 | namespace LibationWinForms
6 | {
7 | public partial class Form1
8 | {
9 | private void Configure_Upgrade()
10 | {
11 | setProgressVisible(false);
12 | #if !DEBUG
13 | Task upgradeAvailable(UpgradeEventArgs e)
14 | {
15 | var notificationResult = new UpgradeNotificationDialog(e.UpgradeProperties).ShowDialog(this);
16 |
17 | e.Ignore = notificationResult == System.Windows.Forms.DialogResult.Ignore;
18 | e.InstallUpgrade = notificationResult == System.Windows.Forms.DialogResult.Yes;
19 |
20 | return Task.CompletedTask;
21 | }
22 |
23 | var upgrader = new Upgrader();
24 | upgrader.DownloadProgress += (_, e) => Invoke(() => upgradePb.Value = int.Max(0, int.Min(100, (int)(e.ProgressPercentage ?? 0))));
25 | upgrader.DownloadBegin += (_, _) => Invoke(() => setProgressVisible(true));
26 | upgrader.DownloadCompleted += (_, _) => Invoke(() => setProgressVisible(false));
27 |
28 | Shown += async (_, _) => await upgrader.CheckForUpgradeAsync(upgradeAvailable);
29 | #endif
30 | }
31 |
32 | private void setProgressVisible(bool visible) => upgradeLbl.Visible = upgradePb.Visible = visible;
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/FormattableLabel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Windows.Forms;
4 |
5 | namespace LibationWinForms
6 | {
7 | public class FormattableLabel : Label
8 | {
9 | public string FormatText { get; set; }
10 |
11 | /// Text set: first non-null, non-whitespace set is also saved as
12 | public override string Text
13 | {
14 | get => base.Text;
15 | set
16 | {
17 | if (string.IsNullOrWhiteSpace(FormatText))
18 | FormatText = value;
19 |
20 | base.Text = value;
21 | }
22 | }
23 |
24 | #region ctor.s
25 | public FormattableLabel() : base() { }
26 | #endregion
27 |
28 | /// Replaces the format item in a specified string with the string representation of a corresponding object in a specified array. Returns for convenience.
29 | /// An object array that contains zero or more objects to format.
30 | public string Format(params object[] args) => Text = string.Format(FormatText, args);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/GridView/AsyncNotifyPropertyChanged.cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core.Threading;
2 | using System.ComponentModel;
3 | using System.Runtime.CompilerServices;
4 |
5 | namespace LibationWinForms.GridView
6 | {
7 | public abstract class AsyncNotifyPropertyChanged : SynchronizeInvoker, INotifyPropertyChanged
8 | {
9 | // see also notes in Libation/Source/_ARCHITECTURE NOTES.txt :: MVVM
10 | public event PropertyChangedEventHandler PropertyChanged;
11 |
12 | // per standard INotifyPropertyChanged pattern:
13 | // https://docs.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-implement-property-change-notification
14 | public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
15 | => this.UIThreadSync(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/GridView/DataGridViewImageButtonCell.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 |
3 | namespace LibationWinForms.GridView
4 | {
5 | public class DataGridViewImageButtonCell : AccessibleDataGridViewButtonCell
6 | {
7 | public DataGridViewImageButtonCell(string accessibilityName) : base(accessibilityName) { }
8 |
9 | protected void DrawButtonImage(Graphics graphics, Image image, Rectangle cellBounds)
10 | {
11 | var scaleFactor = OwningColumn is IDataGridScaleColumn scCol ? scCol.ScaleFactor : 1f;
12 |
13 | var w = (int)float.Round(graphics.ScaleX(image.Width) * scaleFactor);
14 | var h = (int)float.Round(graphics.ScaleY(image.Height) * scaleFactor);
15 | var x = cellBounds.Left + (cellBounds.Width - w) / 2;
16 | var y = cellBounds.Top + (cellBounds.Height - h) / 2;
17 |
18 | graphics.DrawImage(image, new Rectangle(x, y, w, h));
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/GridView/RowComparer.cs:
--------------------------------------------------------------------------------
1 | using LibationUiBase.GridView;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 |
6 | namespace LibationWinForms.GridView
7 | {
8 | internal class RowComparer : RowComparerBase
9 | {
10 | public ListSortDirection SortOrder { get; set; } = ListSortDirection.Descending;
11 | public override string PropertyName { get; set; } = nameof(IGridEntry.DateAdded);
12 | protected override ListSortDirection GetSortOrder() => SortOrder;
13 |
14 | ///
15 | /// Helper method for ordering grid entries
16 | ///
17 | public IOrderedEnumerable OrderEntries(IEnumerable entries)
18 | => SortOrder is ListSortDirection.Descending ? entries.OrderDescending(this) : entries.Order(this);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/GridView/SyncBindingSource.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Threading;
3 | using System.Windows.Forms;
4 |
5 | // https://stackoverflow.com/a/32886415
6 | namespace LibationWinForms.GridView
7 | {
8 | public class SyncBindingSource : BindingSource
9 | {
10 | private SynchronizationContext syncContext { get; }
11 |
12 | public SyncBindingSource() : base()
13 | => syncContext = SynchronizationContext.Current;
14 | public SyncBindingSource(IContainer container) : base(container)
15 | => syncContext = SynchronizationContext.Current;
16 | public SyncBindingSource(object dataSource, string dataMember) : base(dataSource, dataMember)
17 | => syncContext = SynchronizationContext.Current;
18 |
19 | public override bool SupportsFiltering => true;
20 |
21 | protected override void OnListChanged(ListChangedEventArgs e)
22 | {
23 | if (syncContext is not null)
24 | syncContext.Send(_ => base.OnListChanged(e), null);
25 | else
26 | base.OnListChanged(e);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/GridView/WinFormsEntryStatus.cs:
--------------------------------------------------------------------------------
1 | using DataLayer;
2 | using LibationUiBase.GridView;
3 | using System.Drawing;
4 |
5 | namespace LibationWinForms.GridView
6 | {
7 | public class WinFormsEntryStatus : EntryStatus, IEntryStatus
8 | {
9 | private static readonly Color SERIES_BG_COLOR = Color.FromArgb(230, 255, 230);
10 | public Color BackgroundBrush => IsEpisode ? SERIES_BG_COLOR : SystemColors.ControlLightLight;
11 |
12 | private WinFormsEntryStatus(LibraryBook libraryBook) : base(libraryBook) { }
13 | public static EntryStatus Create(LibraryBook libraryBook) => new WinFormsEntryStatus(libraryBook);
14 |
15 | protected override Image LoadImage(byte[] picture)
16 | => WinFormsUtil.TryLoadImageOrDefault(picture, LibationFileManager.PictureSize._80x80);
17 |
18 | protected override Image GetResourceImage(string rescName)
19 | {
20 | var image = Properties.Resources.ResourceManager.GetObject(rescName);
21 | return image as Bitmap;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/ProcessQueue/ProcessBookForm.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace LibationWinForms.ProcessQueue
4 | {
5 | public partial class ProcessBookForm : Form
6 | {
7 | private Control _dockControl;
8 | public int WidthChange { get; set; }
9 | public ProcessBookForm()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | public void PassControl(Control dockControl)
15 | {
16 | _dockControl = dockControl;
17 | Controls.Add(_dockControl);
18 | }
19 |
20 | public Control RegainControl()
21 | {
22 | Controls.Remove(_dockControl);
23 | return _dockControl;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Properties/DataSources/LibationWinForm.GridEntry.datasource:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | LibationWinForms.GridView.GridEntry, LibationWinForms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
10 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Properties/PublishProfiles/WindowsProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\bin\Publish\classic
10 | FileSystem
11 | net9.0-windows
12 | win-x64
13 | true
14 | false
15 |
16 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow-PSD.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow-PSD.zip
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow.ico
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_1024.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_128.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_16.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_20.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_24.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_256.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_32.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_40.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_48.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_512.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_512x512.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_512x512.pdn
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_64.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/Libation icon/glass-with-glow_96.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/_icon how to.txt:
--------------------------------------------------------------------------------
1 | how to create the app's icon
2 | ============================
3 | orig from (no longer available) https://www.flaticon.com/free-icon/glass-with-wine_33529
4 | get this image, color=black, at each of these sizes: 16,32,64,128,256
5 |
6 | from this answer: https://stackoverflow.com/a/16922387
7 | install image magick. in the install process, check legacy support (necessary for "convert")
8 | go to the install dir: C:\Program Files\ImageMagick...
9 | run the convert method from the stackoverflow example
10 | run cmd
11 | drag/drop convert.exe into cmd
12 | ...
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/cheers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/cheers.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/download-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/download-arrow.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/edit-tags-25x25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/edit-tags-25x25.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/edit-tags-50x50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/edit-tags-50x50.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/edit_25x25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/edit_25x25.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/edit_64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/edit_64x64.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/error.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/img-coverart-prod-unavailable_300x300.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/img-coverart-prod-unavailable_300x300.jpg
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/img-coverart-prod-unavailable_500x500.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/img-coverart-prod-unavailable_500x500.jpg
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/img-coverart-prod-unavailable_80x80.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/img-coverart-prod-unavailable_80x80.jpg
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/import_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/import_16x16.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/libation.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/libation.ico
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate and pdf icons.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate and pdf icons.pdn
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate icons.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate icons.pdn
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_green.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_green_pdf_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_green_pdf_no.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_green_pdf_yes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_green_pdf_yes.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_red.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_red_pdf_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_red_pdf_no.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_red_pdf_yes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_red_pdf_yes.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_yellow.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_yellow_pdf_no.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_yellow_pdf_no.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/liberate_yellow_pdf_yes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/liberate_yellow_pdf_yes.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/minus.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/pdf source.url:
--------------------------------------------------------------------------------
1 | [InternetShortcut]
2 | URL=https://www.flaticon.com/free-icon/pdf-file-format-symbol_29099
3 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/Resources/plus.png
--------------------------------------------------------------------------------
/Source/LibationWinForms/Resources/stoplight source.url:
--------------------------------------------------------------------------------
1 | [InternetShortcut]
2 | URL=https://www.flaticon.com/free-icon/semaphore_55291
3 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/WinFormsUtil.cs:
--------------------------------------------------------------------------------
1 | using Dinah.Core.WindowsDesktop.Drawing;
2 | using LibationFileManager;
3 | using System.Drawing;
4 | using System.Windows.Forms;
5 |
6 | namespace LibationWinForms
7 | {
8 | internal static class WinFormsUtil
9 | {
10 | private const float BaseDpi = 96;
11 |
12 | private static Bitmap defaultImage;
13 | public static Image TryLoadImageOrDefault(byte[] picture, PictureSize defaultSize = PictureSize.Native)
14 | {
15 | try
16 | {
17 | return ImageReader.ToImage(picture);
18 | }
19 | catch
20 | {
21 | using var ms = new System.IO.MemoryStream(PictureStorage.GetDefaultImage(defaultSize));
22 | return defaultImage ??= new Bitmap(ms);
23 | }
24 | }
25 |
26 | public static int DpiScale(this Control control, int value, float additionalScaleFactor = 1)
27 | => (int)float.Round(control.DeviceDpi / BaseDpi * value * additionalScaleFactor);
28 |
29 | public static int DpiUnscale(this Control control, int value)
30 | => (int)float.Round(BaseDpi / control.DeviceDpi * value);
31 |
32 | public static int ScaleX(this Graphics control, int value)
33 | => (int)float.Round(control.DpiX / BaseDpi * value);
34 | public static int ScaleY(this Graphics control, int value)
35 | => (int)float.Round(control.DpiY / BaseDpi * value);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Source/LibationWinForms/libation.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LibationWinForms/libation.ico
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/Libation.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Libation
3 | Exec=/usr/bin/libation
4 | Icon=libation
5 | Comment=Liberate your Audiobooks
6 | Terminal=false
7 | Type=Application
8 | MimeType=x-content/unix-software;
9 | Categories=Audio;AudioVideo;
10 | Keywords=audible;audio;audiobook;book;
11 |
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/Program.cs:
--------------------------------------------------------------------------------
1 | namespace LinuxConfigApp
2 | {
3 | class Program
4 | {
5 | static void Main() { }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/Properties/PublishProfiles/LinuxProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\..\bin\Publish\Linux-chardonnay
10 | FileSystem
11 | net9.0
12 | linux-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/screenshots/accounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/LinuxConfigApp/screenshots/accounts.png
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/screenshots/audio-file-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/LinuxConfigApp/screenshots/audio-file-settings.png
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/screenshots/download-decrypt-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/LinuxConfigApp/screenshots/download-decrypt-settings.png
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/screenshots/filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/LinuxConfigApp/screenshots/filter.png
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/screenshots/important-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/LinuxConfigApp/screenshots/important-settings.png
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/screenshots/main-page-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/LinuxConfigApp/screenshots/main-page-dark.png
--------------------------------------------------------------------------------
/Source/LoadByOS/LinuxConfigApp/screenshots/main-page-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/LinuxConfigApp/screenshots/main-page-light.png
--------------------------------------------------------------------------------
/Source/LoadByOS/MacOSConfigApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CFBundleExecutable
7 | Libation
8 | CFBundleName
9 | Libation
10 | LSArchitecturePriority
11 | ARCHITECTURE_STRING
12 | LSMinimumSystemVersion
13 | 10.15.0
14 | CFBundleIdentifier
15 | org.libation.macos
16 | NSHighResolutionCapable
17 |
18 | CFBundleIconFile
19 | libation.icns
20 | CFBundleVersion
21 | VERSION_STRING
22 | com.apple.security.app-sandbox
23 |
24 | com.apple.security.cs.allow-jit
25 |
26 | com.apple.security.cs.allow-unsigned-executable-memory
27 |
28 | com.apple.security.cs.disable-library-validation
29 |
30 | com.apple.security.cs.disable-executable-page-protection
31 |
32 | com.apple.security.automation.apple-events
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Source/LoadByOS/MacOSConfigApp/Program.cs:
--------------------------------------------------------------------------------
1 | namespace MacOSConfigApp
2 | {
3 | class Program
4 | {
5 | static void Main() { }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Source/LoadByOS/MacOSConfigApp/Properties/PublishProfiles/MacOSProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\..\bin\Publish\MacOS-chardonnay
10 | FileSystem
11 | net9.0
12 | osx-x64
13 | true
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/Source/LoadByOS/MacOSConfigApp/libation.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/MacOSConfigApp/libation.icns
--------------------------------------------------------------------------------
/Source/LoadByOS/README.txt:
--------------------------------------------------------------------------------
1 | Streamlined example is in \Source\_Demos\LoadByOS
2 |
3 | MUST follow naming conventions in InteropFactory
4 |
5 | Windows : Path.GetFileName(a).StartsWithInsensitive("win")
6 | Linux : Path.GetFileName(a).StartsWithInsensitive("linux")
7 | MacOs : Path.GetFileName(a).StartsWithInsensitive("mac") || a.StartsWithInsensitive("osx")
--------------------------------------------------------------------------------
/Source/LoadByOS/WindowsConfigApp/Program.cs:
--------------------------------------------------------------------------------
1 | namespace WindowsConfigApp
2 | {
3 | class Program
4 | {
5 | static void Main() { }
6 | }
7 | }
--------------------------------------------------------------------------------
/Source/LoadByOS/WindowsConfigApp/Properties/PublishProfiles/WindowsProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | ..\..\bin\Publish\classic
10 | FileSystem
11 | net9.0-windows
12 | win-x64
13 | true
14 | false
15 |
16 |
--------------------------------------------------------------------------------
/Source/LoadByOS/WindowsConfigApp/ZipExtractor.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmcrackan/Libation/248fdfd2bc013ba10f8ee9b980109a27ee1825f6/Source/LoadByOS/WindowsConfigApp/ZipExtractor.exe
--------------------------------------------------------------------------------
/Source/Upgrading dotnet version.txt:
--------------------------------------------------------------------------------
1 | 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:
2 |
3 | * https://github.com/rmcrackan/Dinah.Core
4 | * https://github.com/rmcrackan/AudibleApi
5 | * https://github.com/rmcrackan/LuceneNet303r2
6 |
7 | * https://github.com/Mbucari/AAXClean.Codecs
8 | * https://github.com/Mbucari/AAXClean
9 | * https://github.com/Mbucari/serilog-sinks-zipfile
10 |
--------------------------------------------------------------------------------
/Source/_ARCHITECTURE NOTES.txt:
--------------------------------------------------------------------------------
1 | MVVM
2 | ====
3 | Libation is not strictly MVVM. It's not strictly anything. There are however efforts at moving some major components toward this pattern.
4 |
5 | Primary View: ProductsGrid
6 | Primary View Model: GridEntry
7 |
8 | see also: https://docs.microsoft.com/en-us/dotnet/desktop/winforms/controls/raise-change-notifications--bindingsource
9 | BindingSource + INotifyPropertyChanged + DataGridView is the backbone of our implementation. The SortableBindingList (BindingList/BindingSource) automatically subscribes to each entry's NotifyPropertyChanged -- which is why our AsyncNotifyPropertyChanged.NotifyPropertyChanged is needed even though none of our code calls it.
10 |
11 | - Adding or removing an entry to/from this BindingSource automatically updates the UI. No additional code needed.
12 | - Calling NotifyPropertyChanged with the name of a field updates that field in the UI
13 | - Calling NotifyPropertyChanged without a field name (or with an invalid field name) updates the whole entry in the UI
14 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/CrossPlatformClientExe/CrossPlatformClientExe.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | false
8 | false
9 |
10 |
11 |
12 | ..\bin\Debug
13 | embedded
14 |
15 |
16 |
17 | ..\bin\Release
18 | embedded
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/CrossPlatformClientExe/IInteropFunctions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CrossPlatformClientExe
4 | {
5 | public interface IInteropFunctions
6 | {
7 | public string TransformInit1();
8 | public int TransformInit2();
9 | public void CopyTextToClipboard(string text);
10 | public void ShowForm();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/CrossPlatformClientExe/NullInteropFunctions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CrossPlatformClientExe
4 | {
5 | internal class NullInteropFunctions : IInteropFunctions
6 | {
7 | public NullInteropFunctions(params object[] values) { }
8 |
9 | public string TransformInit1() => throw new PlatformNotSupportedException();
10 | public int TransformInit2() => throw new PlatformNotSupportedException();
11 | public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
12 | public void ShowForm() => throw new PlatformNotSupportedException();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/CrossPlatformClientExe/OSConfigBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CrossPlatformClientExe
4 | {
5 | public abstract class OSConfigBase
6 | {
7 | public abstract Type InteropFunctionsType { get; }
8 | public virtual Type[] ReferencedTypes { get; } = new Type[0];
9 |
10 | public void Run()
11 | {
12 | //Each of these types belongs to a different windows-only assembly that's needed by
13 | //the WinInterop methods. By referencing these types in main we force the runtime to
14 | //load their assemblies before execution reaches inside main. This allows the calling
15 | //process to find these assemblies in its module list.
16 | _ = ReferencedTypes;
17 | _ = InteropFunctionsType;
18 |
19 | //Wait for the calling process to be ready to read the WriteLine()
20 | Console.ReadLine();
21 |
22 | // Signal the calling process that execution has reached inside main, and that all referenced assemblies have been loaded.
23 | Console.WriteLine();
24 |
25 | // Wait for the calling process to finish reading the process module list, then exit.
26 | Console.ReadLine();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/CrossPlatformClientExe/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CrossPlatformClientExe
4 | {
5 | class Program
6 | {
7 | [STAThread]
8 | public static void Main()
9 | {
10 | var interopInstance = new OSInteropProxy("this IS SOME text", 42);
11 |
12 | Console.WriteLine("X-Formed Value 1: {0}", interopInstance.TransformInit1());
13 | Console.WriteLine("X-Formed Value 2: {0}", interopInstance.TransformInit2());
14 |
15 | interopInstance.ShowForm();
16 | interopInstance.CopyTextToClipboard("This is copied text!");
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/LinuxConfigApp/LinuxConfigApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | false
8 | false
9 |
10 |
11 |
12 | ..\bin\Debug
13 | embedded
14 |
15 |
16 |
17 | ..\bin\Release
18 | embedded
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/LinuxConfigApp/LinuxInterop.cs:
--------------------------------------------------------------------------------
1 | using CrossPlatformClientExe;
2 |
3 | namespace LinuxConfigApp
4 | {
5 | internal class LinuxInterop : IInteropFunctions
6 | {
7 | private string InitValue1 { get; }
8 | private int InitValue2 { get; }
9 |
10 | public LinuxInterop(string initValue1, int initValue2)
11 | {
12 | InitValue1 = initValue1;
13 | InitValue2 = initValue2;
14 | }
15 |
16 | public string TransformInit1() => InitValue1.ToLower();
17 |
18 | public int TransformInit2() => InitValue2 + InitValue2;
19 |
20 | public void CopyTextToClipboard(string text) => throw new PlatformNotSupportedException();
21 | public void ShowForm() => throw new PlatformNotSupportedException();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/LinuxConfigApp/Program.cs:
--------------------------------------------------------------------------------
1 | using CrossPlatformClientExe;
2 |
3 | namespace LinuxConfigApp
4 | {
5 | class Program : OSConfigBase
6 | {
7 | public override Type InteropFunctionsType => typeof(LinuxInterop);
8 |
9 | static void Main() => new Program().Run();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/WindowsConfigApp/Form1.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace WindowsConfigApp
2 | {
3 | partial class Form1
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
33 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
34 | this.ClientSize = new System.Drawing.Size(800, 450);
35 | this.Text = "Form1";
36 | }
37 |
38 | #endregion
39 | }
40 | }
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/WindowsConfigApp/Form1.cs:
--------------------------------------------------------------------------------
1 | namespace WindowsConfigApp
2 | {
3 | public partial class Form1 : Form
4 | {
5 | public Form1()
6 | {
7 | InitializeComponent();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/WindowsConfigApp/Program.cs:
--------------------------------------------------------------------------------
1 | using CrossPlatformClientExe;
2 |
3 | namespace WindowsConfigApp
4 | {
5 | class Program : OSConfigBase
6 | {
7 | public override Type InteropFunctionsType => typeof(WinInterop);
8 | public override Type[] ReferencedTypes => new Type[]
9 | {
10 | typeof(Form1),
11 | typeof(Bitmap),
12 | typeof(Accessibility.IAccIdentity),
13 | typeof(Microsoft.Win32.SystemEvents)
14 | };
15 |
16 | static void Main() => new Program().Run();
17 | }
18 | }
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/WindowsConfigApp/WinInterop.cs:
--------------------------------------------------------------------------------
1 | using CrossPlatformClientExe;
2 |
3 | namespace WindowsConfigApp
4 | {
5 | internal class WinInterop : IInteropFunctions
6 | {
7 | private string InitValue1 { get; }
8 | private int InitValue2 { get; }
9 |
10 | public WinInterop() { }
11 | public WinInterop(string initValue1, int initValue2)
12 | {
13 | InitValue1 = initValue1;
14 | InitValue2 = initValue2;
15 | }
16 |
17 | public void CopyTextToClipboard(string text) => Clipboard.SetDataObject(text, true, 5, 150);
18 |
19 | public void ShowForm()
20 | {
21 | ApplicationConfiguration.Initialize();
22 | Application.Run(new Form1());
23 | }
24 |
25 | public string TransformInit1() => InitValue1.ToUpper();
26 |
27 | public int TransformInit2() => InitValue2 * InitValue2;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Source/_Demos/LoadByOS/WindowsConfigApp/WindowsConfigApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net9.0-windows7.0
6 | true
7 | true
8 | enable
9 | false
10 | false
11 |
12 |
13 |
14 | ..\bin\Debug
15 | embedded
16 |
17 |
18 |
19 | ..\bin\Release
20 | embedded
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Source/_Tests/AudibleUtilities.Tests/AudibleUtilities.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Source/_Tests/FileLiberator.Tests/FileLiberator.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Source/_Tests/FileManager.Tests/FileManager.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Source/_Tests/LibationFileManager.Tests/LibationFileManager.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Source/_Tests/LibationSearchEngine.Tests/LibationSearchEngine.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | all
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------