-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Add support for book front covers #14330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
bblhd
wants to merge
66
commits into
JabRef:main
Choose a base branch
from
bblhd:feature/book-front-cover-10120
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 14 commits
Commits
Show all changes
66 commits
Select commit
Hold shift + click to select a range
d29ab3b
Added initial work to get basic image covers working properly
bblhd 7b33850
Merge branch 'JabRef:main' into feature/book-front-cover-10120
bblhd 05d21d4
Merge branch 'main' of https://github.com/JabRef/jabref into feature/…
bblhd 2865553
fixed order of cover images, refactored some of the cover code
bblhd 7842c6e
rudimentary cover fetching
bblhd 3f91954
changed tabs to spaces
bblhd bb58664
merged with upstream
bblhd 112a095
fixed problems and changed cover tag parenthesis for display
bblhd 56475f3
fixed small issues, re-re-factoring
bblhd 68e55c6
incorrect name, now getCoverImageFileFromStringOfISBN
bblhd a9e8120
merged with upstream
bblhd 71fba83
fixed pattern naming somewhat, improved handling of edge cases and ad…
bblhd 0d97ac5
Added preferences for cover images
bblhd 25b996c
merge from upstream
bblhd f4946cd
Merge branch 'main' into feature/book-front-cover-10120
Siedlerchr 2c61ef3
Fix submodule changes
bblhd dc614c4
style changes
bblhd 5e9b8af
fixes to getSuggestedFileName and added localization for new text
bblhd 2d9abbc
Merge branch 'main' into feature/book-front-cover-10120
bblhd b56b664
fixed problem in getSuggestedFileName
bblhd 45ff913
further fixes
bblhd 4f6a3b6
fixed missing period in suggested filename
bblhd 8b05127
fixed bad LinkedList initializer in test and missed whitespace
bblhd 9d705fc
brought behaviour of getSuggestedFileName(extension) in line with get…
bblhd 73831aa
fixed errors in test
bblhd d7a6537
removed unused import
bblhd 692db5f
fixed test cases and some rewrite checks
bblhd 23f3203
fixed test name collision
bblhd c5d7d7e
reversed my misunderstanding of org.openrewrite.java.migrate.util.Opt…
bblhd e5df521
added pull request to changelog, added tests for getFileNameFromUrl, …
bblhd b3473b0
Merge branch 'main' into feature/book-front-cover-10120
bblhd 77398d8
removed wrong arguments from test
bblhd ca5dcb2
removed wrong arguments from test
bblhd cd33ce2
improved behaviour of BookCoverFetcher when no directory available
bblhd df05e83
fix for changed argument order
bblhd a41f391
filled catch block
bblhd 9ef2bb4
changed uses of path.tofile().method() to Files.method(path) where po…
bblhd cbb3340
added mime type detection for existing book covers
bblhd 4124d98
fixed import order
bblhd 4f6607f
Merge branch 'main' into feature/book-front-cover-10120
bblhd 99fe001
missing semicolon
bblhd 777ad41
fixed new variable and removed superfluous check
bblhd 9fc23be
missing argument somehow
bblhd 966fd67
Update jabgui/src/main/java/org/jabref/gui/importer/BookCoverFetcher.…
bblhd c68ef91
initial rework of covers, away from using attached files
bblhd bc9f0b4
Merge branch 'main' into feature/book-front-cover-10120
bblhd 622838b
replaced findFileByNameWithAnyExtension with simpler code that is eas…
bblhd ca67eb3
removed LinkedFile#getURI/getURL due to lack of use
bblhd 3ebdc55
improved test sources
bblhd f2d7a1a
fixed test formating
bblhd 7f17cd9
fixed name of getValidFileName
bblhd 477d845
fixed missing .get() for Optional
bblhd 6824940
fixed missing import
bblhd b0be871
fixed things missed in book cover changes
bblhd 609371a
better use of map
bblhd 5813bcc
fixed syntax
bblhd 11b3570
corrected bad definition and removed createFileNameFromPattern "defau…
bblhd fb1deab
Merge branch 'main' into feature/book-front-cover-10120
bblhd 2cb5c99
minor BookCoverFetcher fixes
bblhd 05d3561
more usage of map, implemented basic seperation of cover images from …
bblhd ef72987
Merge branch 'main' into feature/book-front-cover-10120
bblhd 8b87815
fixed mangled merge
bblhd d642f6c
fixed test using Argument.of rather than Arguments.of
bblhd 0d474a3
more Arguments fix, removed unused imports, other syntax fixes
bblhd 8f39853
fixes for wrong import and made getFileNameFromUrl return Optional, p…
bblhd 3d9b17f
fixed missing character when parsing urls, removed bad test case
bblhd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
jabgui/src/main/java/org/jabref/gui/importer/BookCoverFetcher.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| package org.jabref.gui.importer; | ||
|
|
||
| import java.net.MalformedURLException; | ||
| import java.net.URISyntaxException; | ||
| import java.net.URL; | ||
| import java.io.File; | ||
| import java.nio.file.Path; | ||
| import java.util.Optional; | ||
| import java.util.regex.Pattern; | ||
| import java.util.regex.Matcher; | ||
|
|
||
| import org.apache.hc.core5.net.URIBuilder; | ||
|
|
||
| import org.jabref.gui.externalfiletype.ExternalFileType; | ||
| import org.jabref.gui.externalfiletype.ExternalFileTypes; | ||
| import org.jabref.gui.frame.ExternalApplicationsPreferences; | ||
| import org.jabref.model.database.BibDatabaseContext; | ||
| import org.jabref.logic.FilePreferences; | ||
| import org.jabref.model.entry.BibEntry; | ||
| import org.jabref.model.entry.identifier.ISBN; | ||
| import org.jabref.model.entry.LinkedFile; | ||
| import org.jabref.logic.importer.FetcherException; | ||
| import org.jabref.logic.net.URLDownload; | ||
| import org.jabref.logic.util.io.FileUtil; | ||
|
|
||
| import kong.unirest.core.UnirestException; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| /** | ||
| * Searches web resources for bibliographic information. | ||
| */ | ||
| public class BookCoverFetcher { | ||
|
|
||
| private static final Logger LOGGER = LoggerFactory.getLogger(BookCoverFetcher.class); | ||
|
|
||
| private static final Pattern JSON_CONTAINING_URL_PATTERN = Pattern.compile("^\\s*\\{\\s*\"url\"\\s*:\\s*\"([^\"]*)\"\\s*\\}\\s*$"); | ||
|
|
||
| public static Optional<BibEntry> withAttachedCoverFileIfExists(Optional<BibEntry> possible, BibDatabaseContext databaseContext, FilePreferences filePreferences, ExternalApplicationsPreferences externalApplicationsPreferences) { | ||
| if (possible.isPresent() && filePreferences.shouldDownloadCovers()) { | ||
| BibEntry entry = possible.get(); | ||
| Optional<ISBN> isbn = entry.getISBN(); | ||
| if (isbn.isPresent()) { | ||
| final String url = getCoverImageURLForIsbn(isbn.get()); | ||
| final Path directory = databaseContext.getFirstExistingFileDir(filePreferences).orElse(filePreferences.getWorkingDirectory()); | ||
|
|
||
| // Cannot use pattern for name, as auto-generated citation keys aren't available where function is used (org.jabref.gui.newentry.NewEntryViewModel#withCoversAttached) | ||
| final String name = "isbn-"+isbn.get().asString(); | ||
|
|
||
| Optional<LinkedFile> file = tryToDownloadLinkedFile(externalApplicationsPreferences, directory, url, filePreferences.coversDownloadLocation(), name); | ||
| if (file.isPresent()) { | ||
| entry.addFile(file.get()); | ||
| } | ||
| } | ||
| possible = Optional.of(entry); | ||
| } | ||
| return possible; | ||
| } | ||
|
|
||
| private static String getCoverImageURLForIsbn(ISBN isbn) { | ||
| if (isbn.isIsbn13()) { | ||
| String url = "https://bookcover.longitood.com/bookcover/" + isbn.asString(); | ||
bblhd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try { | ||
| LOGGER.info("Downloading book cover url from {}", url); | ||
|
|
||
| URLDownload download = new URLDownload(url); | ||
| download.canBeReached(); | ||
|
|
||
| String json = download.asString(); | ||
| Matcher matches = JSON_CONTAINING_URL_PATTERN.matcher(json); | ||
|
|
||
| if (matches.find()) { | ||
| String coverUrlString = matches.group(1); | ||
| if (coverUrlString != null) { | ||
| return coverUrlString; | ||
| } | ||
| } | ||
| } catch (MalformedURLException | FetcherException e) { | ||
| LOGGER.error("Error while querying cover url, using fallback", e); | ||
| } | ||
| } | ||
| return "https://covers.openlibrary.org/b/isbn/" + isbn.asString() + "-L.jpg"; | ||
| } | ||
|
|
||
| private static Optional<LinkedFile> tryToDownloadLinkedFile(ExternalApplicationsPreferences externalApplicationsPreferences, Path directory, String url, String location, String name) { | ||
| final Path subdirectory = directory.resolve(location); | ||
|
|
||
| subdirectory.toFile().mkdirs(); | ||
| if (subdirectory.toFile().exists()) { | ||
| final Optional<String> extension = FileUtil.getFileExtension(FileUtil.getFileNameFromUrl(url)); | ||
| final Path destination = subdirectory.resolve(extension.map(x -> name + "." + x).orElse(name)); | ||
| final String link = directory.relativize(destination).toString(); | ||
|
|
||
| if (destination.toFile().exists()) { | ||
| return Optional.of(new LinkedFile("[cover]", link, inferFileTypeFromExtension(externalApplicationsPreferences, extension), url)); | ||
|
|
||
| } else try { | ||
| LOGGER.info("Downloading cover image file from {}", url); | ||
|
|
||
| URLDownload download = new URLDownload(url); | ||
| download.canBeReached(); | ||
|
|
||
| final String type = inferFileType(externalApplicationsPreferences, download.getMimeType(), extension); | ||
| download.toFile(destination); | ||
| return Optional.of(new LinkedFile("[cover]", link, type, url)); | ||
|
|
||
| } catch (UnirestException | FetcherException | MalformedURLException e) { | ||
| LOGGER.error("Error while downloading cover image file", e); | ||
| } | ||
| } else { | ||
| LOGGER.warn("File directory not available while downloading cover image {}. Storing as URL in file field.", url); | ||
| return Optional.of(new LinkedFile("[cover]", url, "")); | ||
| } | ||
|
|
||
| return Optional.empty(); | ||
| } | ||
|
|
||
| private static String inferFileType(ExternalApplicationsPreferences externalApplicationsPreferences, Optional<String> mime, Optional<String> extension) { | ||
| if (mime.isPresent()) { | ||
| Optional<ExternalFileType> suggested = ExternalFileTypes.getExternalFileTypeByMimeType(mime.get(), externalApplicationsPreferences); | ||
| if (suggested.isPresent()) { | ||
| return suggested.get().getName(); | ||
| } | ||
| } | ||
| return inferFileTypeFromExtension(externalApplicationsPreferences, extension); | ||
| } | ||
|
|
||
| private static String inferFileTypeFromExtension(ExternalApplicationsPreferences externalApplicationsPreferences, Optional<String> extension) { | ||
| return extension.map(x -> ExternalFileTypes.getExternalFileTypeByExt(x, externalApplicationsPreferences).map(t -> t.getName()).orElse("")).orElse(""); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,10 @@ | |
|
|
||
| import java.io.IOException; | ||
| import java.net.MalformedURLException; | ||
| import java.nio.file.Path; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
|
|
||
| import javafx.beans.InvalidationListener; | ||
| import javafx.beans.Observable; | ||
|
|
@@ -25,14 +27,19 @@ | |
| import org.jabref.gui.theme.ThemeManager; | ||
| import org.jabref.gui.util.UiTaskExecutor; | ||
| import org.jabref.gui.util.WebViewStore; | ||
| import org.jabref.gui.externalfiletype.ExternalFileType; | ||
| import org.jabref.gui.externalfiletype.ExternalFileTypes; | ||
| import org.jabref.logic.l10n.Localization; | ||
| import org.jabref.logic.layout.format.Number; | ||
| import org.jabref.logic.preview.PreviewLayout; | ||
| import org.jabref.logic.util.BackgroundTask; | ||
| import org.jabref.logic.util.TaskExecutor; | ||
| import org.jabref.logic.util.io.FileUtil; | ||
| import org.jabref.logic.util.strings.StringUtil; | ||
| import org.jabref.model.database.BibDatabaseContext; | ||
| import org.jabref.model.entry.BibEntry; | ||
| import org.jabref.model.entry.LinkedFile; | ||
| import org.jabref.model.entry.types.StandardEntryType; | ||
| import org.jabref.model.search.query.SearchQuery; | ||
|
|
||
| import com.airhacks.afterburner.injection.Injector; | ||
|
|
@@ -222,17 +229,67 @@ private String formatError(BibEntry entry, Throwable exception) { | |
| } | ||
|
|
||
| private void setPreviewText(String text) { | ||
| String coverIfAny = ""; | ||
| Optional<String> image = getCoverImageURI(); | ||
| if (image.isPresent()) { | ||
| coverIfAny = "<img style=\"border-width:1px; border-style:solid; border-color:black; display:block; height:12rem;\" src=\"%s\"> <br>".formatted(image.get()); | ||
| } | ||
|
|
||
| layoutText = """ | ||
| <html> | ||
| <body id="previewBody"> | ||
| <div id="content"> %s </div> | ||
| %s <div id="content"> %s </div> | ||
| </body> | ||
| </html> | ||
| """.formatted(text); | ||
| """.formatted(coverIfAny, text); | ||
| highlightLayoutText(); | ||
| setHvalue(0); | ||
| } | ||
|
|
||
| private Optional<String> getCoverImageURI() { | ||
bblhd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (shouldShowCoverImage()) { | ||
| String nameFromFormat = FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, preferences.getFilePreferences().getFileNamePattern()).orElse("cover"); | ||
|
|
||
| List<LinkedFile> linkedFiles = entry.getFiles(); | ||
| for (LinkedFile file : linkedFiles) { | ||
| // matches images that are either named according to the preferred file name format | ||
| // or images with case-insensitive "[cover]" in their description, to allow using any image regardless of name | ||
|
|
||
| if (file.getDescription().toLowerCase().contains("[cover]") || isFileTypeAValidCoverImage(file.getFileType()) && (FileUtil.getBaseName(file.getFileName()).equals(nameFromFormat))) { | ||
|
||
| return file.getURI(databaseContext, preferences.getFilePreferences()); | ||
| } | ||
| } | ||
| } | ||
| return Optional.empty(); | ||
| } | ||
|
|
||
| private boolean shouldShowCoverImage() { | ||
| //entry is sometimes null when setPreviewText is called | ||
| if (entry == null) { | ||
| return false; | ||
| } | ||
|
|
||
| return switch (entry.getType()) { | ||
| case StandardEntryType.Book, StandardEntryType.Booklet, StandardEntryType.BookInBook, StandardEntryType.InBook, StandardEntryType.MvBook -> true; | ||
| default -> false; | ||
| }; | ||
| } | ||
|
|
||
| private boolean isFileTypeAValidCoverImage(String fileType) { | ||
| // to allow url links | ||
| if (fileType.equals("")) { | ||
| return true; | ||
| } | ||
|
|
||
| // needed because type names are stored in a localization dependent way | ||
| Optional<ExternalFileType> actualFileType = ExternalFileTypes.getExternalFileTypeByName(fileType, preferences.getExternalApplicationsPreferences()); | ||
|
|
||
| if (actualFileType.isPresent()) { | ||
| return actualFileType.get().getMimeType().startsWith("image/"); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| private void highlightLayoutText() { | ||
| if (layoutText == null) { | ||
| return; | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.