import filter work and cleanup

This commit is contained in:
Bryan Van Deusen
2026-01-18 21:57:08 -05:00
parent b3331bad76
commit 482c5a8ead
6 changed files with 725 additions and 146 deletions
+146 -3
View File
@@ -244,24 +244,34 @@ def search_tags():
Query params:
- q: search query (matches tag name, case-insensitive)
- limit: max results (default 10)
- exclude_kind: comma-separated list of tag kinds to exclude (e.g., 'archive')
"""
query = request.args.get('q', '').strip().lower()
limit = request.args.get('limit', 10, type=int)
exclude_kind = request.args.get('exclude_kind', '').strip()
exclude_kinds = [k.strip() for k in exclude_kind.split(',') if k.strip()]
if not query:
# Return most-used tags when no query
tags = (
base_query = (
Tag.query
.join(image_tags)
.group_by(Tag.id)
)
if exclude_kinds:
base_query = base_query.filter(~Tag.kind.in_(exclude_kinds))
tags = (
base_query
.order_by(func.count(image_tags.c.image_id).desc())
.limit(limit)
.all()
)
else:
base_query = Tag.query.filter(Tag.name.ilike(f'%{query}%'))
if exclude_kinds:
base_query = base_query.filter(~Tag.kind.in_(exclude_kinds))
tags = (
Tag.query
.filter(Tag.name.ilike(f'%{query}%'))
base_query
.order_by(Tag.name)
.limit(limit)
.all()
@@ -571,3 +581,136 @@ def get_import_stats():
return jsonify(ok=True, stats=stats)
except Exception as e:
return jsonify(ok=False, error=str(e)), 500
# ----------------------------
# Image filter/cleanup endpoints
# ----------------------------
@main.get("/api/filter-scan")
def scan_filterable_images():
"""
Scan existing images against current filter settings.
Returns counts and IDs of images that would be filtered.
Query params:
- filter_type: 'transparency' or 'dimensions'
- threshold: for transparency (0.0-1.0), or min_width/min_height for dimensions
- min_width: minimum width (for dimensions filter)
- min_height: minimum height (for dimensions filter)
- limit: max results to return (default 100)
"""
from app.utils.image_importer import is_mostly_transparent
filter_type = request.args.get("filter_type", "transparency")
limit = request.args.get("limit", 100, type=int)
results = []
if filter_type == "transparency":
threshold = request.args.get("threshold", 0.9, type=float)
# Only check images that could have transparency
images = ImageRecord.query.filter(
ImageRecord.filepath.ilike("%.png") |
ImageRecord.filepath.ilike("%.gif") |
ImageRecord.filepath.ilike("%.webp")
).limit(limit * 5).all() # Check more to find enough matches
for img in images:
if len(results) >= limit:
break
if img.filepath and os.path.exists(img.filepath):
try:
if is_mostly_transparent(img.filepath, threshold):
results.append({
"id": img.id,
"filename": img.filename,
"filepath": img.filepath,
"thumb_url": url_for('main.serve_image',
filename=(img.thumb_path or img.filepath).replace('/images/', ''))
})
except Exception:
pass
elif filter_type == "dimensions":
min_width = request.args.get("min_width", 0, type=int)
min_height = request.args.get("min_height", 0, type=int)
if min_width <= 0 and min_height <= 0:
return jsonify(ok=True, count=0, images=[], message="No dimension filter set")
query = ImageRecord.query
if min_width > 0:
query = query.filter(ImageRecord.width < min_width)
if min_height > 0:
query = query.filter(ImageRecord.height < min_height)
images = query.limit(limit).all()
for img in images:
results.append({
"id": img.id,
"filename": img.filename,
"filepath": img.filepath,
"width": img.width,
"height": img.height,
"thumb_url": url_for('main.serve_image',
filename=(img.thumb_path or img.filepath).replace('/images/', ''))
})
# Get total count (without limit) for progress indication
total_count = len(results)
return jsonify(ok=True, count=total_count, images=results, filter_type=filter_type)
@main.post("/api/filter-delete")
def delete_filtered_images():
"""
Delete images by their IDs after filter review.
Query params:
- image_ids: comma-separated list of image IDs to delete
- confirm: must be 'true' to actually delete
"""
image_ids_str = request.form.get("image_ids", "")
confirm = request.form.get("confirm") == "true"
if not confirm:
return jsonify(ok=False, error="Confirmation required"), 400
if not image_ids_str:
return jsonify(ok=False, error="No image IDs provided"), 400
try:
image_ids = [int(x) for x in image_ids_str.split(",") if x.strip()]
except ValueError:
return jsonify(ok=False, error="Invalid image IDs"), 400
deleted_count = 0
errors = []
for img_id in image_ids:
img = ImageRecord.query.get(img_id)
if not img:
continue
try:
# Remove image file from disk
if img.filepath and os.path.exists(img.filepath):
os.remove(img.filepath)
# Remove thumbnail if exists
if img.thumb_path and os.path.exists(img.thumb_path):
os.remove(img.thumb_path)
# Delete DB record
db.session.delete(img)
deleted_count += 1
except Exception as e:
errors.append(f"Failed to delete {img.filename}: {e}")
db.session.commit()
return jsonify(
ok=True,
deleted_count=deleted_count,
errors=errors if errors else None
)