Skip to content

Commit 7d06e37

Browse files
Merge pull request AOSSIE-Org#340 from rahulharpal1603/feature/multiple-folder-support
Feature/multiple folder support (Sr. No. 3)
2 parents 9fc1460 + a08383c commit 7d06e37

35 files changed

+4609
-713
lines changed

backend/app/routes/images.py

+139-63
Original file line numberDiff line numberDiff line change
@@ -251,41 +251,31 @@ def delete_multiple_images(payload: dict):
251251
},
252252
)
253253
path = os.path.normpath(path)
254-
parts = path.split(os.sep)
255-
if "images" in parts:
256-
parts.insert(parts.index("images") + 1, "PictoPy.thumbnails")
257-
thumb_nail_image_path = os.sep.join(parts)
258-
259-
if os.path.exists(thumb_nail_image_path):
260-
print(f"Thumbnail found: {thumb_nail_image_path}")
261-
else:
262-
print(f"Thumbnail not found: {thumb_nail_image_path}")
263-
else:
264-
thumb_nail_image_path = ""
265-
print(f"'images' directory not found in path: {path}")
254+
folder_path, filename = os.path.split(path)
255+
thumbnail_folder = os.path.join(folder_path, "PictoPy.thumbnails")
256+
thumb_nail_image_path = os.path.join(thumbnail_folder, filename)
266257

267-
268-
if os.path.exists(path) :
269-
try :
258+
# Check and remove the original file
259+
if os.path.exists(path):
260+
try:
270261
os.remove(path)
271262
except PermissionError:
272-
print(f"Permission denied for file '{thumb_nail_image_path}'.")
263+
print(f"Permission denied for file '{path}'.")
273264
except Exception as e:
274265
print(f"An error occurred: {e}")
275-
276266
else:
277267
print(f"File '{path}' does not exist.")
278-
279-
if os.path.exists(thumb_nail_image_path) :
280-
try :
268+
269+
# Check and remove the thumbnail file
270+
if os.path.exists(thumb_nail_image_path):
271+
try:
281272
os.remove(thumb_nail_image_path)
282273
except PermissionError:
283274
print(f"Permission denied for file '{thumb_nail_image_path}'.")
284275
except Exception as e:
285276
print(f"An error occurred: {e}")
286-
else :
277+
else:
287278
print(f"File '{thumb_nail_image_path}' does not exist.")
288-
289279

290280
delete_image_db(path)
291281
deleted_paths.append(path)
@@ -489,6 +479,98 @@ async def add_folder(payload: dict):
489479
@router.post("/generate-thumbnails")
490480
@exception_handler_wrapper
491481
def generate_thumbnails(payload: dict):
482+
if "folder_paths" not in payload or not isinstance(payload["folder_paths"], list):
483+
return JSONResponse(
484+
status_code=400,
485+
content={
486+
"status_code": 400,
487+
"content": {
488+
"success": False,
489+
"error": "Invalid or missing 'folder_paths' in payload",
490+
"message": "'folder_paths' must be a list of folder paths",
491+
},
492+
},
493+
)
494+
495+
folder_paths = payload["folder_paths"]
496+
image_extensions = [".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp"]
497+
failed_paths = []
498+
499+
for folder_path in folder_paths:
500+
if not os.path.isdir(folder_path):
501+
failed_paths.append(
502+
{
503+
"folder_path": folder_path,
504+
"error": "Invalid folder path",
505+
"message": "The provided path is not a valid directory",
506+
}
507+
)
508+
continue
509+
510+
for root, _, files in os.walk(folder_path):
511+
# Do not generate thumbnails for the "PictoPy.thumbnails" folder
512+
if "PictoPy.thumbnails" in root:
513+
continue
514+
515+
# Create the "PictoPy.thumbnails" folder in the current directory (`root`)
516+
thumbnail_folder = os.path.join(root, "PictoPy.thumbnails")
517+
os.makedirs(thumbnail_folder, exist_ok=True)
518+
519+
for file in files:
520+
file_path = os.path.join(root, file)
521+
file_extension = os.path.splitext(file_path)[1].lower()
522+
if file_extension in image_extensions:
523+
try:
524+
# Create a unique thumbnail name based on the file name
525+
thumbnail_name = file
526+
thumbnail_path = os.path.join(thumbnail_folder, thumbnail_name)
527+
528+
# Skip if the thumbnail already exists
529+
if os.path.exists(thumbnail_path):
530+
continue
531+
532+
# Generate the thumbnail
533+
img = Image.open(file_path)
534+
img.thumbnail((400, 400))
535+
img.save(thumbnail_path)
536+
except Exception as e:
537+
failed_paths.append(
538+
{
539+
"folder_path": folder_path,
540+
"file": file_path,
541+
"error": "Thumbnail generation error",
542+
"message": f"Error processing file {file}: {str(e)}",
543+
}
544+
)
545+
546+
if failed_paths:
547+
return JSONResponse(
548+
status_code=207, # Multi-Status (some succeeded, some failed)
549+
content={
550+
"status_code": 207,
551+
"content": {
552+
"success": False,
553+
"error": "Partial processing",
554+
"message": "Some folders or files could not be processed",
555+
"failed_paths": failed_paths,
556+
},
557+
},
558+
)
559+
560+
return JSONResponse(
561+
status_code=201,
562+
content={
563+
"data": "",
564+
"message": "Thumbnails generated successfully for all valid folders",
565+
"success": True,
566+
},
567+
)
568+
569+
570+
# Delete all the thumbnails present in the given folder
571+
@router.delete("/delete-thumbnails")
572+
@exception_handler_wrapper
573+
def delete_thumbnails(payload: dict):
492574
if "folder_path" not in payload:
493575
return JSONResponse(
494576
status_code=400,
@@ -516,53 +598,47 @@ def generate_thumbnails(payload: dict):
516598
},
517599
)
518600

519-
thumbnail_folder = os.path.join(folder_path, "PictoPy.thumbnails")
520-
os.makedirs(thumbnail_folder, exist_ok=True)
601+
# List to store any errors encountered while deleting thumbnails
602+
failed_deletions = []
521603

522-
image_extensions = [".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp"]
523-
for root, _, files in os.walk(folder_path):
524-
if "PictoPy.thumbnails" in root:
525-
continue
526-
for file in files:
527-
file_path = os.path.join(root, file)
528-
file_extension = os.path.splitext(file_path)[1].lower()
529-
if file_extension in image_extensions:
604+
# Walk through the folder path and find all `PictoPy.thumbnails` folders
605+
for root, dirs, _ in os.walk(folder_path):
606+
for dir_name in dirs:
607+
if dir_name == "PictoPy.thumbnails":
608+
thumbnail_folder = os.path.join(root, dir_name)
530609
try:
531-
# Create a unique name based on the relative folder structure
532-
relative_path = os.path.relpath(root, folder_path)
533-
sanitized_relative_path = relative_path.replace(
534-
os.sep, "."
535-
) # Replace path separators
536-
thumbnail_name = (
537-
f"{sanitized_relative_path}.{file}"
538-
if relative_path != "."
539-
else file
540-
)
541-
thumbnail_path = os.path.join(thumbnail_folder, thumbnail_name)
542-
if os.path.exists(thumbnail_path):
543-
# print(f"Thumbnail {thumbnail_name} already exists. Skipping.")
544-
continue
545-
img = Image.open(file_path)
546-
img.thumbnail((400, 400))
547-
img.save(thumbnail_path)
610+
# Delete the thumbnail folder
611+
shutil.rmtree(thumbnail_folder)
612+
print(f"Deleted: {thumbnail_folder}")
548613
except Exception as e:
549-
return JSONResponse(
550-
status_code=500,
551-
content={
552-
"status_code": 500,
553-
"content": {
554-
"success": False,
555-
"error": "Internal server error",
556-
"message": f"Error processing file {file}: {str(e)}",
557-
},
558-
},
614+
failed_deletions.append(
615+
{
616+
"folder": thumbnail_folder,
617+
"error": str(e),
618+
}
559619
)
560620

621+
if failed_deletions:
622+
return JSONResponse(
623+
status_code=500,
624+
content={
625+
"status_code": 500,
626+
"content": {
627+
"success": False,
628+
"error": "Some thumbnail folders could not be deleted",
629+
"message": "See the `failed_deletions` field for details.",
630+
"failed_deletions": failed_deletions,
631+
},
632+
},
633+
)
634+
561635
return JSONResponse(
562-
status_code=201,
636+
status_code=200,
563637
content={
564-
"data": "",
565-
"message": "Thumbnails generated successfully",
566-
"success": True,
638+
"status_code": 200,
639+
"content": {
640+
"success": True,
641+
"message": "All PictoPy.thumbnails folders have been successfully deleted.",
642+
},
567643
},
568644
)

frontend/api/api-functions/images.ts

+14-4
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ const parseAndSortImageData = (data: APIResponse['data']): Image[] => {
3131
const parsedImages: Image[] = Object.entries(data.images).map(
3232
([src, tags]) => {
3333
const url = convertFileSrc(src);
34-
const thumbnailUrl = convertFileSrc(
35-
extractThumbnailPath(data.folder_path, src),
36-
);
34+
const thumbnailUrl = convertFileSrc(extractThumbnailPath(src));
3735
return {
3836
imagePath: src,
3937
title: src.substring(src.lastIndexOf('\\') + 1),
@@ -86,12 +84,24 @@ export const addMultImages = async (paths: string[]) => {
8684
return data;
8785
};
8886

89-
export const generateThumbnails = async (folderPath: string) => {
87+
export const generateThumbnails = async (folderPath: string[]) => {
9088
const response = await fetch(imagesEndpoints.generateThumbnails, {
9189
method: 'POST',
9290
headers: {
9391
'Content-Type': 'application/json',
9492
},
93+
body: JSON.stringify({ folder_paths: folderPath }),
94+
});
95+
const data = await response.json();
96+
return data;
97+
};
98+
99+
export const deleteThumbnails = async (folderPath: string) => {
100+
const response = await fetch(imagesEndpoints.deleteThumbnails, {
101+
method: 'DELETE',
102+
headers: {
103+
'Content-Type': 'application/json',
104+
},
95105
body: JSON.stringify({ folder_path: folderPath }),
96106
});
97107
const data = await response.json();

frontend/api/apiEndpoints.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const imagesEndpoints = {
77
addFolder: `${BACKED_URL}/images/add-folder`,
88
addMultipleImages: `${BACKED_URL}/images/multiple-images`,
99
generateThumbnails: `${BACKED_URL}/images/generate-thumbnails`,
10+
deleteThumbnails: `${BACKED_URL}/images/delete-thumbnails`,
1011
};
1112

1213
export const albumEndpoints = {

0 commit comments

Comments
 (0)