mm_organize#

Organize photos and videos based on EXIF creation dates.

Photos → destination/Photos/YYYY-MM/YYYY-MM-DD_HHMMSS.ext Videos → destination/Movies/YYYY-MM/YYYY-MM-DD_HHMMSS.ext Unknown → destination/Unknown/original_name.ext

Functions#

get_exif_datetime(file_path)

Return a datetime extracted from the file's EXIF data.

organize_files(source, destination, *[, dry_run, workers])

Walk source recursively, locate media files, and copy them into destination.

main(source, destination, *, dry_run, workers)

Organise media files from SOURCE into DESTINATION by EXIF date.

test_parse_exif_dt()

Test the _parse_exif_dt function.

test_format_parts()

Test the _format_parts function.

test_is_media_file()

Test the _is_media_file function.

Module Contents#

mm_organize.get_exif_datetime(file_path)#

Return a datetime extracted from the file’s EXIF data.

Parameters:

file_path (pathlib.Path)

Return type:

datetime.datetime | None

mm_organize.organize_files(source, destination, *, dry_run=False, workers=4)#

Walk source recursively, locate media files, and copy them into destination.

Parameters:
  • source (pathlib.Path)

  • destination (pathlib.Path)

  • dry_run (bool)

  • workers (int)

Return type:

tuple[int, int]

mm_organize.main(source, destination, *, dry_run, workers)#

Organise media files from SOURCE into DESTINATION by EXIF date.

Files that contain a usable EXIF timestamp are placed under Photos/YYYY-MM or Movies/YYYY-MM (renamed to DD_HHMMSS.ext). Files without a timestamp end up in Unknown/ (original name preserved).

The script never falls back to the file’s modification time.

Parameters:
  • source (pathlib.Path)

  • destination (pathlib.Path)

  • dry_run (bool)

  • workers (int)

Return type:

None

mm_organize.test_parse_exif_dt()#

Test the _parse_exif_dt function.

Return type:

None

mm_organize.test_format_parts()#

Test the _format_parts function.

Return type:

None

mm_organize.test_is_media_file()#

Test the _is_media_file function.

Return type:

None