diff --git a/backend/src/main/java/com/storycove/service/ImageService.java b/backend/src/main/java/com/storycove/service/ImageService.java index 294ca6e..435f41f 100644 --- a/backend/src/main/java/com/storycove/service/ImageService.java +++ b/backend/src/main/java/com/storycove/service/ImageService.java @@ -462,6 +462,7 @@ public class ImageService { Files.walk(contentImagesDir, 2) .filter(Files::isDirectory) .filter(path -> !path.equals(contentImagesDir)) // Skip the root content directory + .filter(path -> !isSynologySystemPath(path)) // Skip Synology system directories .forEach(storyDir -> { try { String storyId = storyDir.getFileName().toString(); @@ -476,6 +477,8 @@ public class ImageService { try { Files.walk(storyDir) .filter(Files::isRegularFile) + .filter(path -> !isSynologySystemPath(path)) // Skip Synology system files + .filter(path -> isValidImageFile(path)) // Only process actual image files .forEach(file -> { try { long size = Files.size(file); @@ -495,13 +498,18 @@ public class ImageService { try { Files.walk(storyDir) .filter(Files::isRegularFile) + .filter(path -> !isSynologySystemPath(path)) // Skip Synology system files + .filter(path -> isValidImageFile(path)) // Only process actual image files .forEach(imageFile -> { try { - String imagePath = getRelativeImagePath(imageFile); + String filename = imageFile.getFileName().toString(); - if (!referencedImages.contains(imagePath)) { - logger.debug("Found orphaned image: {}", imagePath); + // Only consider it orphaned if it's not in our referenced filenames + if (!referencedImages.contains(filename)) { + logger.debug("Found orphaned image: {}", filename); orphanedImages.add(imageFile.toString()); + } else { + logger.debug("Image file is referenced, keeping: {}", filename); } } catch (Exception e) { errors.add("Error checking image file " + imageFile + ": " + e.getMessage()); @@ -583,10 +591,10 @@ public class ImageService { } /** - * Collect all image references from all story content + * Collect all image filenames referenced in content (UUID-based filenames only) */ private Set collectAllImageReferences() { - Set referencedImages = new HashSet<>(); + Set referencedFilenames = new HashSet<>(); try { // Get all stories @@ -596,21 +604,21 @@ public class ImageService { Pattern imagePattern = Pattern.compile("src=[\"']([^\"']*(?:content/[^\"']*\\.(jpg|jpeg|png)))[\"']", Pattern.CASE_INSENSITIVE); for (com.storycove.entity.Story story : allStories) { - // Add story cover image if present + // Add story cover image filename if present if (story.getCoverPath() != null && !story.getCoverPath().trim().isEmpty()) { - String relativePath = convertAbsolutePathToRelative(story.getCoverPath()); - if (relativePath != null) { - referencedImages.add(relativePath); - logger.debug("Found cover image reference in story {}: {}", story.getId(), relativePath); + String filename = extractFilename(story.getCoverPath()); + if (filename != null) { + referencedFilenames.add(filename); + logger.debug("Found cover image filename in story {}: {}", story.getId(), filename); } } - // Add author avatar image if present + // Add author avatar image filename if present if (story.getAuthor() != null && story.getAuthor().getAvatarImagePath() != null && !story.getAuthor().getAvatarImagePath().trim().isEmpty()) { - String relativePath = convertAbsolutePathToRelative(story.getAuthor().getAvatarImagePath()); - if (relativePath != null) { - referencedImages.add(relativePath); - logger.debug("Found avatar image reference for author {}: {}", story.getAuthor().getId(), relativePath); + String filename = extractFilename(story.getAuthor().getAvatarImagePath()); + if (filename != null) { + referencedFilenames.add(filename); + logger.debug("Found avatar image filename for author {}: {}", story.getAuthor().getId(), filename); } } @@ -621,11 +629,11 @@ public class ImageService { while (matcher.find()) { String imageSrc = matcher.group(1); - // Convert to relative path format that matches our file system - String relativePath = convertSrcToRelativePath(imageSrc); - if (relativePath != null) { - referencedImages.add(relativePath); - logger.debug("Found content image reference in story {}: {}", story.getId(), relativePath); + // Extract just the filename from the URL + String filename = extractFilename(imageSrc); + if (filename != null && isUuidBasedFilename(filename)) { + referencedFilenames.add(filename); + logger.debug("Found content image filename in story {}: {}", story.getId(), filename); } } } @@ -635,10 +643,10 @@ public class ImageService { List allAuthors = authorService.findAll(); for (com.storycove.entity.Author author : allAuthors) { if (author.getAvatarImagePath() != null && !author.getAvatarImagePath().trim().isEmpty()) { - String relativePath = convertAbsolutePathToRelative(author.getAvatarImagePath()); - if (relativePath != null) { - referencedImages.add(relativePath); - logger.debug("Found standalone avatar image reference for author {}: {}", author.getId(), relativePath); + String filename = extractFilename(author.getAvatarImagePath()); + if (filename != null) { + referencedFilenames.add(filename); + logger.debug("Found standalone avatar image filename for author {}: {}", author.getId(), filename); } } } @@ -647,10 +655,10 @@ public class ImageService { List allCollections = collectionService.findAllWithTags(); for (com.storycove.entity.Collection collection : allCollections) { if (collection.getCoverImagePath() != null && !collection.getCoverImagePath().trim().isEmpty()) { - String relativePath = convertAbsolutePathToRelative(collection.getCoverImagePath()); - if (relativePath != null) { - referencedImages.add(relativePath); - logger.debug("Found collection cover image reference for collection {}: {}", collection.getId(), relativePath); + String filename = extractFilename(collection.getCoverImagePath()); + if (filename != null) { + referencedFilenames.add(filename); + logger.debug("Found collection cover image filename for collection {}: {}", collection.getId(), filename); } } } @@ -659,7 +667,7 @@ public class ImageService { logger.error("Error collecting image references from stories", e); } - return referencedImages; + return referencedFilenames; } /** @@ -848,4 +856,82 @@ public class ImageService { return String.format("%.1f GB", totalSizeBytes / (1024.0 * 1024.0 * 1024.0)); } } + + /** + * Check if a path is a Synology system path that should be ignored + */ + private boolean isSynologySystemPath(Path path) { + String pathStr = path.toString(); + String fileName = path.getFileName().toString(); + + // Skip Synology metadata directories and files + return pathStr.contains("@eaDir") || + fileName.startsWith("@") || + fileName.contains("@SynoEAStream") || + fileName.startsWith(".") || + fileName.equals("Thumbs.db") || + fileName.equals(".DS_Store"); + } + + /** + * Check if a file is a valid image file (not a system/metadata file) + */ + private boolean isValidImageFile(Path path) { + if (isSynologySystemPath(path)) { + return false; + } + + String fileName = path.getFileName().toString().toLowerCase(); + return fileName.endsWith(".jpg") || + fileName.endsWith(".jpeg") || + fileName.endsWith(".png") || + fileName.endsWith(".gif") || + fileName.endsWith(".webp"); + } + + /** + * Extract filename from a path or URL + */ + private String extractFilename(String pathOrUrl) { + if (pathOrUrl == null || pathOrUrl.trim().isEmpty()) { + return null; + } + + try { + // Remove query parameters if present + if (pathOrUrl.contains("?")) { + pathOrUrl = pathOrUrl.substring(0, pathOrUrl.indexOf("?")); + } + + // Get the last part after slash + String filename = pathOrUrl.substring(pathOrUrl.lastIndexOf("/") + 1); + + // Remove any special Synology suffixes + filename = filename.replace("@SynoEAStream", ""); + + return filename.trim().isEmpty() ? null : filename; + } catch (Exception e) { + logger.debug("Failed to extract filename from: {}", pathOrUrl); + return null; + } + } + + /** + * Check if a filename follows UUID pattern (indicates it's our generated file) + */ + private boolean isUuidBasedFilename(String filename) { + if (filename == null || filename.trim().isEmpty()) { + return false; + } + + // Remove extension + String nameWithoutExt = filename; + int lastDot = filename.lastIndexOf("."); + if (lastDot > 0) { + nameWithoutExt = filename.substring(0, lastDot); + } + + // Check if it matches UUID pattern (8-4-4-4-12 hex characters) + return nameWithoutExt.matches("[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"); + } } \ No newline at end of file