diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a461263 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +/.vs +/packages +/LibgenDesktop +/LibgenDesktop.Setup/obj/Debug x64 diff --git a/LibgenDesktop/LibgenDesktop.csproj b/LibgenDesktop/LibgenDesktop.csproj index 27ee3ac..f55c2c5 100644 --- a/LibgenDesktop/LibgenDesktop.csproj +++ b/LibgenDesktop/LibgenDesktop.csproj @@ -98,14 +98,9 @@ - - ..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll - - - 4.0 @@ -252,6 +247,7 @@ + diff --git a/LibgenDesktop/Models/Localization/Localizators/LibraryTabLocalizator.cs b/LibgenDesktop/Models/Localization/Localizators/LibraryTabLocalizator.cs index 8305b1b..d17f5e8 100644 --- a/LibgenDesktop/Models/Localization/Localizators/LibraryTabLocalizator.cs +++ b/LibgenDesktop/Models/Localization/Localizators/LibraryTabLocalizator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using LibgenDesktop.Models.ProgressArgs; namespace LibgenDesktop.Models.Localization.Localizators { @@ -22,6 +23,8 @@ public LibraryTabLocalizator(List prioritizedTranslationList, Langu ColumnsFile = Format(translation => translation?.File); ColumnsAuthors = Format(translation => translation?.Authors); ColumnsTitle = Format(translation => translation?.Title); + ColumnsErrorType = Format(translation => translation?.ErrorType); + ColumnsErrorDescription = Format(translation => translation?.ErrorDescription); } public string TabTitle { get; } @@ -35,6 +38,8 @@ public LibraryTabLocalizator(List prioritizedTranslationList, Langu public string Added { get; } public string ScanLog { get; } public string Error { get; } + public string ColumnsErrorType { get; } + public string ColumnsErrorDescription { get; } public string ColumnsFile { get; } public string ColumnsAuthors { get; } public string ColumnsTitle { get; } @@ -42,6 +47,37 @@ public LibraryTabLocalizator(List prioritizedTranslationList, Langu public string GetScanStartedString(string directory) => Format(translation => translation?.ScanStarted, new { directory }); public string GetFoundString(int count) => Format(translation => translation?.Found, new { count }); public string GetNotFoundString(int count) => Format(translation => translation?.NotFound, new { count }); + public string GetErrorsString(int count) => Format(translation => translation?.Errors, new { count }); + public string GetErrorDescription(ErrorTypes errorType) + { + switch (errorType) + { + case ErrorTypes.ERROR_NONE: + return ""; + case ErrorTypes.ERROR_DIRECTORY_ACCESS: + return Format(translation => translation?.Library.ErrorDescriptions.DirectoryAccess); + case ErrorTypes.ERROR_DIRECTORY_NOT_FOUND: + return Format(translation => translation?.Library.ErrorDescriptions.DirectoryNotFound); + case ErrorTypes.ERROR_FILE_SIZE_ZERO: + return Format(translation => translation?.Library.ErrorDescriptions.FileSize); + case ErrorTypes.ERROR_FILE_NOT_FOUND: + return Format(translation => translation?.Library.ErrorDescriptions.FileNotFound); + case ErrorTypes.ERROR_FILE_PATH_TOO_LONG: + return Format(translation => translation?.Library.ErrorDescriptions.FilePathTooLong); + case ErrorTypes.ERROR_FILE_ACCESS: + return Format(translation => translation?.Library.ErrorDescriptions.FileAccess); + case ErrorTypes.ERROR_FILE_IN_USE: + return Format(translation => translation?.Library.ErrorDescriptions.FileInUse); + case ErrorTypes.ERROR_IO_EXCEPTION: + return Format(translation => translation?.Library.ErrorDescriptions.IoException); + case ErrorTypes.ERROR_MD5_HASH_NOT_IN_DB: + return Format(translation => translation?.Library.ErrorDescriptions.MD5HashError); + case ErrorTypes.ERROR_OTHER: + return Format(translation => translation?.Library.ErrorDescriptions.OtherException); + default: throw new ArgumentOutOfRangeException(); + } + } + public string GetScanCompleteString(int found, int notFound, int errors) => Format(translation => translation?.ScanComplete, new { found = Formatter.ToFormattedString(found), notFound = Formatter.ToFormattedString(notFound), errors = Formatter.ToFormattedString(errors) }); diff --git a/LibgenDesktop/Models/Localization/Translation.cs b/LibgenDesktop/Models/Localization/Translation.cs index 15e9bf3..420323e 100644 --- a/LibgenDesktop/Models/Localization/Translation.cs +++ b/LibgenDesktop/Models/Localization/Translation.cs @@ -570,6 +570,22 @@ internal class LibraryColumnsTranslation public string File { get; set; } public string Authors { get; set; } public string Title { get; set; } + public string ErrorType { get; set; } + public string ErrorDescription { get; set; } + } + + internal class LibraryErrorDescriptionsTranslation + { + public string DirectoryAccess { get; set; } + public string DirectoryNotFound { get; set; } + public string FileSize { get; set; } + public string FileNotFound { get; set; } + public string FilePathTooLong { get; set; } + public string FileAccess { get; set; } + public string FileInUse { get; set; } + public string IoException { get; set; } + public string MD5HashError { get; set; } + public string OtherException { get; set; } } internal class LibraryTranslation @@ -588,8 +604,10 @@ internal class LibraryTranslation public string NotFound { get; set; } public string ScanLog { get; set; } public string Error { get; set; } + public string Errors { get; set; } public string ScanComplete { get; set; } public LibraryColumnsTranslation Columns { get; set; } + public LibraryErrorDescriptionsTranslation ErrorDescriptions { get; set; } } internal class DatabaseTranslation diff --git a/LibgenDesktop/Models/MainModel.cs b/LibgenDesktop/Models/MainModel.cs index d3ade15..4de5e8a 100644 --- a/LibgenDesktop/Models/MainModel.cs +++ b/LibgenDesktop/Models/MainModel.cs @@ -648,7 +648,7 @@ public Task GetDatabaseStatsAsync() public async Task CheckForApplicationUpdateAsync() { - Updater.UpdateCheckResult result = await updater.CheckForUpdateAsync(ignoreSpecifiedRelease: false); + Updater.UpdateCheckResult result = await updater.CheckForUpdateAsync(false); if (result != null && result.NewReleaseName != AppSettings.LastUpdate.IgnoreReleaseName) { LastApplicationUpdateCheckResult = result; @@ -1083,27 +1083,83 @@ private void ScanDirectory(string rootScanDirectory, string scanDirectory, IP { relativeFilePath = relativeFilePath.Substring(rootScanDirectory.Length + 1); } + + if (!StringExtensions.HasEbookExtension(relativeFilePath)) + { + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, false, ErrorTypes.ERROR_NONE)); + notFound++; + continue; + } + string md5Hash; try { - using (MD5 md5 = MD5.Create()) - using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var md5 = MD5.Create()) + using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 8192, true)) + { + if (fileStream.Length <= 0) + { + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_FILE_SIZE_ZERO)); + errors++; + continue; + } + var md5HashArray = md5.ComputeHash(fileStream); + md5Hash = BitConverter.ToString(md5HashArray).Replace("-", "").ToLowerInvariant(); + } + } + catch (IOException ex) + { + int hresult = ExceptionUtils.GetHRForException(ex); + const int E_PATHTOOLONG = unchecked((int) 0x800700CE); + const int E_FILENOTFOUND = unchecked((int) 0x80070002); + const int E_ACCESSDENIED = unchecked((int) 0x80070005); + const int E_SHARING_VIOLATION = unchecked((int) 0x80070020); + + switch (hresult) { - byte[] md5HashArray = md5.ComputeHash(fileStream); - md5Hash = BitConverter.ToString(md5HashArray).Replace("-", String.Empty).ToLowerInvariant(); + case E_PATHTOOLONG: + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_FILE_PATH_TOO_LONG).Replace("_", " ")}, length: {filePath.Length}"); + Logger.Exception(ex); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_FILE_PATH_TOO_LONG)); + errors++; + continue; + case E_FILENOTFOUND: + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_FILE_NOT_FOUND).Replace("_", " ")}, filePath: {filePath}"); + Logger.Exception(ex); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_FILE_NOT_FOUND)); + errors++; + continue; + case E_ACCESSDENIED: + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_FILE_ACCESS).Replace("_", " ")}, filePath: {filePath}"); + Logger.Exception(ex); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_FILE_ACCESS)); + errors++; + continue; + case E_SHARING_VIOLATION: + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_FILE_IN_USE).Replace("_", " ")}, filePath: {filePath}"); + Logger.Exception(ex); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_FILE_IN_USE)); + errors++; + continue; } + + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_IO_EXCEPTION).Replace("_", " ")}, IOException: {ex}"); + Logger.Exception(ex); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_IO_EXCEPTION)); + errors++; + continue; } catch (Exception exception) { Logger.Debug($"Couldn't calculate MD5 hash for the file: {filePath}"); Logger.Exception(exception); - progressHandler.Report(new ScanUnknownProgress(relativeFilePath, error: true)); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_OTHER)); errors++; continue; } try { - T libgenObject = getObjectByMd5HashFunction(md5Hash); + var libgenObject = getObjectByMd5HashFunction(md5Hash); if (libgenObject != null) { progressHandler.Report(new ScanProgress(relativeFilePath, libgenObject)); @@ -1111,17 +1167,16 @@ private void ScanDirectory(string rootScanDirectory, string scanDirectory, IP } else { - progressHandler.Report(new ScanUnknownProgress(relativeFilePath, error: false)); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, false, ErrorTypes.ERROR_NONE)); notFound++; } } catch (Exception exception) { - Logger.Debug($"Couldn't lookup the MD5 hash: {md5Hash} in the database for the file: {filePath}"); + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_MD5_HASH_NOT_IN_DB).Replace("_", " ")}, MD5 hash: {md5Hash} in the database for the file: {filePath}"); Logger.Exception(exception); - progressHandler.Report(new ScanUnknownProgress(relativeFilePath, error: true)); + progressHandler.Report(new ScanUnknownProgress(relativeFilePath, true, ErrorTypes.ERROR_MD5_HASH_NOT_IN_DB)); errors++; - continue; } } foreach (string directoryPath in Directory.EnumerateDirectories(scanDirectory)) @@ -1129,10 +1184,30 @@ private void ScanDirectory(string rootScanDirectory, string scanDirectory, IP ScanDirectory(rootScanDirectory, directoryPath, progressHandler, getObjectByMd5HashFunction, ref found, ref notFound, ref errors); } } + catch (UnauthorizedAccessException ex) + { + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_DIRECTORY_ACCESS).Replace("_", " ")}, filePath: {scanDirectory}"); + Logger.Exception(ex); + progressHandler.Report(new ScanUnknownProgress(scanDirectory, true, ErrorTypes.ERROR_DIRECTORY_ACCESS)); + errors++; + } + catch (IOException ex) + { + int hresult = ExceptionUtils.GetHRForException(ex); + const int E_DIRECTORYNOTFOUND = unchecked((int)0x80070003); + + if (hresult == E_DIRECTORYNOTFOUND) + { + Logger.Debug($"Error: {nameof(ErrorTypes.ERROR_DIRECTORY_NOT_FOUND).Replace("_", " ")}, filePath: {scanDirectory}"); + Logger.Exception(ex); + progressHandler.Report(new ScanUnknownProgress(scanDirectory, true, ErrorTypes.ERROR_DIRECTORY_NOT_FOUND)); + errors++; + } + } catch (Exception exception) { Logger.Exception(exception); - progressHandler.Report(new ScanUnknownProgress(scanDirectory, error: true)); + progressHandler.Report(new ScanUnknownProgress(scanDirectory, true, ErrorTypes.ERROR_OTHER)); errors++; } } diff --git a/LibgenDesktop/Models/ProgressArgs/ScanUnknownProgress.cs b/LibgenDesktop/Models/ProgressArgs/ScanUnknownProgress.cs index 73e6935..37a9e38 100644 --- a/LibgenDesktop/Models/ProgressArgs/ScanUnknownProgress.cs +++ b/LibgenDesktop/Models/ProgressArgs/ScanUnknownProgress.cs @@ -1,14 +1,31 @@ namespace LibgenDesktop.Models.ProgressArgs { + internal enum ErrorTypes + { + ERROR_NONE, + ERROR_DIRECTORY_ACCESS, + ERROR_DIRECTORY_NOT_FOUND, + ERROR_FILE_NOT_FOUND, + ERROR_FILE_PATH_TOO_LONG, + ERROR_FILE_ACCESS, + ERROR_FILE_IN_USE, + ERROR_FILE_SIZE_ZERO, + ERROR_IO_EXCEPTION, + ERROR_MD5_HASH_NOT_IN_DB, + ERROR_OTHER + } + internal class ScanUnknownProgress { - public ScanUnknownProgress(string relativeFilePath, bool error) + public ScanUnknownProgress(string relativeFilePath, bool error, ErrorTypes errorType) { RelativeFilePath = relativeFilePath; Error = error; + ErrorType = errorType; } public string RelativeFilePath { get; } public bool Error { get; } + public ErrorTypes ErrorType {get;} } -} +} \ No newline at end of file diff --git a/LibgenDesktop/Models/Utils/ExceptionUtils.cs b/LibgenDesktop/Models/Utils/ExceptionUtils.cs index 767e309..2f76b82 100644 --- a/LibgenDesktop/Models/Utils/ExceptionUtils.cs +++ b/LibgenDesktop/Models/Utils/ExceptionUtils.cs @@ -1,9 +1,21 @@ using System; +using System.Runtime.InteropServices; namespace LibgenDesktop.Models.Utils { internal static class ExceptionUtils { + public static int GetHRForException(Exception exception) + { + if (exception == null) throw new ArgumentNullException(); + + //on first call there is possible pollution of thread IErrorInfo with sensitive data + int hr = Marshal.GetHRForException(exception); + //therefore call with empty ex. obj. to cleanup IErrorInfo + Marshal.GetHRForException(new Exception()); + return hr; + } + public static Exception GetInnermostException(this Exception exception) { while (exception.InnerException != null) diff --git a/LibgenDesktop/Models/Utils/StringExtensions.cs b/LibgenDesktop/Models/Utils/StringExtensions.cs index ac2ebdb..717e254 100644 --- a/LibgenDesktop/Models/Utils/StringExtensions.cs +++ b/LibgenDesktop/Models/Utils/StringExtensions.cs @@ -6,6 +6,11 @@ namespace LibgenDesktop.Models.Utils { internal static class StringExtensions { + public static bool HasEbookExtension(string source) + { + return (source.EndsWith(".djvu") || source.EndsWith(".mobi") || source.EndsWith(".pdf") || source.EndsWith(".epub")|| source.EndsWith(".doc") || source.EndsWith(".docx") ); + } + public static bool CompareOrdinalIgnoreCase(this string currentString, string otherString) { return String.Compare(currentString, otherString, StringComparison.OrdinalIgnoreCase) == 0; diff --git a/LibgenDesktop/Resources/Languages/English.lng b/LibgenDesktop/Resources/Languages/English.lng index 0487d07..2049b24 100644 --- a/LibgenDesktop/Resources/Languages/English.lng +++ b/LibgenDesktop/Resources/Languages/English.lng @@ -1,768 +1,735 @@ { - "General": - { - "Name": "English", - "LocalizedName": "US English", - "CultureCode": "en-US", - "TranslatorName": "libgenapps" - }, - "Formatting": - { - "DecimalSeparator": ".", - "ThousandsSeparator": ",", - "DateFormat": "MM/dd/yyyy", - "TimeFormat": "hh:mm:ss tt", - "FileSizePostfixes": - { - "Byte": "bytes", - "Kilobyte": "KB", - "Megabyte": "MB", - "Gigabyte": "GB", - "Terabyte": "TB" - } - }, - "MainWindow": - { - "WindowTitle": "Libgen Desktop", - "MainMenu": - { - "DownloadManagerTooltip": "Download Manager", - "BookmarksTooltip": "Bookmarks", - "NoBookmarks": "No bookmarks", - "Update": "Update...", - "Import": "Import...", - "Synchronize": "Synchronization...", - "Library": "Library", - "Database": "Database...", - "SqlDebugger": "SQL debugger", - "Settings": "Settings", - "About": "About" - } - }, - "CreateDatabaseWindow": - { - "WindowTitle": "Libgen Desktop", - "FirstRunMessage": "Welcome to Libgen Desktop.", - "DatabaseNotFound": "Database {database} not found.", - "DatabaseCorrupted": "Database {database} is corrupted.", - "LibgenServerDatabase": "Database {database} is a Libgen Server database which is not compatible with the Libgen Desktop database structure.", - "ChooseOption": "Choose an action", - "CreateNewDatabase": "Create a new database", - "OpenExistingDatabase": "Open an existing database", - "BrowseNewDatabaseDialogTitle": "New database location", - "BrowseExistingDatabaseDialogTitle": "Existing database location", - "Databases": "Databases", - "AllFiles": "All files", - "Error": "Error", - "CannotCreateDatabase": "Couldn't create database.", - "Ok": "OK", - "Cancel": "CANCEL" - }, - "SearchTab": - { - "TabTitle": "Search", - "SearchPlaceHolder": "Search", - "NonFictionSelector": "Non-fiction books", - "FictionSelector": "Fiction books", - "SciMagSelector": "Scientific articles", - "NonFictionSearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN without dashes", - "FictionSearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN with dashes", - "SciMagSearchBoxTooltip": "Search by title, authors, magazine name, DOI, Pubmed ID, and ISSN (p/e)", - "SearchInProgress": "Search in progress...", - "NonFictionSearchProgress": "Books found: {count}", - "FictionSearchProgress": "Books found: {count}", - "SciMagSearchProgress": "Articles found: {count}", - "Interrupt": "INTERRUPT", - "Interrupting": "INTERRUPTING...", - "DatabaseIsEmpty": "The database is empty. Download a database dump from the Library Genesis web site and import it here.", - "ImportButton": "Import" - }, - "SearchResultsTabs": - { - "SearchPlaceHolder": "Search", - "SearchInProgress": "Search in progress...", - "Interrupt": "INTERRUPT", - "Interrupting": "INTERRUPTING...", - "AddToBookmarksTooltip": "Add to bookmarks", - "RemoveFromBookmarksTooltip": "Remove from bookmarks", - "ExportButtonTooltip": "Export search results to a file", - "Details": "Details", - "Open": "Open", - "Download": "Download", - "ErrorMessageTitle": "Error", - "FileNotFoundError": "File {file} not found.", - "OfflineModeIsOnMessageTitle": "Offline mode is on", - "OfflineModeIsOnMessageText": "Downloading is disabled while the offline mode is on. You can turn it off in the settings window.", - "NoDownloadMirrorError": "No download mirror has been selected. Please choose one of the mirrors in the settings window.", - "LargeNumberOfItemsToDownloadPromptTitle": "Large number of items to download", - "LargeNumberOfItemsToDownloadPromptText": "You are about to download {number} items. Are you sure?" - }, - "NonFictionSearchResultsTab": - { - "SearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN without dashes", - "SearchProgress": "Books found: {count}", - "StatusBar": "Books found: {count}", - "Columns": - { - "Title": "Title", - "Authors": "Authors", - "Series": "Series", - "Year": "Year", - "Publisher": "Publisher", - "Format": "Format", - "FileSize": "File Size", - "Ocr": "OCR", - "InLocalLibrary": "In local library" - } - }, - "FictionSearchResultsTab": - { - "SearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN with dashes", - "SearchProgress": "Books found: {count}", - "StatusBar": "Books found: {count}", - "Columns": - { - "Title": "Title", - "Authors": "Authors", - "Series": "Series", - "Year": "Year", - "Publisher": "Publisher", - "Format": "Format", - "FileSize": "File Size", - "InLocalLibrary": "In local library" - } - }, - "SciMagSearchResultsTab": - { - "SearchBoxTooltip": "Search by title, authors, magazine name, DOI, Pubmed ID, and ISSN (p/e)", - "SearchProgress": "Articles found: {count}", - "StatusBar": "Articles found: {count}", - "Columns": - { - "Title": "Title", - "Authors": "Authors", - "Magazine": "Magazine", - "Year": "Year", - "FileSize": "File Size", - "Doi": "DOI", - "InLocalLibrary": "In local library" - } - }, - "DetailsTabs": - { - "CoverIsLoading": "Loading cover...", - "NoCover": "No cover available", - "NoCoverMirror": "No mirror has been selected{new-line}to load this cover", - "NoCoverDueToOfflineMode": "Offline mode is on.{new-line}Cover not loaded.", - "CoverLoadingError": "Couldn't load the cover", - "Yes": "yes", - "No": "no", - "Unknown": "unknown", - "Portrait": "portrait", - "Landscape": "landscape", - "CopyContextMenu": "Copy \"{text}\"", - "Download": "DOWNLOAD", - "DownloadFromMirror": "DOWNLOAD FROM {mirror}", - "Queued": "QUEUED", - "Downloading": "DOWNLOADING", - "Stopped": "STOPPED", - "Error": "DOWNLOAD ERROR", - "Open": "OPEN FILE", - "ErrorMessageTitle": "Error", - "FileNotFoundError": "File {file} not found.", - "NoDownloadMirrorTooltip": "No mirror has been selected", - "OfflineModeIsOnTooltip": "Offline mode is on", - "Close": "CLOSE" - }, - "NonFictionDetailsTab": - { - "Title": "Title", - "Authors": "Authors", - "Series": "Series", - "Publisher": "Publisher", - "Year": "Year", - "Language": "Language", - "Format": "Format", - "Isbn": "ISBN", - "Added": "Added", - "LastModified": "Last Modified", - "Library": "Library", - "FileSize": "File Size", - "Topics": "Topics", - "Volume": "Volume", - "Magazine": "Magazine", - "City": "City", - "Edition": "Edition", - "Pages": "Pages", - "BodyMatterPages": "body matter", - "TotalPages": "total", - "Tags": "Tags", - "Md5Hash": "MD5 hash", - "Comments": "Comments", - "Identifiers": "Identifiers", - "LibgenId": "Libgen ID", - "Issn": "ISSN", - "Udc": "UDC", - "Lbc": "LBC", - "Lcc": "LCC", - "Ddc": "DDC", - "Doi": "DOI", - "OpenLibraryId": "Open Library ID", - "GoogleBookId": "Google Books ID", - "Asin": "ASIN", - "AdditionalAttributes": "Additional Attributes", - "Dpi": "DPI", - "Ocr": "OCR", - "TableOfContents": "Table Of Contents", - "Scanned": "Scanned", - "Orientation": "Orientation", - "Paginated": "Paginated", - "Colored": "Colored", - "Cleaned": "Cleaned" - }, - "FictionDetailsTab": - { - "Title": "Title", - "Authors": "Authors", - "RussianAuthor": "Author in Russian", - "Series": "Series", - "Publisher": "Publisher", - "Edition": "Edition", - "Year": "Year", - "Language": "Language", - "Format": "Format", - "Pages": "Pages", - "Version": "Version", - "FileSize": "File Size", - "Added": "Added", - "LastModified": "Last Modified", - "Md5Hash": "MD5 hash", - "Comments": "Comments", - "Identifiers": "Identifiers", - "LibgenId": "Libgen ID", - "Isbn": "ISBN", - "GoogleBookId": "Google Books ID", - "Asin": "ASIN" - }, - "SciMagDetailsTab": - { - "Title": "Title", - "Authors": "Authors", - "Magazine": "Magazine", - "Year": "Year", - "Month": "Month", - "Day": "Day", - "Volume": "Volume", - "Issue": "Issue", - "Pages": "Pages", - "FileSize": "File Size", - "AddedDateTime": "Added", - "Md5Hash": "MD5 hash", - "AbstractUrl": "Abstract URL", - "Identifiers": "Identifiers", - "LibgenId": "Libgen ID", - "Doi": "DOI", - "Isbn": "ISBN", - "MagazineId": "Magazine ID", - "Issnp": "ISSN (p)", - "Issne": "ISSN (e)", - "PubmedId": "Pubmed ID", - "Pmc": "PMC", - "Pii": "PII", - "AdditionalAttributes": "Additional Attributes", - "Attribute1": "Attribute 1", - "Attribute2": "Attribute 2", - "Attribute3": "Attribute 3", - "Attribute4": "Attribute 4", - "Attribute5": "Attribute 5", - "Attribute6": "Attribute 6" - }, - "Import": - { - "WindowTitle": "Import from SQL dump", - "BrowseImportFileDialogTitle": "SQL dump file location", - "AllSupportedFiles": "All supported files", - "SqlDumps": "SQL dumps", - "Archives": "Archives", - "AllFiles": "All files", - "Elapsed": "Time elapsed: {elapsed}", - "FreeSpace": "Free space left on device: {freeSpace}", - "Unknown": "unknown", - "Interrupt": "INTERRUPT", - "Interrupting": "INTERRUPTING...", - "Close": "CLOSE", - "StatusMessages": - { - "Step": "Step {current} of {total}.", - "DataLookup": "Table definition lookup", - "CreatingIndexes": "Creating indexes", - "LoadingIds": "Loading identifiers", - "ImportingData": "Importing data", - "ImportComplete": "Import complete", - "ImportCancelled": "Import has been cancelled", - "DataNotFound": "Data not found", - "ImportError": "Import failed" - }, - "LogMessages": - { - "Step": "Step {step}", - "DataLookup": "Searching for the table definitions", - "Scanning": "Scanning...", - "ScannedProgress": "{percent}% of the file have been scanned...", - "NonFictionTableFound": "Non-fiction book table found.", - "FictionTableFound": "Fiction book table found.", - "SciMagTableFound": "Scientific article table found.", - "CreatingIndexes": "Creating missing indexes", - "CreatingIndexForColumn": "Creating index for {column} column...", - "LoadingIds": "Loading existing data identifiers", - "LoadingColumnValues": "Loading data for {column} column...", - "ImportingData": "Importing data", - "ImportBooksProgressNoUpdate": "Books added: {added}.", - "ImportBooksProgressWithUpdate": "Books added: {added}, updated: {updated}.", - "ImportArticlesProgressNoUpdate": "Articles added: {added}.", - "ImportArticlesProgressWithUpdate": "Articles added: {added}, updated: {updated}.", - "ImportSuccessful": "Import completed successfully.", - "ImportCancelled": "Import has been cancelled by the user.", - "DataNotFound": "Couldn't find the data to import.", - "InsufficientDiskSpace": "Insufficient disk space.", - "ImportError": "An error occurred during the import." - } + "General": { + "Name": "English", + "LocalizedName": "US English", + "CultureCode": "en-US", + "TranslatorName": "libgenapps" + }, + "Formatting": { + "DecimalSeparator": ".", + "ThousandsSeparator": ",", + "DateFormat": "MM/dd/yyyy", + "TimeFormat": "hh:mm:ss tt", + "FileSizePostfixes": { + "Byte": "bytes", + "Kilobyte": "KB", + "Megabyte": "MB", + "Gigabyte": "GB", + "Terabyte": "TB" + } + }, + "MainWindow": { + "WindowTitle": "Libgen Desktop", + "MainMenu": { + "DownloadManagerTooltip": "Download Manager", + "BookmarksTooltip": "Bookmarks", + "NoBookmarks": "No bookmarks", + "Update": "Update...", + "Import": "Import...", + "Synchronize": "Synchronization...", + "Library": "Library", + "Database": "Database...", + "SqlDebugger": "SQL debugger", + "Settings": "Settings", + "About": "About" + } + }, + "CreateDatabaseWindow": { + "WindowTitle": "Libgen Desktop", + "FirstRunMessage": "Welcome to Libgen Desktop.", + "DatabaseNotFound": "Database {database} not found.", + "DatabaseCorrupted": "Database {database} is corrupted.", + "LibgenServerDatabase": "Database {database} is a Libgen Server database which is not compatible with the Libgen Desktop database structure.", + "ChooseOption": "Choose an action", + "CreateNewDatabase": "Create a new database", + "OpenExistingDatabase": "Open an existing database", + "BrowseNewDatabaseDialogTitle": "New database location", + "BrowseExistingDatabaseDialogTitle": "Existing database location", + "Databases": "Databases", + "AllFiles": "All files", + "Error": "Error", + "CannotCreateDatabase": "Couldn't create database.", + "Ok": "OK", + "Cancel": "CANCEL" + }, + "SearchTab": { + "TabTitle": "Search", + "SearchPlaceHolder": "Search", + "NonFictionSelector": "Non-fiction books", + "FictionSelector": "Fiction books", + "SciMagSelector": "Scientific articles", + "NonFictionSearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN without dashes", + "FictionSearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN with dashes", + "SciMagSearchBoxTooltip": "Search by title, authors, magazine name, DOI, Pubmed ID, and ISSN (p/e)", + "SearchInProgress": "Search in progress...", + "NonFictionSearchProgress": "Books found: {count}", + "FictionSearchProgress": "Books found: {count}", + "SciMagSearchProgress": "Articles found: {count}", + "Interrupt": "INTERRUPT", + "Interrupting": "INTERRUPTING...", + "DatabaseIsEmpty": "The database is empty. Download a database dump from the Library Genesis web site and import it here.", + "ImportButton": "Import" + }, + "SearchResultsTabs": { + "SearchPlaceHolder": "Search", + "SearchInProgress": "Search in progress...", + "Interrupt": "INTERRUPT", + "Interrupting": "INTERRUPTING...", + "AddToBookmarksTooltip": "Add to bookmarks", + "RemoveFromBookmarksTooltip": "Remove from bookmarks", + "ExportButtonTooltip": "Export search results to a file", + "Details": "Details", + "Open": "Open", + "Download": "Download", + "ErrorMessageTitle": "Error", + "FileNotFoundError": "File {file} not found.", + "OfflineModeIsOnMessageTitle": "Offline mode is on", + "OfflineModeIsOnMessageText": "Downloading is disabled while the offline mode is on. You can turn it off in the settings window.", + "NoDownloadMirrorError": "No download mirror has been selected. Please choose one of the mirrors in the settings window.", + "LargeNumberOfItemsToDownloadPromptTitle": "Large number of items to download", + "LargeNumberOfItemsToDownloadPromptText": "You are about to download {number} items. Are you sure?" + }, + "NonFictionSearchResultsTab": { + "SearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN without dashes", + "SearchProgress": "Books found: {count}", + "StatusBar": "Books found: {count}", + "Columns": { + "Title": "Title", + "Authors": "Authors", + "Series": "Series", + "Year": "Year", + "Publisher": "Publisher", + "Format": "Format", + "FileSize": "File Size", + "Ocr": "OCR", + "InLocalLibrary": "In local library" + } + }, + "FictionSearchResultsTab": { + "SearchBoxTooltip": "Search by title, authors, series, publisher, and ISBN with dashes", + "SearchProgress": "Books found: {count}", + "StatusBar": "Books found: {count}", + "Columns": { + "Title": "Title", + "Authors": "Authors", + "Series": "Series", + "Year": "Year", + "Publisher": "Publisher", + "Format": "Format", + "FileSize": "File Size", + "InLocalLibrary": "In local library" + } + }, + "SciMagSearchResultsTab": { + "SearchBoxTooltip": "Search by title, authors, magazine name, DOI, Pubmed ID, and ISSN (p/e)", + "SearchProgress": "Articles found: {count}", + "StatusBar": "Articles found: {count}", + "Columns": { + "Title": "Title", + "Authors": "Authors", + "Magazine": "Magazine", + "Year": "Year", + "FileSize": "File Size", + "Doi": "DOI", + "InLocalLibrary": "In local library" + } + }, + "DetailsTabs": { + "CoverIsLoading": "Loading cover...", + "NoCover": "No cover available", + "NoCoverMirror": "No mirror has been selected{new-line}to load this cover", + "NoCoverDueToOfflineMode": "Offline mode is on.{new-line}Cover not loaded.", + "CoverLoadingError": "Couldn't load the cover", + "Yes": "yes", + "No": "no", + "Unknown": "unknown", + "Portrait": "portrait", + "Landscape": "landscape", + "CopyContextMenu": "Copy \"{text}\"", + "Download": "DOWNLOAD", + "DownloadFromMirror": "DOWNLOAD FROM {mirror}", + "Queued": "QUEUED", + "Downloading": "DOWNLOADING", + "Stopped": "STOPPED", + "Error": "DOWNLOAD ERROR", + "Open": "OPEN FILE", + "ErrorMessageTitle": "Error", + "FileNotFoundError": "File {file} not found.", + "NoDownloadMirrorTooltip": "No mirror has been selected", + "OfflineModeIsOnTooltip": "Offline mode is on", + "Close": "CLOSE" + }, + "NonFictionDetailsTab": { + "Title": "Title", + "Authors": "Authors", + "Series": "Series", + "Publisher": "Publisher", + "Year": "Year", + "Language": "Language", + "Format": "Format", + "Isbn": "ISBN", + "Added": "Added", + "LastModified": "Last Modified", + "Library": "Library", + "FileSize": "File Size", + "Topics": "Topics", + "Volume": "Volume", + "Magazine": "Magazine", + "City": "City", + "Edition": "Edition", + "Pages": "Pages", + "BodyMatterPages": "body matter", + "TotalPages": "total", + "Tags": "Tags", + "Md5Hash": "MD5 hash", + "Comments": "Comments", + "Identifiers": "Identifiers", + "LibgenId": "Libgen ID", + "Issn": "ISSN", + "Udc": "UDC", + "Lbc": "LBC", + "Lcc": "LCC", + "Ddc": "DDC", + "Doi": "DOI", + "OpenLibraryId": "Open Library ID", + "GoogleBookId": "Google Books ID", + "Asin": "ASIN", + "AdditionalAttributes": "Additional Attributes", + "Dpi": "DPI", + "Ocr": "OCR", + "TableOfContents": "Table Of Contents", + "Scanned": "Scanned", + "Orientation": "Orientation", + "Paginated": "Paginated", + "Colored": "Colored", + "Cleaned": "Cleaned" + }, + "FictionDetailsTab": { + "Title": "Title", + "Authors": "Authors", + "RussianAuthor": "Author in Russian", + "Series": "Series", + "Publisher": "Publisher", + "Edition": "Edition", + "Year": "Year", + "Language": "Language", + "Format": "Format", + "Pages": "Pages", + "Version": "Version", + "FileSize": "File Size", + "Added": "Added", + "LastModified": "Last Modified", + "Md5Hash": "MD5 hash", + "Comments": "Comments", + "Identifiers": "Identifiers", + "LibgenId": "Libgen ID", + "Isbn": "ISBN", + "GoogleBookId": "Google Books ID", + "Asin": "ASIN" + }, + "SciMagDetailsTab": { + "Title": "Title", + "Authors": "Authors", + "Magazine": "Magazine", + "Year": "Year", + "Month": "Month", + "Day": "Day", + "Volume": "Volume", + "Issue": "Issue", + "Pages": "Pages", + "FileSize": "File Size", + "AddedDateTime": "Added", + "Md5Hash": "MD5 hash", + "AbstractUrl": "Abstract URL", + "Identifiers": "Identifiers", + "LibgenId": "Libgen ID", + "Doi": "DOI", + "Isbn": "ISBN", + "MagazineId": "Magazine ID", + "Issnp": "ISSN (p)", + "Issne": "ISSN (e)", + "PubmedId": "Pubmed ID", + "Pmc": "PMC", + "Pii": "PII", + "AdditionalAttributes": "Additional Attributes", + "Attribute1": "Attribute 1", + "Attribute2": "Attribute 2", + "Attribute3": "Attribute 3", + "Attribute4": "Attribute 4", + "Attribute5": "Attribute 5", + "Attribute6": "Attribute 6" + }, + "Import": { + "WindowTitle": "Import from SQL dump", + "BrowseImportFileDialogTitle": "SQL dump file location", + "AllSupportedFiles": "All supported files", + "SqlDumps": "SQL dumps", + "Archives": "Archives", + "AllFiles": "All files", + "Elapsed": "Time elapsed: {elapsed}", + "FreeSpace": "Free space left on device: {freeSpace}", + "Unknown": "unknown", + "Interrupt": "INTERRUPT", + "Interrupting": "INTERRUPTING...", + "Close": "CLOSE", + "StatusMessages": { + "Step": "Step {current} of {total}.", + "DataLookup": "Table definition lookup", + "CreatingIndexes": "Creating indexes", + "LoadingIds": "Loading identifiers", + "ImportingData": "Importing data", + "ImportComplete": "Import complete", + "ImportCancelled": "Import has been cancelled", + "DataNotFound": "Data not found", + "ImportError": "Import failed" }, - "ExportPanel": - { - "Header": "Export search results to a file", - "Format": "File Format", - "Excel": "Microsoft Excel 2007-2016 (XLSX)", - "Csv": "Comma-separated values (CSV)", - "Separator": "Separator", - "Comma": "Comma", - "Semicolon": "Semicolon", - "Tab": "Tab character", - "SaveAs": "Save as", - "Browse": "Browse...", - "BrowseDialogTitle": "Export search results", - "ExcelFiles": "Microsoft Excel files", - "CsvFiles": "CSV files", - "TsvFiles": "TSV files", - "AllFiles": "All files", - "ExportRange": "Export range", - "NoLimit": "all", - "Limit": "first {count} results only", - "Export": "EXPORT", - "Cancel": "CANCEL", - "SavingFile": "Saving the file.", - "RowCountSingleFile": "Exported rows: {rows}.", - "RowCountMultipleFiles": "Exported rows: {rows}, files created: {files}.", - "ErrorWarningTitle": "Error", - "InvalidExportPath": "Invalid export file path.", - "DirectoryNotFound": "Directory {directory} doesn't exist.", - "InvalidExportFileName": "Invalid export file name.", - "OverwritePromptTitle": "Overwrite the file?", - "OverwritePromptText": "File {file} already exists. Do you want to overwrite it?", - "RowLimitWarningTitle": "Row limit", - "RowLimitWarningText": "Microsoft Excel maximum row count has been reached. Export cannot be continued. In order to export more rows please enable \"Split into multiple files\" option in the application settings.", - "ExportError": "An error occurred during the export.", - "Interrupt": "INTERRUPT", - "Interrupting": "INTERRUPTING...", - "ExportInterrupted": "Export has been interrupted.", - "Results": "OPEN RESULTS", - "Close": "CLOSE" + "LogMessages": { + "Step": "Step {step}", + "DataLookup": "Searching for the table definitions", + "Scanning": "Scanning...", + "ScannedProgress": "{percent}% of the file have been scanned...", + "NonFictionTableFound": "Non-fiction book table found.", + "FictionTableFound": "Fiction book table found.", + "SciMagTableFound": "Scientific article table found.", + "CreatingIndexes": "Creating missing indexes", + "CreatingIndexForColumn": "Creating index for {column} column...", + "LoadingIds": "Loading existing data identifiers", + "LoadingColumnValues": "Loading data for {column} column...", + "ImportingData": "Importing data", + "ImportBooksProgressNoUpdate": "Books added: {added}.", + "ImportBooksProgressWithUpdate": "Books added: {added}, updated: {updated}.", + "ImportArticlesProgressNoUpdate": "Articles added: {added}.", + "ImportArticlesProgressWithUpdate": "Articles added: {added}, updated: {updated}.", + "ImportSuccessful": "Import completed successfully.", + "ImportCancelled": "Import has been cancelled by the user.", + "DataNotFound": "Couldn't find the data to import.", + "InsufficientDiskSpace": "Insufficient disk space.", + "ImportError": "An error occurred during the import." + } + }, + "ExportPanel": { + "Header": "Export search results to a file", + "Format": "File Format", + "Excel": "Microsoft Excel 2007-2016 (XLSX)", + "Csv": "Comma-separated values (CSV)", + "Separator": "Separator", + "Comma": "Comma", + "Semicolon": "Semicolon", + "Tab": "Tab character", + "SaveAs": "Save as", + "Browse": "Browse...", + "BrowseDialogTitle": "Export search results", + "ExcelFiles": "Microsoft Excel files", + "CsvFiles": "CSV files", + "TsvFiles": "TSV files", + "AllFiles": "All files", + "ExportRange": "Export range", + "NoLimit": "all", + "Limit": "first {count} results only", + "Export": "EXPORT", + "Cancel": "CANCEL", + "SavingFile": "Saving the file.", + "RowCountSingleFile": "Exported rows: {rows}.", + "RowCountMultipleFiles": "Exported rows: {rows}, files created: {files}.", + "ErrorWarningTitle": "Error", + "InvalidExportPath": "Invalid export file path.", + "DirectoryNotFound": "Directory {directory} doesn't exist.", + "InvalidExportFileName": "Invalid export file name.", + "OverwritePromptTitle": "Overwrite the file?", + "OverwritePromptText": "File {file} already exists. Do you want to overwrite it?", + "RowLimitWarningTitle": "Row limit", + "RowLimitWarningText": "Microsoft Excel maximum row count has been reached. Export cannot be continued. In order to export more rows please enable \"Split into multiple files\" option in the application settings.", + "ExportError": "An error occurred during the export.", + "Interrupt": "INTERRUPT", + "Interrupting": "INTERRUPTING...", + "ExportInterrupted": "Export has been interrupted.", + "Results": "OPEN RESULTS", + "Close": "CLOSE" + }, + "Exporter": { + "Yes": "yes", + "No": "no", + "Unknown": "unknown", + "Portrait": "portrait", + "Landscape": "landscape", + "NonFictionColumns": { + "Id": "ID", + "Title": "Title", + "Authors": "Authors", + "Series": "Series", + "Publisher": "Publisher", + "Year": "Year", + "Language": "Language", + "Format": "Format", + "Isbn": "ISBN", + "Added": "Added", + "LastModified": "Last Modified", + "Library": "Library", + "FileSize": "File Size", + "Topics": "Topics", + "Volume": "Volume", + "Magazine": "Magazine", + "City": "City", + "Edition": "Edition", + "BodyMatterPages": "Pages (Body Matter)", + "TotalPages": "Pages (Total)", + "Tags": "Tags", + "Md5Hash": "MD5 hash", + "Comments": "Comments", + "LibgenId": "Libgen ID", + "Issn": "ISSN", + "Udc": "UDC", + "Lbc": "LBC", + "Lcc": "LCC", + "Ddc": "DDC", + "Doi": "DOI", + "OpenLibraryId": "Open Library ID", + "GoogleBookId": "Google Books ID", + "Asin": "ASIN", + "Dpi": "DPI", + "Ocr": "OCR", + "TableOfContents": "Table Of Contents", + "Scanned": "Scanned", + "Orientation": "Orientation", + "Paginated": "Paginated", + "Colored": "Colored", + "Cleaned": "Cleaned" }, - "Exporter": - { - "Yes": "yes", - "No": "no", - "Unknown": "unknown", - "Portrait": "portrait", - "Landscape": "landscape", - "NonFictionColumns": - { - "Id": "ID", - "Title": "Title", - "Authors": "Authors", - "Series": "Series", - "Publisher": "Publisher", - "Year": "Year", - "Language": "Language", - "Format": "Format", - "Isbn": "ISBN", - "Added": "Added", - "LastModified": "Last Modified", - "Library": "Library", - "FileSize": "File Size", - "Topics": "Topics", - "Volume": "Volume", - "Magazine": "Magazine", - "City": "City", - "Edition": "Edition", - "BodyMatterPages": "Pages (Body Matter)", - "TotalPages": "Pages (Total)", - "Tags": "Tags", - "Md5Hash": "MD5 hash", - "Comments": "Comments", - "LibgenId": "Libgen ID", - "Issn": "ISSN", - "Udc": "UDC", - "Lbc": "LBC", - "Lcc": "LCC", - "Ddc": "DDC", - "Doi": "DOI", - "OpenLibraryId": "Open Library ID", - "GoogleBookId": "Google Books ID", - "Asin": "ASIN", - "Dpi": "DPI", - "Ocr": "OCR", - "TableOfContents": "Table Of Contents", - "Scanned": "Scanned", - "Orientation": "Orientation", - "Paginated": "Paginated", - "Colored": "Colored", - "Cleaned": "Cleaned" - }, - "FictionColumns": - { - "Id": "ID", - "Title": "Title", - "Authors": "Authors", - "RussianAuthor": "Author in Russian", - "Series": "Series", - "Publisher": "Publisher", - "Edition": "Edition", - "Year": "Year", - "Language": "Language", - "Format": "Format", - "Pages": "Pages", - "Version": "Version", - "FileSize": "File Size", - "Added": "Added", - "LastModified": "Last Modified", - "Md5Hash": "MD5 hash", - "Comments": "Comments", - "LibgenId": "Libgen ID", - "Isbn": "ISBN", - "GoogleBookId": "Google Books ID", - "Asin": "ASIN" - }, - "SciMagColumns": - { - "Id": "ID", - "Title": "Title", - "Authors": "Authors", - "Magazine": "Magazine", - "Year": "Year", - "Month": "Month", - "Day": "Day", - "Volume": "Volume", - "Issue": "Issue", - "Pages": "Pages", - "FileSize": "File Size", - "AddedDateTime": "Added", - "Md5Hash": "MD5 hash", - "AbstractUrl": "Abstract URL", - "LibgenId": "Libgen ID", - "Doi1": "DOI 1", - "Doi2": "DOI 2", - "Isbn": "ISBN", - "MagazineId": "Magazine ID", - "Issnp": "ISSN (p)", - "Issne": "ISSN (e)", - "PubmedId": "Pubmed ID", - "Pmc": "PMC", - "Pii": "PII", - "Attribute1": "Attribute 1", - "Attribute2": "Attribute 2", - "Attribute3": "Attribute 3", - "Attribute4": "Attribute 4", - "Attribute5": "Attribute 5", - "Attribute6": "Attribute 6" - } + "FictionColumns": { + "Id": "ID", + "Title": "Title", + "Authors": "Authors", + "RussianAuthor": "Author in Russian", + "Series": "Series", + "Publisher": "Publisher", + "Edition": "Edition", + "Year": "Year", + "Language": "Language", + "Format": "Format", + "Pages": "Pages", + "Version": "Version", + "FileSize": "File Size", + "Added": "Added", + "LastModified": "Last Modified", + "Md5Hash": "MD5 hash", + "Comments": "Comments", + "LibgenId": "Libgen ID", + "Isbn": "ISBN", + "GoogleBookId": "Google Books ID", + "Asin": "ASIN" }, - "Synchronization": - { - "WindowTitle": "Non-fiction book list synchronization", - "ErrorMessageTitle": "Error", - "ImportRequired": "Non-fiction database dump import needs to be completed in order to start the synchronization.", - "NoSynchronizationMirror": "No non-fiction book synchronization mirror has been selected.", - "OfflineModePromptTitle": "Offline mode", - "OfflineModePromptText": "Synchronization cannot be started while the offline mode is on. Do you want to turn it off?", - "Elapsed": "Time elapsed: {elapsed}", - "FreeSpace": "Free space left on device: {freeSpace}", - "Unknown": "unknown", - "Interrupt": "INTERRUPT", - "Interrupting": "INTERRUPTING...", - "Close": "CLOSE", - "StatusMessages": - { - "Step": "Step {current} of {total}.", - "Preparation": "Preparing for synchronization", - "CreatingIndexes": "Creating indexes", - "LoadingIds": "Loading identifiers", - "SynchronizingData": "Synchronizing the data", - "SynchronizationComplete": "Synchronization complete", - "SynchronizationCancelled": "Synchronization has been cancelled", - "SynchronizationError": "Synchronization failed" - }, - "LogMessages": - { - "Step": "Step {step}", - "CreatingIndexes": "Creating missing indexes", - "CreatingIndexForColumn": "Creating index for {column} column...", - "LoadingIds": "Loading existing data identifiers", - "LoadingColumnValues": "Loading data for {column} column...", - "SynchronizingBookList": "Synchronizing the book list", - "DownloadingNewBooks": "Downloading new book metadata", - "SynchronizationProgressNoAddedNoUpdated": "Books downloaded: {downloaded}.", - "SynchronizationProgressAdded": "Books downloaded: {downloaded}, added: {added}.", - "SynchronizationProgressUpdated": "Books downloaded: {downloaded}, updated: {updated}.", - "SynchronizationProgressAddedAndUpdated": "Books downloaded: {downloaded}, added: {added}, updated: {updated}.", - "SynchronizationSuccessful": "Synchronization completed successfully.", - "SynchronizationCancelled": "Synchronization has been cancelled by the user.", - "InsufficientDiskSpace": "Insufficient disk space.", - "SynchronizationError": "An error occurred during the synchronization: {error}" - } + "SciMagColumns": { + "Id": "ID", + "Title": "Title", + "Authors": "Authors", + "Magazine": "Magazine", + "Year": "Year", + "Month": "Month", + "Day": "Day", + "Volume": "Volume", + "Issue": "Issue", + "Pages": "Pages", + "FileSize": "File Size", + "AddedDateTime": "Added", + "Md5Hash": "MD5 hash", + "AbstractUrl": "Abstract URL", + "LibgenId": "Libgen ID", + "Doi1": "DOI 1", + "Doi2": "DOI 2", + "Isbn": "ISBN", + "MagazineId": "Magazine ID", + "Issnp": "ISSN (p)", + "Issne": "ISSN (e)", + "PubmedId": "Pubmed ID", + "Pmc": "PMC", + "Pii": "PII", + "Attribute1": "Attribute 1", + "Attribute2": "Attribute 2", + "Attribute3": "Attribute 3", + "Attribute4": "Attribute 4", + "Attribute5": "Attribute 5", + "Attribute6": "Attribute 6" + } + }, + "Synchronization": { + "WindowTitle": "Non-fiction book list synchronization", + "ErrorMessageTitle": "Error", + "ImportRequired": "Non-fiction database dump import needs to be completed in order to start the synchronization.", + "NoSynchronizationMirror": "No non-fiction book synchronization mirror has been selected.", + "OfflineModePromptTitle": "Offline mode", + "OfflineModePromptText": "Synchronization cannot be started while the offline mode is on. Do you want to turn it off?", + "Elapsed": "Time elapsed: {elapsed}", + "FreeSpace": "Free space left on device: {freeSpace}", + "Unknown": "unknown", + "Interrupt": "INTERRUPT", + "Interrupting": "INTERRUPTING...", + "Close": "CLOSE", + "StatusMessages": { + "Step": "Step {current} of {total}.", + "Preparation": "Preparing for synchronization", + "CreatingIndexes": "Creating indexes", + "LoadingIds": "Loading identifiers", + "SynchronizingData": "Synchronizing the data", + "SynchronizationComplete": "Synchronization complete", + "SynchronizationCancelled": "Synchronization has been cancelled", + "SynchronizationError": "Synchronization failed" }, - "Library": - { - "TabTitle": "Library", - "ScanNonFiction": "SCAN FOR NON-FICTION BOOKS...", - "ScanFiction": "SCAN FOR FICTION BOOKS...", - "ScanSciMag": "SCAN FOR SCIENTIFIC ARTICLES...", - "BrowseDirectoryDialogTitle": "Scan in...", - "ScanStarted": "Scan started in {directory}", - "CreatingIndexes": "Please wait. Creating missing database indexes...", - "Found": "Found ({count})", - "AddAll": "ADD ALL FOUND FILES TO THE LIBRARY", - "Adding": "ADDING FILES TO THE LIBRARY...", - "Added": "ALL FILES HAVE BEEN ADDED TO THE LIBRARY", - "NotFound": "Not found ({count})", - "ScanLog": "Scan log", - "Error": "error", - "ScanComplete": "Scan complete. Found: {found}, not found: {notFound}, errors: {errors}.", - "Columns": - { - "File": "File", - "Authors": "Authors", - "Title": "Title" - } + "LogMessages": { + "Step": "Step {step}", + "CreatingIndexes": "Creating missing indexes", + "CreatingIndexForColumn": "Creating index for {column} column...", + "LoadingIds": "Loading existing data identifiers", + "LoadingColumnValues": "Loading data for {column} column...", + "SynchronizingBookList": "Synchronizing the book list", + "DownloadingNewBooks": "Downloading new book metadata", + "SynchronizationProgressNoAddedNoUpdated": "Books downloaded: {downloaded}.", + "SynchronizationProgressAdded": "Books downloaded: {downloaded}, added: {added}.", + "SynchronizationProgressUpdated": "Books downloaded: {downloaded}, updated: {updated}.", + "SynchronizationProgressAddedAndUpdated": "Books downloaded: {downloaded}, added: {added}, updated: {updated}.", + "SynchronizationSuccessful": "Synchronization completed successfully.", + "SynchronizationCancelled": "Synchronization has been cancelled by the user.", + "InsufficientDiskSpace": "Insufficient disk space.", + "SynchronizationError": "An error occurred during the synchronization: {error}" + } + }, + "Library": { + "TabTitle": "Library", + "ScanNonFiction": "SCAN FOR NON-FICTION BOOKS...", + "ScanFiction": "SCAN FOR FICTION BOOKS...", + "ScanSciMag": "SCAN FOR SCIENTIFIC ARTICLES...", + "BrowseDirectoryDialogTitle": "Scan in...", + "ScanStarted": "Scan started in {directory}", + "CreatingIndexes": "Please wait. Creating missing database indexes...", + "AddAll": "ADD ALL FOUND FILES TO THE LIBRARY", + "Adding": "ADDING FILES TO THE LIBRARY...", + "Added": "ALL FILES HAVE BEEN ADDED TO THE LIBRARY", + "Found": "Found ({count})", + "NotFound": "Not found ({count})", + "Errors": "Errors ({count})", + "ScanLog": "Scan log", + "Error": "error", + "ScanComplete": "Scan complete. Found: {found}, not found: {notFound}, errors: {errors}.", + "Columns": { + "File": "File", + "Authors": "Authors", + "Title": "Title", + "ErrorType": "Error Type", + "ErrorDescription": "Error Description" }, - "Database": - { - "WindowTitle": "Database information", - "CurrentDatabase": "Current database:", - "NonFiction": "Non-fiction books", - "Fiction": "Fiction books", - "SciMagArticles": "Scientific articles", - "TotalBooks": "Total books", - "TotalArticles": "Total articles", - "LastUpdate": "Last update", + "ErrorDescriptions": { + "DirectoryNotFound": "Directory not found", + "DirectoryAccess": "Insufficient access rights to directory", + "FileSize": "File size is 0 bytes", + "FileNotFound": "File not found", + "FilePathTooLong": "File path is too long", + "FileAccess": "Insufficient access rights to file", + "FileInUse": "File is being used by another process", + "IoException": "Unhandled IO exception occured", + "MD5HashError": "Error creating MD5 hash for file", + "OtherException": "Unhandled exception occured" + } + }, + "Database": { + "WindowTitle": "Database information", + "CurrentDatabase": "Current database:", + "NonFiction": "Non-fiction books", + "Fiction": "Fiction books", + "SciMagArticles": "Scientific articles", + "TotalBooks": "Total books", + "TotalArticles": "Total articles", + "LastUpdate": "Last update", + "Never": "never", + "IndexesRequiredTitle": "Database indexes", + "IndexesRequiredText": "Libgen Desktop needs to create missing database indexes. This operation can take a long time (up to an hour) and cannot be interrupted. Do you want to continue?", + "CreatingIndexes": "Please wait. Creating missing database indexes...{new-line}This operation cannot be interrupted.", + "ChangeDatabase": "CHANGE DATABASE...", + "BrowseDatabaseDialogTitle": "Database file location", + "Databases": "Databases", + "AllFiles": "All files", + "Error": "Error", + "CannotOpenDatabase": "An error occurred while trying to open database file: {file}", + "Close": "CLOSE" + }, + "SqlDebugger": { + "WindowTitle": "SQL debugger", + "SqlQueryTextBoxHeader": "Enter SQL query", + "Copy": "COPY TO CLIPBOARD", + "Close": "CLOSE" + }, + "DownloadManager": { + "TabTitle": "Downloads", + "Start": "START", + "Stop": "STOP", + "Remove": "REMOVE", + "StartAll": "START ALL", + "StopAll": "STOP ALL", + "RemoveCompleted": "REMOVE COMPLETED", + "QueuedStatus": "Queued", + "DownloadingStatus": "Downloading", + "StoppedStatus": "Stopped", + "RetryDelayStatus": "Retry delay", + "ErrorStatus": "Error", + "DownloadProgressKnownFileSize": "Downloaded {downloaded} of {total} bytes ({percent}%)", + "DownloadProgressUnknownFileSize": "Downloaded {downloaded} bytes of unknown", + "Log": "Log", + "TechnicalDetails": "Technical details", + "Copy": "Copy to clipboard", + "FileNotFoundErrorTitle": "Error", + "FileNotFoundErrorText": "File {file} not found.", + "LogMessages": { + "Queued": "Added to the download queue.", + "Started": "Started.", + "Stopped": "Stopped.", + "RetryDelay": "Delay {count} seconds...", + "Completed": "Download complete.", + "OfflineModeIsOn": "Offline mode is on.", + "TransformationError": "Transformation {transformation} failed.", + "TransformationReturnedIncorrectUrl": "Transformation {transformation} returned an invalid URL.", + "Attempt": "Attempt {current} of {total}.", + "MaximumDownloadAttempts": "Maximum download attempts have been reached.", + "DownloadingPage": "Downloading page: {url}", + "DownloadingFile": "Downloading file: {url}", + "StartingFileDownloadKnownFileSize": "File download started, file size: {size} bytes.", + "StartingFileDownloadUnknownFileSize": "File download started, file size: unknown.", + "ResumingFileDownloadKnownFileSize": "File download resumed, remaining size: {remaining} bytes.", + "ResumingFileDownloadUnknownFileSize": "File download resumed, remaining size: unknown.", + "Request": "Request", + "Response": "Server response", + "Redirect": "Redirect to {url}", + "TooManyRedirects": "Too many redirect responses have been received from the server.", + "NonSuccessfulStatusCode": "Server returned: {status}.", + "CannotCreateDownloadDirectory": "Couldn't create directory {directory}", + "CannotCreateOrOpenFile": "Couldn't create or open file {file}", + "CannotRenamePartFile": "Couldn't rename file {source} to {destination}.", + "HtmlPageReturned": "Server returned HTML page instead of the file.", + "NoPartialDownloadSupport": "Server doesn't support partial downloads.", + "NoContentLengthWarning": "Warning: server did not provide the file size.", + "ServerResponseTimeout": "Server response timeout.", + "DownloadIncompleteError": "Server indicates that file download is complete, but it is not.", + "FileWriteError": "File write error.", + "LogLineRequestError": "Couldn't send request to {url}", + "IncorrectRedirectUrl": "Server returned incorrect redirect URL: {url}", + "UnexpectedError": "An unexpected error occurred: {error}" + } + }, + "ApplicationUpdate": { + "WindowTitle": "Application update", + "UpdateAvailable": "New version of Libgen Desktop is available", + "NewVersion": "New version: {version} released on {date}", + "Download": "DOWNLOAD", + "DownloadAndInstall": "DOWNLOAD AND INSTALL", + "SkipThisVersion": "SKIP THIS VERSION", + "Cancel": "CANCEL", + "Interrupt": "INTERRUPT", + "Interrupting": "INTERRUPTING...", + "InterruptPromptTitle": "Cancel the download?", + "InterruptPromptText": "Do you want to cancel downloading the update?", + "Error": "Error", + "IncompleteDownload": "Update download is not complete.", + "Close": "CLOSE" + }, + "Settings": { + "WindowTitle": "Settings", + "Ok": "OK", + "Cancel": "CANCEL", + "DiscardChangesPromptTitle": "Discard changes?", + "DiscardChangesPromptText": "Settings have been changed. Are you sure to discard the changes?", + "General": { + "TabHeader": "General", + "Language": "Language", + "CheckUpdates": "Check for the application updates", + "UpdateCheckIntervals": { "Never": "never", - "IndexesRequiredTitle": "Database indexes", - "IndexesRequiredText": "Libgen Desktop needs to create missing database indexes. This operation can take a long time (up to an hour) and cannot be interrupted. Do you want to continue?", - "CreatingIndexes": "Please wait. Creating missing database indexes...{new-line}This operation cannot be interrupted.", - "ChangeDatabase": "CHANGE DATABASE...", - "BrowseDatabaseDialogTitle": "Database file location", - "Databases": "Databases", - "AllFiles": "All files", - "Error": "Error", - "CannotOpenDatabase": "An error occurred while trying to open database file: {file}", - "Close": "CLOSE" - }, - "SqlDebugger": - { - "WindowTitle": "SQL debugger", - "SqlQueryTextBoxHeader": "Enter SQL query", - "Copy": "COPY TO CLIPBOARD", - "Close": "CLOSE" + "Daily": "daily", + "Weekly": "weekly", + "Monthly": "monthly" + } }, - "DownloadManager": - { - "TabTitle": "Downloads", - "Start": "START", - "Stop": "STOP", - "Remove": "REMOVE", - "StartAll": "START ALL", - "StopAll": "STOP ALL", - "RemoveCompleted": "REMOVE COMPLETED", - "QueuedStatus": "Queued", - "DownloadingStatus": "Downloading", - "StoppedStatus": "Stopped", - "RetryDelayStatus": "Retry delay", - "ErrorStatus": "Error", - "DownloadProgressKnownFileSize": "Downloaded {downloaded} of {total} bytes ({percent}%)", - "DownloadProgressUnknownFileSize": "Downloaded {downloaded} bytes of unknown", - "Log": "Log", - "TechnicalDetails": "Technical details", - "Copy": "Copy to clipboard", - "FileNotFoundErrorTitle": "Error", - "FileNotFoundErrorText": "File {file} not found.", - "LogMessages": - { - "Queued": "Added to the download queue.", - "Started": "Started.", - "Stopped": "Stopped.", - "RetryDelay": "Delay {count} seconds...", - "Completed": "Download complete.", - "OfflineModeIsOn": "Offline mode is on.", - "TransformationError": "Transformation {transformation} failed.", - "TransformationReturnedIncorrectUrl": "Transformation {transformation} returned an invalid URL.", - "Attempt": "Attempt {current} of {total}.", - "MaximumDownloadAttempts": "Maximum download attempts have been reached.", - "DownloadingPage": "Downloading page: {url}", - "DownloadingFile": "Downloading file: {url}", - "StartingFileDownloadKnownFileSize": "File download started, file size: {size} bytes.", - "StartingFileDownloadUnknownFileSize": "File download started, file size: unknown.", - "ResumingFileDownloadKnownFileSize": "File download resumed, remaining size: {remaining} bytes.", - "ResumingFileDownloadUnknownFileSize": "File download resumed, remaining size: unknown.", - "Request": "Request", - "Response": "Server response", - "Redirect": "Redirect to {url}", - "TooManyRedirects": "Too many redirect responses have been received from the server.", - "NonSuccessfulStatusCode": "Server returned: {status}.", - "CannotCreateDownloadDirectory": "Couldn't create directory {directory}", - "CannotCreateOrOpenFile": "Couldn't create or open file {file}", - "CannotRenamePartFile": "Couldn't rename file {source} to {destination}.", - "HtmlPageReturned": "Server returned HTML page instead of the file.", - "NoPartialDownloadSupport": "Server doesn't support partial downloads.", - "NoContentLengthWarning": "Warning: server did not provide the file size.", - "ServerResponseTimeout": "Server response timeout.", - "DownloadIncompleteError": "Server indicates that file download is complete, but it is not.", - "FileWriteError": "File write error.", - "LogLineRequestError": "Couldn't send request to {url}", - "IncorrectRedirectUrl": "Server returned incorrect redirect URL: {url}", - "UnexpectedError": "An unexpected error occurred: {error}" - } + "Network": { + "TabHeader": "Network", + "OfflineMode": "Offline mode", + "UseHttpProxy": "Use HTTP proxy", + "ProxyAddress": "Address", + "ProxyAddressRequired": "Required field", + "ProxyPort": "Port", + "ProxyPortValidation": "Numbers from {min} to {max}", + "ProxyUserName": "User name", + "ProxyPassword": "Password", + "ProxyPasswordWarning": "Storing the password here is insecure." }, - "ApplicationUpdate": - { - "WindowTitle": "Application update", - "UpdateAvailable": "New version of Libgen Desktop is available", - "NewVersion": "New version: {version} released on {date}", - "Download": "DOWNLOAD", - "DownloadAndInstall": "DOWNLOAD AND INSTALL", - "SkipThisVersion": "SKIP THIS VERSION", - "Cancel": "CANCEL", - "Interrupt": "INTERRUPT", - "Interrupting": "INTERRUPTING...", - "InterruptPromptTitle": "Cancel the download?", - "InterruptPromptText": "Do you want to cancel downloading the update?", - "Error": "Error", - "IncompleteDownload": "Update download is not complete.", - "Close": "CLOSE" + "Download": { + "TabHeader": "Downloads", + "DownloadMode": "Download mode", + "OpenInBrowser": "open download URL in the browser", + "UseDownloadManager": "use internal download manager", + "DownloadDirectory": "Download into", + "BrowseDirectoryDialogTitle": "Download directory", + "DownloadDirectoryNotFound": "Directory doesn't exist", + "Timeout": "Download timeout", + "TimeoutValidation": "Numbers: {min} — {max}", + "Seconds": "seconds", + "DownloadAttempts": "Download attempts", + "DownloadAttemptsValidation": "Numbers: {min} — {max}", + "Times": "times", + "RetryDelay": "Download retry delay", + "RetryDelayValidation": "Numbers: {min} — {max}" }, - "Settings": - { - "WindowTitle": "Settings", - "Ok": "OK", - "Cancel": "CANCEL", - "DiscardChangesPromptTitle": "Discard changes?", - "DiscardChangesPromptText": "Settings have been changed. Are you sure to discard the changes?", - "General": - { - "TabHeader": "General", - "Language": "Language", - "CheckUpdates": "Check for the application updates", - "UpdateCheckIntervals": - { - "Never": "never", - "Daily": "daily", - "Weekly": "weekly", - "Monthly": "monthly" - } - }, - "Network": - { - "TabHeader": "Network", - "OfflineMode": "Offline mode", - "UseHttpProxy": "Use HTTP proxy", - "ProxyAddress": "Address", - "ProxyAddressRequired": "Required field", - "ProxyPort": "Port", - "ProxyPortValidation": "Numbers from {min} to {max}", - "ProxyUserName": "User name", - "ProxyPassword": "Password", - "ProxyPasswordWarning": "Storing the password here is insecure." - }, - "Download": - { - "TabHeader": "Downloads", - "DownloadMode": "Download mode", - "OpenInBrowser": "open download URL in the browser", - "UseDownloadManager": "use internal download manager", - "DownloadDirectory": "Download into", - "BrowseDirectoryDialogTitle": "Download directory", - "DownloadDirectoryNotFound": "Directory doesn't exist", - "Timeout": "Download timeout", - "TimeoutValidation": "Numbers: {min} — {max}", - "Seconds": "seconds", - "DownloadAttempts": "Download attempts", - "DownloadAttemptsValidation": "Numbers: {min} — {max}", - "Times": "times", - "RetryDelay": "Download retry delay", - "RetryDelayValidation": "Numbers: {min} — {max}" - }, - "Mirrors": - { - "TabHeader": "Mirrors", - "NonFiction": "Non-fiction books", - "Fiction": "Fiction books", - "SciMagArticles": "Scientific articles", - "Books": "Books", - "Articles": "Articles", - "Covers": "Covers", - "Synchronization": "Synchronization", - "NoMirror": "no mirror" - }, - "Search": - { - "TabHeader": "Search", - "LimitResults": "Limit search results", - "MaximumResults": "Maximum result count", - "PositiveNumbersOnly": "Positive numbers only", - "OpenDetails": "Open book / article details:", - "InModalWindow": "in a modal window", - "InNonModalWindow": "in a non-modal window", - "InNewTab": "in a new tab" - }, - "Export": - { - "TabHeader": "Export", - "OpenResults": "Open results after the export", - "SplitIntoMultipleFiles": "Split into multiple files", - "MaximumRowsPerFile": "Maximum rows per file", - "MaximumRowsPerFileValidation": "Numbers from {min} to {max}", - "ExcelLimitNote": "Note: Microsoft Excel doesn't support files with more than {count} rows." - }, - "Advanced": - { - "TabHeader": "Advanced", - "UseLogging": "Write diagnostic information into a file", - "EnableSqlDebugger": "Enable SQL debugger" - } + "Mirrors": { + "TabHeader": "Mirrors", + "NonFiction": "Non-fiction books", + "Fiction": "Fiction books", + "SciMagArticles": "Scientific articles", + "Books": "Books", + "Articles": "Articles", + "Covers": "Covers", + "Synchronization": "Synchronization", + "NoMirror": "no mirror" }, - "About": - { - "WindowTitle": "About Libgen Desktop", - "ApplicationName": "Libgen Desktop", - "Version": "Current version: {version} released on {date}", - "CheckForUpdates": "CHECK FOR UPDATES", - "CheckingUpdates": "CHECKING UPDATES...", - "OfflineModeIsOnTooltip": "Offline mode is on", - "LatestVersion": "You are using the latest version.", - "NewVersionAvailable": "New version is available: {version} released on {date}", - "Update": "UPDATE", - "Translators": "Translators:" + "Search": { + "TabHeader": "Search", + "LimitResults": "Limit search results", + "MaximumResults": "Maximum result count", + "PositiveNumbersOnly": "Positive numbers only", + "OpenDetails": "Open book / article details:", + "InModalWindow": "in a modal window", + "InNonModalWindow": "in a non-modal window", + "InNewTab": "in a new tab" }, - "MessageBox": - { - "Ok": "OK", - "Yes": "YES", - "No": "NO" + "Export": { + "TabHeader": "Export", + "OpenResults": "Open results after the export", + "SplitIntoMultipleFiles": "Split into multiple files", + "MaximumRowsPerFile": "Maximum rows per file", + "MaximumRowsPerFileValidation": "Numbers from {min} to {max}", + "ExcelLimitNote": "Note: Microsoft Excel doesn't support files with more than {count} rows." }, - "ErrorWindow": - { - "WindowTitle": "Error", - "UnexpectedError": "An unexpected error occurred", - "Copy": "COPY TO CLIPBOARD", - "Close": "CLOSE" + "Advanced": { + "TabHeader": "Advanced", + "UseLogging": "Write diagnostic information into a file", + "EnableSqlDebugger": "Enable SQL debugger" } + }, + "About": { + "WindowTitle": "About Libgen Desktop", + "ApplicationName": "Libgen Desktop", + "Version": "Current version: {version} released on {date}", + "CheckForUpdates": "CHECK FOR UPDATES", + "CheckingUpdates": "CHECKING UPDATES...", + "OfflineModeIsOnTooltip": "Offline mode is on", + "LatestVersion": "You are using the latest version.", + "NewVersionAvailable": "New version is available: {version} released on {date}", + "Update": "UPDATE", + "Translators": "Translators:" + }, + "MessageBox": { + "Ok": "OK", + "Yes": "YES", + "No": "NO" + }, + "ErrorWindow": { + "WindowTitle": "Error", + "UnexpectedError": "An unexpected error occurred", + "Copy": "COPY TO CLIPBOARD", + "Close": "CLOSE" + } } diff --git a/LibgenDesktop/ViewModels/Library/ScanResultErrorItemViewModel.cs b/LibgenDesktop/ViewModels/Library/ScanResultErrorItemViewModel.cs new file mode 100644 index 0000000..da9624a --- /dev/null +++ b/LibgenDesktop/ViewModels/Library/ScanResultErrorItemViewModel.cs @@ -0,0 +1,17 @@ +using LibgenDesktop.Models.ProgressArgs; + +namespace LibgenDesktop.ViewModels.Library +{ + internal class ScanResultErrorItemViewModel : ViewModel + { + public ScanResultErrorItemViewModel(string relativeFilePath, ErrorTypes errorTypes) + { + RelativeFilePath = relativeFilePath; + ErrorType = errorTypes; + } + + public ErrorTypes ErrorType { get; } + public string RelativeFilePath { get; } + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/LibgenDesktop/ViewModels/Tabs/LibraryTabViewModel.cs b/LibgenDesktop/ViewModels/Tabs/LibraryTabViewModel.cs index 036ba31..33ed490 100644 --- a/LibgenDesktop/ViewModels/Tabs/LibraryTabViewModel.cs +++ b/LibgenDesktop/ViewModels/Tabs/LibraryTabViewModel.cs @@ -30,6 +30,7 @@ private enum Mode private bool isResultsPanelVisible; private string foundTabHeaderTitle; private string notFoundTabHeaderTitle; + private string errorsTabHeaderTitle; private bool isScanLogTabSelected; private bool isAddAllFilesToLibraryButtonVisible; private bool isAddAllFilesToLibraryButtonEnabled; @@ -37,6 +38,7 @@ private enum Mode private ObservableCollection foundItems; private ScanResultItemViewModel selectedFoundItem; private ObservableCollection notFoundItems; + private ObservableCollection errors; private ObservableCollection scanLogs; private string scanDirectory; private bool hasFilesToAdd; @@ -121,6 +123,19 @@ public string NotFoundTabHeaderTitle } } + public string ErrorsTabHeaderTitle + { + get + { + return errorsTabHeaderTitle; + } + set + { + errorsTabHeaderTitle = value; + NotifyPropertyChanged(); + } + } + public bool IsScanLogTabSelected { get @@ -212,6 +227,19 @@ public ObservableCollection NotFoundItems } } + public ObservableCollection Errors + { + get + { + return errors; + } + set + { + errors = value; + NotifyPropertyChanged(); + } + } + public ObservableCollection ScanLogs { get @@ -277,6 +305,7 @@ private async void Scan(Func, Task> scanFunction) wh mode = Mode.SCANNING; FoundItems = new ObservableCollection(); NotFoundItems = new ObservableCollection(); + Errors = new ObservableCollection(); UpdateResultTabHeaders(); UpdateAddAllFilesToLibraryButton(); IsScanLogTabSelected = true; @@ -317,12 +346,15 @@ private void HandleScanProgress(object progress) where T : LibgenObject FoundItems.Add(new SciMagScanResultItemViewModel(scanProgress.RelativeFilePath, sciMagArticle)); break; } - UpdateResultTabHeaders(); + UpdateResultTabHeaders(); break; case ScanUnknownProgress scanUnknownProgress: if (scanUnknownProgress.Error) { - ScanLogs.Add($"{scanUnknownProgress.RelativeFilePath} — {Localization.Error}."); + var error = new ScanResultErrorItemViewModel(scanUnknownProgress.RelativeFilePath, scanUnknownProgress.ErrorType); + ResolveErrorDescription(error); + Errors.Add(error); + UpdateResultTabHeaders(); } else { @@ -346,6 +378,16 @@ private void UpdateResultTabHeaders() { NotFoundTabHeaderTitle = Localization.GetNotFoundString(NotFoundItems.Count); } + if (Errors != null) + { + ErrorsTabHeaderTitle = Localization.GetErrorsString(Errors.Count); + } + } + + private void ResolveErrorDescription(ScanResultErrorItemViewModel error) + { + if (Errors == null || error == null) return; + error.Description = Localization.GetErrorDescription(error.ErrorType); } private void UpdateAddAllFilesToLibraryButton() diff --git a/LibgenDesktop/Views/Tabs/LibraryTab.xaml b/LibgenDesktop/Views/Tabs/LibraryTab.xaml index 8628ef4..f28fec6 100644 --- a/LibgenDesktop/Views/Tabs/LibraryTab.xaml +++ b/LibgenDesktop/Views/Tabs/LibraryTab.xaml @@ -95,6 +95,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LibgenDesktop/packages.config b/LibgenDesktop/packages.config index 85bade8..709ce1e 100644 --- a/LibgenDesktop/packages.config +++ b/LibgenDesktop/packages.config @@ -9,7 +9,6 @@ - \ No newline at end of file