How to control Sort Moments from Python.
Guided recipes for one-off runs, reusable organizers, output/session control, progress callbacks, staged detection, and safer error handling.
These docs describe the intended package surface. Do not treat the PyPI install path as live until the package, wheel metadata, and import surface are verified. For now, use the CLI from source.
Quickstart
Use organize_photos() when you want defaults close to the desktop app and do not need to reuse configuration.
from sortmoments import organize_photos
result = organize_photos("D:/photos/wedding")
print("Output:", result.output_folder)
print("People:", result.person_count)
print("Faces:", result.face_count)The output defaults to <input>/all_images_processed. Each person folder starts as rename_0, rename_1, etc.
Configured organizer
Use SortMomentsOrganizer when you want one configured object for multiple folders, callbacks, model overrides, or testable dependency injection.
from sortmoments import SortMomentsConfig, SortMomentsOrganizer
config = SortMomentsConfig(
min_face_size=96,
min_confidence=0.86,
similarity_threshold=0.60,
batch_size=6,
max_workers=3,
prefer_gpu=True,
)
organizer = SortMomentsOrganizer(config)See all fields in SortMomentsConfig.
Output and session control
You can keep source photos untouched, choose a final output directory, and isolate repeated runs under a session id.
organizer = SortMomentsOrganizer(SortMomentsConfig(
final_output_folder="E:/sorted/family",
session_id="threshold-060",
keep_intermediate=True,
))
result = organizer.organize("E:/incoming/family")- final_output_folder sets the exact final destination.
- session_id lets you compare runs without overwriting a previous result.
- keep_intermediate keeps face crops and embeddings for inspection or regrouping.
Progress callbacks
Callbacks receive current, total, and a message. Use this for notebook progress, terminal logs, background workers, or a custom UI.
def on_progress(current, total, message):
pct = 0 if not total else round((current / total) * 100, 1)
print(f"{pct:5.1f}% {message}")
organizer = SortMomentsOrganizer(progress_callback=on_progress)Two-pass workflow: detect once, regroup many times
This is the control pattern you want when tuning thresholds. Detection is the expensive step; grouping is the experiment.
from sortmoments import SortMomentsConfig, detect_faces, group_faces
config = SortMomentsConfig(keep_intermediate=True)
work = "D:/cache/sortmoments/family-faces"
detect_faces("D:/photos/family", work, config=config)
for threshold in [0.50, 0.56, 0.62]:
trial = SortMomentsConfig(
similarity_threshold=threshold,
final_output_folder=f"D:/sorted/family-{threshold}",
)
people, folder = group_faces(work, input_folder="D:/photos/family", config=trial)
print(threshold, len(people), folder)Error handling
Long-running photo jobs fail for normal reasons: missing paths, unreadable images, model initialization, filesystem permissions, or disk space. Keep scripts explicit.
from pathlib import Path
from sortmoments import organize_photos
folder = Path("D:/photos/family")
if not folder.exists():
raise SystemExit(f"Missing folder: {folder}")
try:
result = organize_photos(folder)
except Exception as exc:
print(f"Sort Moments failed: {exc}")For unattended batch runs, consider the CLI exit codes and JSON config.