From df5e1241156fb51aaab90a54250ae3e1691b0a7e Mon Sep 17 00:00:00 2001 From: Stefan Hardegger Date: Sat, 27 Sep 2025 09:15:01 +0200 Subject: [PATCH] fix image processing --- .../com/storycove/service/StoryService.java | 96 +++++++++++++++++-- .../storycove/service/StoryServiceTest.java | 3 +- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/com/storycove/service/StoryService.java b/backend/src/main/java/com/storycove/service/StoryService.java index 9e9c4a4..4a59690 100644 --- a/backend/src/main/java/com/storycove/service/StoryService.java +++ b/backend/src/main/java/com/storycove/service/StoryService.java @@ -43,6 +43,7 @@ public class StoryService { private final SeriesService seriesService; private final HtmlSanitizationService sanitizationService; private final SearchServiceAdapter searchServiceAdapter; + private final ImageService imageService; @Autowired public StoryService(StoryRepository storyRepository, @@ -52,7 +53,8 @@ public class StoryService { TagService tagService, SeriesService seriesService, HtmlSanitizationService sanitizationService, - SearchServiceAdapter searchServiceAdapter) { + SearchServiceAdapter searchServiceAdapter, + ImageService imageService) { this.storyRepository = storyRepository; this.tagRepository = tagRepository; this.readingPositionRepository = readingPositionRepository; @@ -61,6 +63,7 @@ public class StoryService { this.seriesService = seriesService; this.sanitizationService = sanitizationService; this.searchServiceAdapter = searchServiceAdapter; + this.imageService = imageService; } @Transactional(readOnly = true) @@ -342,12 +345,40 @@ public class StoryService { } Story savedStory = storyRepository.save(story); - + + // Process external images in content (now that we have a story ID) + if (savedStory.getContentHtml() != null && !savedStory.getContentHtml().trim().isEmpty()) { + try { + ImageService.ContentImageProcessingResult imageResult = + imageService.processContentImages(savedStory.getContentHtml(), savedStory.getId()); + + // Update content if images were processed + if (!imageResult.getProcessedContent().equals(savedStory.getContentHtml())) { + savedStory.setContentHtml(imageResult.getProcessedContent()); + savedStory = storyRepository.save(savedStory); + } + + // Log any warnings or downloaded images + if (imageResult.hasWarnings()) { + logger.warn("Image processing warnings for new story {}: {}", + savedStory.getId(), imageResult.getWarnings()); + } + if (!imageResult.getDownloadedImages().isEmpty()) { + logger.info("Downloaded {} external images for new story {}: {}", + imageResult.getDownloadedImages().size(), savedStory.getId(), + imageResult.getDownloadedImages()); + } + } catch (Exception e) { + logger.warn("Failed to process images for new story {}: {}", + savedStory.getId(), e.getMessage()); + } + } + // Handle tags if (story.getTags() != null && !story.getTags().isEmpty()) { updateStoryTags(savedStory, story.getTags()); } - + // Index in search engine searchServiceAdapter.indexStory(savedStory); @@ -370,12 +401,40 @@ public class StoryService { } Story savedStory = storyRepository.save(story); - + + // Process external images in content (now that we have a story ID) + if (savedStory.getContentHtml() != null && !savedStory.getContentHtml().trim().isEmpty()) { + try { + ImageService.ContentImageProcessingResult imageResult = + imageService.processContentImages(savedStory.getContentHtml(), savedStory.getId()); + + // Update content if images were processed + if (!imageResult.getProcessedContent().equals(savedStory.getContentHtml())) { + savedStory.setContentHtml(imageResult.getProcessedContent()); + savedStory = storyRepository.save(savedStory); + } + + // Log any warnings or downloaded images + if (imageResult.hasWarnings()) { + logger.warn("Image processing warnings for new story {}: {}", + savedStory.getId(), imageResult.getWarnings()); + } + if (!imageResult.getDownloadedImages().isEmpty()) { + logger.info("Downloaded {} external images for new story {}: {}", + imageResult.getDownloadedImages().size(), savedStory.getId(), + imageResult.getDownloadedImages()); + } + } catch (Exception e) { + logger.warn("Failed to process images for new story {}: {}", + savedStory.getId(), e.getMessage()); + } + } + // Handle tags by names if (tagNames != null && !tagNames.isEmpty()) { updateStoryTagsByNames(savedStory, tagNames); } - + // Index in search engine searchServiceAdapter.indexStory(savedStory); @@ -588,7 +647,32 @@ public class StoryService { story.setSummary(updateReq.getSummary()); } if (updateReq.getContentHtml() != null) { - story.setContentHtml(sanitizationService.sanitize(updateReq.getContentHtml())); + String sanitizedContent = sanitizationService.sanitize(updateReq.getContentHtml()); + + // Process external images in the content + try { + ImageService.ContentImageProcessingResult imageResult = + imageService.processContentImages(sanitizedContent, story.getId()); + + // Use the processed content (with local image URLs) + story.setContentHtml(imageResult.getProcessedContent()); + + // Log any warnings or downloaded images + if (imageResult.hasWarnings()) { + logger.warn("Image processing warnings for story {}: {}", + story.getId(), imageResult.getWarnings()); + } + if (!imageResult.getDownloadedImages().isEmpty()) { + logger.info("Downloaded {} external images for story {}: {}", + imageResult.getDownloadedImages().size(), story.getId(), + imageResult.getDownloadedImages()); + } + } catch (Exception e) { + logger.warn("Failed to process images for story {}, using content without image processing: {}", + story.getId(), e.getMessage()); + // Fallback to sanitized content without image processing + story.setContentHtml(sanitizedContent); + } } if (updateReq.getSourceUrl() != null) { story.setSourceUrl(updateReq.getSourceUrl()); diff --git a/backend/src/test/java/com/storycove/service/StoryServiceTest.java b/backend/src/test/java/com/storycove/service/StoryServiceTest.java index b49996f..377c359 100644 --- a/backend/src/test/java/com/storycove/service/StoryServiceTest.java +++ b/backend/src/test/java/com/storycove/service/StoryServiceTest.java @@ -56,7 +56,8 @@ class StoryServiceTest { null, // tagService - not needed for reading progress tests null, // seriesService - not needed for reading progress tests null, // sanitizationService - not needed for reading progress tests - searchServiceAdapter + searchServiceAdapter, + null // imageService - not needed for reading progress tests ); }