├── .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 | 2 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Images/libation_hangover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Images/libation_slosh.svg: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 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 |