sorting
This commit is contained in:
parent
30d6453c6b
commit
f0ad4d0d35
80
tools.py
80
tools.py
@ -61,6 +61,7 @@ async def list_thumbnails_handler(args: Dict[str, Any]):
|
|||||||
dir_path = args.get("path")
|
dir_path = args.get("path")
|
||||||
page = int(args.get("page", 1))
|
page = int(args.get("page", 1))
|
||||||
page_size = int(args.get("page_size", 64))
|
page_size = int(args.get("page_size", 64))
|
||||||
|
sort_by = args.get("sort_by", "mtime")
|
||||||
|
|
||||||
if not dir_path:
|
if not dir_path:
|
||||||
return {"text": "Error: Missing path argument"}
|
return {"text": "Error: Missing path argument"}
|
||||||
@ -68,24 +69,47 @@ async def list_thumbnails_handler(args: Dict[str, Any]):
|
|||||||
return {"text": f"Error: {dir_path} is not a directory."}
|
return {"text": f"Error: {dir_path} is not a directory."}
|
||||||
|
|
||||||
exts = ('.png', '.jpg', '.jpeg', '.webp', '.bmp')
|
exts = ('.png', '.jpg', '.jpeg', '.webp', '.bmp')
|
||||||
all_files = sorted([f for f in os.listdir(dir_path) if f.lower().endswith(exts)])
|
|
||||||
|
|
||||||
if not all_files:
|
# Collect file info for sorting
|
||||||
|
file_info_list = []
|
||||||
|
for f in os.listdir(dir_path):
|
||||||
|
if f.lower().endswith(exts):
|
||||||
|
full_path = os.path.join(dir_path, f)
|
||||||
|
try:
|
||||||
|
stats = os.stat(full_path)
|
||||||
|
file_info_list.append({
|
||||||
|
"name": f,
|
||||||
|
"path": full_path,
|
||||||
|
"mtime": stats.st_mtime,
|
||||||
|
"size": stats.st_size
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Could not stat {f}: {e}")
|
||||||
|
|
||||||
|
if not file_info_list:
|
||||||
return {"text": "No supported images found in the directory."}
|
return {"text": "No supported images found in the directory."}
|
||||||
|
|
||||||
total_files = len(all_files)
|
# Sorting logic
|
||||||
|
if sort_by == "mtime":
|
||||||
|
file_info_list.sort(key=lambda x: x["mtime"], reverse=True)
|
||||||
|
elif sort_by == "size":
|
||||||
|
file_info_list.sort(key=lambda x: x["size"], reverse=True)
|
||||||
|
else: # Default to name
|
||||||
|
file_info_list.sort(key=lambda x: x["name"].lower())
|
||||||
|
|
||||||
|
total_files = len(file_info_list)
|
||||||
start_idx = (page - 1) * page_size
|
start_idx = (page - 1) * page_size
|
||||||
end_idx = start_idx + page_size
|
end_idx = start_idx + page_size
|
||||||
files = all_files[start_idx:end_idx]
|
paged_files = file_info_list[start_idx:end_idx]
|
||||||
|
|
||||||
if not files:
|
if not paged_files:
|
||||||
return {"text": f"No images found on page {page}."}
|
return {"text": f"No images found on page {page}."}
|
||||||
|
|
||||||
thumb_size = 160
|
thumb_size = 160
|
||||||
padding = 15
|
padding = 15
|
||||||
label_height = 30
|
label_height = 35 # Increased for larger font
|
||||||
|
|
||||||
num_files = len(files)
|
num_files = len(paged_files)
|
||||||
cols = int(num_files**0.5) if num_files > 0 else 1
|
cols = int(num_files**0.5) if num_files > 0 else 1
|
||||||
if cols == 0: cols = 1
|
if cols == 0: cols = 1
|
||||||
rows = (num_files + cols - 1) // cols
|
rows = (num_files + cols - 1) // cols
|
||||||
@ -96,38 +120,53 @@ async def list_thumbnails_handler(args: Dict[str, Any]):
|
|||||||
canvas = Image.new('RGB', (canvas_w, canvas_h), (30, 30, 30))
|
canvas = Image.new('RGB', (canvas_w, canvas_h), (30, 30, 30))
|
||||||
draw = ImageDraw.Draw(canvas)
|
draw = ImageDraw.Draw(canvas)
|
||||||
|
|
||||||
try:
|
# Attempt to load a larger font
|
||||||
font = ImageFont.load_default()
|
|
||||||
except Exception:
|
|
||||||
font = None
|
font = None
|
||||||
|
font_paths = [
|
||||||
|
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
||||||
|
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
|
||||||
|
"/usr/share/fonts/truetype/freefont/FreeSansBold.ttf",
|
||||||
|
]
|
||||||
|
for path in font_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype(path, 20)
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if font is None:
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
|
||||||
file_details = []
|
file_details = []
|
||||||
|
|
||||||
for i, filename in enumerate(files):
|
for i, info in enumerate(paged_files):
|
||||||
full_path = os.path.join(dir_path, filename)
|
filename = info["name"]
|
||||||
|
full_path = info["path"]
|
||||||
row = i // cols
|
row = i // cols
|
||||||
col = i % cols
|
col = i % cols
|
||||||
|
|
||||||
x = padding + col * (thumb_size + padding)
|
x = padding + col * (thumb_size + padding)
|
||||||
y = padding + row * (thumb_size + label_height + padding)
|
y = padding + row * (thumb_size + label_height + padding)
|
||||||
|
|
||||||
# Gather metadata for the text list
|
|
||||||
try:
|
try:
|
||||||
stats = os.stat(full_path)
|
|
||||||
with Image.open(full_path) as img:
|
with Image.open(full_path) as img:
|
||||||
width, height = img.size
|
width, height = img.size
|
||||||
# Process thumbnail
|
|
||||||
img.thumbnail((thumb_size, thumb_size))
|
img.thumbnail((thumb_size, thumb_size))
|
||||||
off_x = (thumb_size - img.width) // 2
|
off_x = (thumb_size - img.width) // 2
|
||||||
off_y = (thumb_size - img.height) // 2
|
off_y = (thumb_size - img.height) // 2
|
||||||
canvas.paste(img, (x + off_x, y + off_y))
|
canvas.paste(img, (x + off_x, y + off_y))
|
||||||
|
|
||||||
mod_time = datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M')
|
mod_time = datetime.datetime.fromtimestamp(info["mtime"]).strftime('%Y-%m-%d %H:%M')
|
||||||
size_kb = stats.st_size / 1024
|
size_kb = info["size"] / 1024
|
||||||
file_details.append(f"{i+1}. {filename} | {width}x{height} | {size_kb:.1f}KB | {mod_time}")
|
file_details.append(f"{i+1}. {filename} | {width}x{height} | {size_kb:.1f}KB | {mod_time}")
|
||||||
|
|
||||||
# Label the thumbnail with just the index number
|
# Draw index number - add a small background rectangle for contrast
|
||||||
draw.text((x, y + thumb_size + 2), str(i + 1), fill=(220, 220, 220), font=font)
|
text = str(i + 1)
|
||||||
|
# Simple text bounding box calculation (approximate)
|
||||||
|
text_w = len(text) * 10
|
||||||
|
draw.rectangle([x, y + thumb_size + 2, x + text_w + 4, y + thumb_size + 24], fill=(60, 60, 60))
|
||||||
|
draw.text((x + 2, y + thumb_size + 2), text, fill=(255, 255, 255), font=font)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to process {filename}: {e}")
|
logger.error(f"Failed to process {filename}: {e}")
|
||||||
draw.text((x, y + thumb_size // 2), "Error", fill=(255, 0, 0), font=font)
|
draw.text((x, y + thumb_size // 2), "Error", fill=(255, 0, 0), font=font)
|
||||||
@ -170,13 +209,14 @@ TOOL_REGISTRY = [
|
|||||||
),
|
),
|
||||||
Tool(
|
Tool(
|
||||||
name="list_thumbnails",
|
name="list_thumbnails",
|
||||||
description="Generates a thumbnail grid of images in a directory. Returns a numbered grid and a detailed file list.",
|
description="Generates a thumbnail grid of images in a directory. Supports pagination and sorting.",
|
||||||
schema={
|
schema={
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"path": {"type": "string", "description": "Path to the directory containing images"},
|
"path": {"type": "string", "description": "Path to the directory containing images"},
|
||||||
"page": {"type": "integer", "description": "The page number to retrieve (1-indexed)", "default": 1},
|
"page": {"type": "integer", "description": "The page number to retrieve (1-indexed)", "default": 1},
|
||||||
"page_size": {"type": "integer", "description": "Number of images per page", "default": 64},
|
"page_size": {"type": "integer", "description": "Number of images per page", "default": 64},
|
||||||
|
"sort_by": {"type": "string", "description": "Sort order: 'mtime' (newest first, default), 'name' (alphabetical), or 'size' (largest first)", "default": "mtime"},
|
||||||
},
|
},
|
||||||
"required": ["path"],
|
"required": ["path"],
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user