diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/BookMapper.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/BookMapper.java index d6d190e7f..e3b9497f1 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/BookMapper.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/BookMapper.java @@ -6,7 +6,10 @@ import com.adityachandel.booklore.model.entity.AuthorEntity; import com.adityachandel.booklore.model.entity.BookEntity; import com.adityachandel.booklore.model.entity.CategoryEntity; import com.adityachandel.booklore.model.entity.LibraryPathEntity; -import org.mapstruct.*; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; import java.util.Set; import java.util.stream.Collectors; diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/FetchedProposalMapper.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/FetchedProposalMapper.java index eb635f839..427ba1eeb 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/FetchedProposalMapper.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/FetchedProposalMapper.java @@ -5,7 +5,10 @@ import com.adityachandel.booklore.model.dto.FetchedProposal; import com.adityachandel.booklore.model.entity.MetadataFetchProposalEntity; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; -import org.mapstruct.*; +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; import org.springframework.beans.factory.annotation.Autowired; @Mapper(componentModel = "spring") diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryMapper.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryMapper.java index 9d54f665a..6808e85f9 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryMapper.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryMapper.java @@ -5,8 +5,6 @@ import com.adityachandel.booklore.model.entity.LibraryEntity; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import java.util.List; - @Mapper(componentModel = "spring") public interface LibraryMapper { diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryPathMapper.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryPathMapper.java index c09c13941..ebc5c39fa 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryPathMapper.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/LibraryPathMapper.java @@ -1,13 +1,8 @@ package com.adityachandel.booklore.mapper; import com.adityachandel.booklore.model.dto.LibraryPath; -import com.adityachandel.booklore.model.dto.Shelf; import com.adityachandel.booklore.model.entity.LibraryPathEntity; -import com.adityachandel.booklore.model.entity.ShelfEntity; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; - -import java.util.List; @Mapper(componentModel = "spring") public interface LibraryPathMapper { diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/OpdsUserMapper.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/OpdsUserMapper.java index d468c2760..2518ae80f 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/OpdsUserMapper.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/OpdsUserMapper.java @@ -1,9 +1,7 @@ package com.adityachandel.booklore.mapper; import com.adityachandel.booklore.model.dto.OpdsUser; -import com.adityachandel.booklore.model.dto.Shelf; import com.adityachandel.booklore.model.entity.OpdsUserEntity; -import com.adityachandel.booklore.model.entity.ShelfEntity; import org.mapstruct.Mapper; @Mapper(componentModel = "spring") diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java index 154b1593b..95e3ee271 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/custom/BookLoreUserTransformer.java @@ -13,7 +13,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.Collections; -import java.util.List; import java.util.stream.Collectors; @Slf4j diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/v2/BookMapperV2.java b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/v2/BookMapperV2.java index 7d3a123e6..9a9f8e55a 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/mapper/v2/BookMapperV2.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/mapper/v2/BookMapperV2.java @@ -3,12 +3,10 @@ package com.adityachandel.booklore.mapper.v2; import com.adityachandel.booklore.model.dto.Book; import com.adityachandel.booklore.model.dto.BookMetadata; import com.adityachandel.booklore.model.dto.LibraryPath; -import com.adityachandel.booklore.model.entity.AuthorEntity; -import com.adityachandel.booklore.model.entity.BookEntity; -import com.adityachandel.booklore.model.entity.BookMetadataEntity; -import com.adityachandel.booklore.model.entity.CategoryEntity; -import com.adityachandel.booklore.model.entity.LibraryPathEntity; -import org.mapstruct.*; +import com.adityachandel.booklore.model.entity.*; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; import java.util.Set; import java.util.stream.Collectors; diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Comic.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Comic.java index eddffc0b9..d85ee6e20 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Comic.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Comic.java @@ -1,127 +1,83 @@ package com.adityachandel.booklore.model.dto.response.comicvineapi; -import java.time.LocalDate; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo( - use = JsonTypeInfo.Id.NAME, - include = JsonTypeInfo.As.EXISTING_PROPERTY, - - property = "resource_type", - defaultImpl = Volume.class -) +public class Comic { -@JsonSubTypes({ - @JsonSubTypes.Type(value = Volume.class, name = "volume"), - @JsonSubTypes.Type(value = Issue.class, name = "issue") -}) + private int id; -public abstract class Comic { - private int id; - protected String name; - protected ComicVineImage image; - private String description; + @JsonProperty("api_detail_url") + private String apiDetailUrl; - @JsonProperty("concept_credits") - private List conceptCredits; + @JsonProperty("cover_date") + private String coverDate; + private String description; - @JsonProperty("person_credits") - private List personCredits; + private String name; + @JsonProperty("issue_number") + private String issueNumber; + private Image image; + private Volume volume; - - @JsonProperty("resource_type") - private String resourceType; - - - public abstract String getDisplayName(); - - public abstract String getComicId(); - - public abstract LocalDate getDate(); - - public Set getAuthors() { - Set authors = new HashSet<>(); - if (personCredits != null) { - for (ComicvineItem person : personCredits) { - authors.add(person.name); - } - } - return authors; - } - - public String getImageUrl() { - if (image == null) { - return null; - } - return image.getOriginalUrl() != null ? image.getOriginalUrl() : image.getThumbUrl(); - } - + @JsonProperty("resource_type") + private String resourceType; @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) - public static class ComicVineImage { + public static class Image { @JsonProperty("icon_url") private String iconUrl; - + @JsonProperty("medium_url") private String mediumUrl; - + @JsonProperty("screen_url") private String screenUrl; - + @JsonProperty("screen_large_url") private String screenLargeUrl; - + @JsonProperty("small_url") private String smallUrl; - + @JsonProperty("super_url") private String superUrl; - + @JsonProperty("thumb_url") private String thumbUrl; - + @JsonProperty("tiny_url") private String tinyUrl; - + @JsonProperty("original_url") private String originalUrl; - + @JsonProperty("image_tags") private String imageTags; } - - @Data + @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) - public static class ComicvineItem { - @JsonProperty("api_detail_url") - private String apiDetailUrl; - + public static class Volume { private int id; private String name; + + @JsonProperty("api_detail_url") + private String apiDetailUrl; + + @JsonProperty("site_detail_url") + private String siteDetailUrl; } - - - } - - \ No newline at end of file +} \ No newline at end of file diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/ComicvineApiResponse.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/ComicvineApiResponse.java index 5a1f9db49..e640172b2 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/ComicvineApiResponse.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/ComicvineApiResponse.java @@ -2,16 +2,11 @@ package com.adityachandel.booklore.model.dto.response.comicvineapi; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; - - import lombok.Data; import lombok.NoArgsConstructor; - - import java.util.List; - @Data @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) @@ -30,5 +25,4 @@ public class ComicvineApiResponse { private int statusCode; private List results; private String version; - } \ No newline at end of file diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/ComicvineIssueResponse.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/ComicvineIssueResponse.java new file mode 100644 index 000000000..62b7f3a51 --- /dev/null +++ b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/ComicvineIssueResponse.java @@ -0,0 +1,35 @@ +package com.adityachandel.booklore.model.dto.response.comicvineapi; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ComicvineIssueResponse { + private String error; + private int limit; + private int offset; + private int number_of_page_results; + private int number_of_total_results; + private int status_code; + private IssueResults results; + private String version; + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class IssueResults { + @JsonProperty("person_credits") + private List personCredits; + } + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + public static class PersonCredit { + private long id; + private String name; + private String role; + } +} \ No newline at end of file diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Issue.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Issue.java deleted file mode 100644 index e40c3e3c8..000000000 --- a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Issue.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.adityachandel.booklore.model.dto.response.comicvineapi; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonIgnoreProperties(ignoreUnknown = true) -public class Issue extends Comic { - @JsonProperty("cover_date") - private String coverDate; - - @JsonProperty("volume") - private ComicvineItem volume; - - @JsonProperty("issue_number") - private int issueNumber; - - - @Override - public String getComicId() { - return "4000-" + String.valueOf(getId()); - } - - @Override - public String getDisplayName() { - if(name ==null){ - if(volume != null){ - return volume.getName() + " " + "Issue #" + String.valueOf(issueNumber); - - } - else{ - return "Unknown Comic"; - } - - - } - return name; - } - - @Override - public LocalDate getDate() { - if (coverDate == null || coverDate.isEmpty()) return null; - try { - return LocalDate.parse(coverDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); - } catch (Exception e) { - return null; - } - } -} diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Volume.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Volume.java deleted file mode 100644 index a82084107..000000000 --- a/booklore-api/src/main/java/com/adityachandel/booklore/model/dto/response/comicvineapi/Volume.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.adityachandel.booklore.model.dto.response.comicvineapi; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; - - -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@JsonIgnoreProperties(ignoreUnknown = true) -public class Volume extends Comic{ - @JsonProperty("api_detail_url") - private String apiDetailUrl; - - private String description; - - @JsonProperty("publisher") - private ComicvineItem publisher; - - @JsonProperty("site_detail_url") - private String siteDetailUrl; - - @JsonProperty("start_year") - private String startYear; - - public String getPublisherName() { - return publisher != null ? publisher.getName() : null; - } - - - @Override - public String getDisplayName(){ - if(name==null){ - return "Unknown Comic"; - } - else{ - return name; - } - } - - @Override - public String getComicId() { - return "4500-" + String.valueOf(getId()); - } - - @Override - public LocalDate getDate() { - if (startYear == null || startYear.isEmpty()) return null; - try { - return LocalDate.parse(startYear, DateTimeFormatter.ofPattern("yyyy")); - } catch (Exception e) { - return null; - } - } -} - diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/parser/ComicvineBookParser.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/parser/ComicvineBookParser.java index 56e905cd4..2808abbed 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/parser/ComicvineBookParser.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/parser/ComicvineBookParser.java @@ -3,10 +3,8 @@ package com.adityachandel.booklore.service.metadata.parser; import com.adityachandel.booklore.model.dto.Book; import com.adityachandel.booklore.model.dto.BookMetadata; import com.adityachandel.booklore.model.dto.request.FetchMetadataRequest; -import com.adityachandel.booklore.model.dto.response.comicvineapi.ComicvineApiResponse; -import com.adityachandel.booklore.model.dto.response.comicvineapi.Issue; -import com.adityachandel.booklore.model.dto.response.comicvineapi.Volume; import com.adityachandel.booklore.model.dto.response.comicvineapi.Comic; +import com.adityachandel.booklore.model.dto.response.comicvineapi.ComicvineApiResponse; import com.adityachandel.booklore.model.enums.MetadataProvider; import com.adityachandel.booklore.service.appsettings.AppSettingService; import com.adityachandel.booklore.util.BookUtils; @@ -23,134 +21,181 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Collections; +import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collector; import java.util.stream.Collectors; - @Slf4j @Service @RequiredArgsConstructor public class ComicvineBookParser implements BookParser { - - private final ObjectMapper objectMapper; private static final String COMICVINE_URL = "https://comicvine.gamespot.com/api/"; + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private final ObjectMapper objectMapper; private final AppSettingService appSettingService; - - + private final HttpClient httpClient = HttpClient.newHttpClient(); @Override public List fetchMetadata(Book book, FetchMetadataRequest fetchMetadataRequest) { String searchTerm = getSearchTerm(book, fetchMetadataRequest); - return searchTerm != null ? getMetadataListByTerm(searchTerm) : List.of(); + if (searchTerm == null) { + log.warn("No valid search term provided for metadata fetch."); + return Collections.emptyList(); + } + return getMetadataListByTerm(searchTerm); + } + + @Override + public BookMetadata fetchTopMetadata(Book book, FetchMetadataRequest fetchMetadataRequest) { + List metadataList = fetchMetadata(book, fetchMetadataRequest); + return metadataList.isEmpty() ? null : metadataList.getFirst(); } public List getMetadataListByTerm(String term) { - String apiToken = appSettingService.getAppSettings().getMetadataProviderSettings().getComicvine().getApiKey(); - if (apiToken == null || apiToken.isEmpty()) { - log.warn("Comicvine API token not set"); - return Collections.emptyList(); - } - log.info("Comicvine: Fetching metadata for: {}", term); + String apiToken = getApiToken(); + if (apiToken == null) return Collections.emptyList(); + + log.info("Comicvine: Fetching metadata for term: '{}'", term); try { - - String fieldsList = "cover_date,id,issue_number,name,person_credits,volume,api_detail_url,concept_credits,start_year,publisher,description,image"; + String fieldsList = String.join(",", "api_detail_url", "cover_date", "description", "id", "image", "issue_number", "name", "publisher", "volume"); String resources = "volume,issue"; - URI uri = UriComponentsBuilder.fromUriString(COMICVINE_URL) // Base URL - + URI uri = UriComponentsBuilder.fromUriString(COMICVINE_URL) .path("/search/") - .queryParam("api_key", apiToken) + .queryParam("api_key", apiToken) .queryParam("format", "json") .queryParam("resources", resources) - .queryParam("resource_type", resources) .queryParam("query", term) - .queryParam("filter", "name:" + term) - .queryParam("limit", "10") // Limit results to reduce response size + .queryParam("limit", 10) .queryParam("field_list", fieldsList) .build() .toUri(); - log.debug("Comicvine API request URI: {}", uri); - HttpClient httpClient = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder() .uri(uri) - .header("User-Agent", "MyComicApp/1.0") + .header("User-Agent", "Booklore/1.0") .GET() .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() == 200) { return parseComicvineApiResponse(response.body()); } else { - log.error("Failed to fetch data from Comicvine API. Status code: {}", response.statusCode()); - return List.of(); + log.error("Comicvine Search API returned status code {}", response.statusCode()); } } catch (IOException | InterruptedException e) { - log.error("Error fetching metadata from Comicvine API", e); - return List.of(); + log.error("Error fetching metadata from Comicvine Search API", e); } + return Collections.emptyList(); } - - - @Override - public BookMetadata fetchTopMetadata(Book book, FetchMetadataRequest fetchMetadataRequest) { - List fetchedBookMetadata = fetchMetadata(book, fetchMetadataRequest); - return fetchedBookMetadata.isEmpty() ? null : fetchedBookMetadata.get(0); - } - - private String getSearchTerm(Book book, FetchMetadataRequest request) { - return (request.getTitle() != null && !request.getTitle().isEmpty()) - ? request.getTitle() - : (book.getFileName() != null && !book.getFileName().isEmpty() - ? BookUtils.cleanFileName(book.getFileName()) - : null); + private String getSearchTerm(Book book, FetchMetadataRequest request) { + if (request.getTitle() != null && !request.getTitle().isEmpty()) { + return request.getTitle(); + } else if (book.getFileName() != null && !book.getFileName().isEmpty()) { + return BookUtils.cleanFileName(book.getFileName()); + } + return null; } private List parseComicvineApiResponse(String responseBody) throws IOException { ComicvineApiResponse apiResponse = objectMapper.readValue(responseBody, ComicvineApiResponse.class); + if (apiResponse.getResults() == null) { + return Collections.emptyList(); + } return apiResponse.getResults().stream() .map(this::convertToBookMetadata) .collect(Collectors.toList()); } - - - - private BookMetadata convertToBookMetadata(Comic comic) { - BookMetadata.BookMetadataBuilder builder = BookMetadata.builder() - .title(comic.getDisplayName()) - .comicvineId(String.valueOf(comic.getId())) - .authors(comic.getAuthors()) - .thumbnailUrl(comic.getImageUrl()) - .description(comic.getDescription()) - .provider(MetadataProvider.Comicvine); + return BookMetadata.builder() + .provider(MetadataProvider.Comicvine) + .comicvineId(String.valueOf(comic.getId())) + .title(comic.getName()) + .authors(new HashSet<>()) + .thumbnailUrl(comic.getImage() != null ? comic.getImage().getMediumUrl() : null) + .description(comic.getDescription()) + .seriesName(comic.getVolume() != null ? comic.getVolume().getName() : null) + .seriesNumber(safeParseFloat(comic.getIssueNumber())) + .publishedDate(safeParseDate(comic.getCoverDate())) + .build(); + } - // Handle publishedDate based on the comic type - if (comic instanceof Volume) { - Volume volume = (Volume) comic; - builder.publisher(volume.getPublisherName()); - builder.publishedDate(volume.getDate()); - } else if (comic instanceof Issue) { - Issue issue = (Issue) comic; - builder.seriesName(issue.getVolume().getName()); - builder.seriesNumber((float) issue.getIssueNumber()); - builder.publishedDate(issue.getDate()); // Already parsed + private static LocalDate safeParseDate(String dateStr) { + if (dateStr == null || dateStr.isEmpty()) return null; + try { + return LocalDate.parse(dateStr, DATE_FORMATTER); + } catch (DateTimeParseException e) { + log.warn("Invalid date '{}'", dateStr); + return null; } + } - // Return the final built object - return builder.build(); + private static Float safeParseFloat(String numStr) { + if (numStr == null || numStr.isEmpty()) return null; + try { + return Float.valueOf(numStr); + } catch (NumberFormatException e) { + return null; + } + } + private String getApiToken() { + String apiToken = appSettingService.getAppSettings().getMetadataProviderSettings().getComicvine().getApiKey(); + if (apiToken == null || apiToken.isEmpty()) { + log.warn("Comicvine API token not set"); + return null; + } + return apiToken; + } + /*public Set fetchAuthors(int issueId) { + String apiToken = getApiToken(); + if (apiToken == null) return Collections.emptySet(); + try { + String fieldsList = String.join(",", "person_credits"); -} -} + URI uri = UriComponentsBuilder.fromUriString(COMICVINE_URL) + .path("/issue/4000-" + issueId + "/") + .queryParam("api_key", apiToken) + .queryParam("format", "json") + .queryParam("field_list", fieldsList) + .build() + .toUri(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(uri) + .header("User-Agent", "Booklore/1.0") + .GET() + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 200) { + ComicvineIssueResponse issueResponse = objectMapper.readValue(response.body(), ComicvineIssueResponse.class); + if (issueResponse.getResults() == null || issueResponse.getResults().getPersonCredits() == null) { + log.warn("No person credits found for issue ID {}", issueId); + return Collections.emptySet(); + } + + return issueResponse.getResults().getPersonCredits().stream() + .filter(pc -> pc.getRole() != null && pc.getRole().toLowerCase().contains("writer")) + .map(ComicvineIssueResponse.PersonCredit::getName) + .collect(Collectors.toSet()); + + } else { + log.error("Comicvine Issue API returned status code {}", response.statusCode()); + } + } catch (IOException | InterruptedException e) { + log.error("Error fetching issue metadata from Comicvine Issue API", e); + } + return Collections.emptySet(); + }*/ +} \ No newline at end of file diff --git a/booklore-ui/src/app/bookdrop/bookdrop-file-metadata-picker-component/bookdrop-file-metadata-picker.component.ts b/booklore-ui/src/app/bookdrop/bookdrop-file-metadata-picker-component/bookdrop-file-metadata-picker.component.ts index ce25d2d1b..56cd642a3 100644 --- a/booklore-ui/src/app/bookdrop/bookdrop-file-metadata-picker-component/bookdrop-file-metadata-picker.component.ts +++ b/booklore-ui/src/app/bookdrop/bookdrop-file-metadata-picker-component/bookdrop-file-metadata-picker.component.ts @@ -68,7 +68,7 @@ export class BookdropFileMetadataPickerComponent { {label: 'HC Reviews', controlName: 'hardcoverReviewCount', lockedKey: 'hardcoverReviewCountLocked', fetchedKey: 'hardcoverReviewCount'}, {label: 'HC Rating', controlName: 'hardcoverRating', lockedKey: 'hardcoverRatingLocked', fetchedKey: 'hardcoverRating'}, {label: 'Google ID', controlName: 'googleId', lockedKey: 'googleIdLocked', fetchedKey: 'googleIdRating'}, - {label: 'CV ID', controlName: 'comicvineId', lockedKey: 'comicvineIdLocked', fetchedKey: 'comicvineId'}, + {label: 'Comicvine ID', controlName: 'comicvineId', lockedKey: 'comicvineIdLocked', fetchedKey: 'comicvineId'}, {label: 'Pages', controlName: 'pageCount', lockedKey: 'pageCountLocked', fetchedKey: 'pageCount'} ]; diff --git a/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-picker/metadata-picker.component.ts b/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-picker/metadata-picker.component.ts index 3e8b1114d..82743ccd3 100644 --- a/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-picker/metadata-picker.component.ts +++ b/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-picker/metadata-picker.component.ts @@ -61,11 +61,11 @@ export class MetadataPickerComponent implements OnInit { {label: 'ASIN', controlName: 'asin', lockedKey: 'asinLocked', fetchedKey: 'asin'}, {label: 'Amz Reviews', controlName: 'amazonReviewCount', lockedKey: 'amazonReviewCountLocked', fetchedKey: 'amazonReviewCount'}, {label: 'Amz Rating', controlName: 'amazonRating', lockedKey: 'amazonRatingLocked', fetchedKey: 'amazonRating'}, - {label: 'GR ID', controlName: 'goodreadsId', lockedKey: 'goodreadsIdLocked', fetchedKey: 'goodreadsId'}, - {label: 'CV ID', controlName: 'comicvineId', lockedKey: 'comicvineIdLocked', fetchedKey: 'comicvineId'}, + {label: 'Comicvine ID', controlName: 'comicvineId', lockedKey: 'comicvineIdLocked', fetchedKey: 'comicvineId'}, + {label: 'Goodreads ID', controlName: 'goodreadsId', lockedKey: 'goodreadsIdLocked', fetchedKey: 'goodreadsId'}, {label: 'GR Reviews', controlName: 'goodreadsReviewCount', lockedKey: 'goodreadsReviewCountLocked', fetchedKey: 'goodreadsReviewCount'}, {label: 'GR Rating', controlName: 'goodreadsRating', lockedKey: 'goodreadsRatingLocked', fetchedKey: 'goodreadsRating'}, - {label: 'HC ID', controlName: 'hardcoverId', lockedKey: 'hardcoverIdLocked', fetchedKey: 'hardcoverId'}, + {label: 'Hardcover ID', controlName: 'hardcoverId', lockedKey: 'hardcoverIdLocked', fetchedKey: 'hardcoverId'}, {label: 'HC Reviews', controlName: 'hardcoverReviewCount', lockedKey: 'hardcoverReviewCountLocked', fetchedKey: 'hardcoverReviewCount'}, {label: 'HC Rating', controlName: 'hardcoverRating', lockedKey: 'hardcoverRatingLocked', fetchedKey: 'hardcoverRating'}, {label: 'Google ID', controlName: 'googleId', lockedKey: 'googleIdLocked', fetchedKey: 'googleIdRating'}, diff --git a/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-searcher/metadata-searcher.component.html b/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-searcher/metadata-searcher.component.html index 31d30ed31..9f135042b 100644 --- a/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-searcher/metadata-searcher.component.html +++ b/booklore-ui/src/app/metadata/book-metadata-center-component/metadata-searcher/metadata-searcher.component.html @@ -80,14 +80,18 @@

{{ truncateText(metadata['title']!, 90) }}

-

ISBN: {{ metadata['isbn10'] || metadata['asin'] }}

- + @if (metadata['isbn10'] || metadata['asin']) { +

ISBN: {{ metadata['isbn10'] || metadata['asin'] }}

+ + }

Published: {{ metadata['publishedDate'] }}

-

by {{ truncateText(metadata['authors']!.join(', '), 70) }}

- + @if (metadata['authors'] && metadata['authors'].length > 0) { +

by {{ truncateText(metadata['authors'].join(', '), 70) }}

+ + }

Source: