fix orphaned file discovery
This commit is contained in:
@@ -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<String> collectAllImageReferences() {
|
||||
Set<String> referencedImages = new HashSet<>();
|
||||
Set<String> 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<com.storycove.entity.Author> 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<com.storycove.entity.Collection> 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}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user