-
-
Notifications
You must be signed in to change notification settings - Fork 5
Folder Structure
kei organizes downloaded photos into date-based subdirectories within your download directory.
With the default --folder-structure "%Y/%m/%d", a photo taken on March 15, 2024 is saved to:
/photos/2024/03/15/IMG_1234.HEIC
The folder structure is a strftime format string applied to each asset's creation date. All standard strftime specifiers are supported.
| Template | Example path |
|---|---|
%Y/%m/%d |
2024/03/15/IMG_1234.HEIC |
%Y/%m |
2024/03/IMG_1234.HEIC |
%Y/%B |
2024/March/IMG_1234.HEIC |
%Y |
2024/IMG_1234.HEIC |
{album}/%Y/%m |
Favorites/2024/03/IMG_1234.HEIC |
none |
IMG_1234.HEIC |
The Python {:%Y/%m/%d} syntax is also accepted for compatibility with existing configurations.
The {album} token expands to the album name for each asset. This is useful when syncing specific albums into separate directories:
kei sync --album "Favorites" --album "Travel" --folder-structure "{album}/%Y/%m"Produces:
/photos/Favorites/2024/03/IMG_1234.HEIC
/photos/Travel/2024/01/IMG_5678.HEIC
Album names are sanitized for filesystem safety (see below).
Which passes kei runs depends on the combination of -a and whether {album} is in the template. The table below uses %Y/%m/%d as a stand-in date format; any strftime template works the same way.
| Command | Passes run | Where files land |
|---|---|---|
kei sync --folder-structure "%Y/%m/%d" |
One library pass. |
2025/06/15/<filename> for every asset. |
kei sync --folder-structure "{album}/%Y/%m/%d" |
Auto-upgraded to -a all. Per-album passes plus a library-wide unfiled pass. |
Albumed assets at <AlbumName>/2025/06/15/<filename>. Unfiled assets at /2025/06/15/<filename> (the {album} segment collapses to empty). |
kei sync -a all --folder-structure "%Y/%m/%d" |
Per-album passes merged into one stream. No unfiled pass. | All albumed assets at 2025/06/15/<filename>. Unfiled assets are not synced. Assets in multiple albums dedup to one on-disk copy. |
kei sync -a all --folder-structure "{album}/%Y/%m/%d" |
Per-album passes plus an unfiled pass. | Albumed assets at <AlbumName>/2025/06/15/<filename>. Unfiled assets at /2025/06/15/<filename>. Assets in multiple albums get one copy per album folder. |
kei sync -a Vacation --folder-structure "%Y/%m/%d" |
One pass for Vacation. |
2025/06/15/<filename> for each Vacation asset. No unfiled pass. |
kei sync -a Vacation --folder-structure "{album}/%Y/%m/%d" |
One pass for Vacation. |
Vacation/2025/06/15/<filename>. No unfiled pass. |
kei sync -a Vacation -a Family --folder-structure "{album}/%Y/%m/%d" |
Two named passes. |
Vacation/... and Family/.... Shared assets duplicated per folder. No unfiled pass. |
Key rules behind the table:
- The unfiled pass runs only when
{album}is in the template and the selection is-a all(explicit or auto-upgraded). - An "unfiled" asset is one not in any user-created album. Apple's smart folders (Favorites, Screenshots, etc.) don't count.
- With
{album}in the template, an asset in N albums writes N on-disk copies, one per album folder. - Without
{album}in the template, an asset in N albums writes one copy. The state DB's(asset_id, version_size)key catches the duplicate before the second write. - The unfiled pass carries an
exclude_idsset built from every user album's membership (including excluded albums), so albumed assets never also appear at the collapsed root path.
Within each folder, filenames are preserved from iCloud. Characters that are invalid on the local filesystem (/\:*?"<>|) are stripped.
When downloading from specific albums, album names are used as directory components. These are sanitized to prevent path traversal attacks or invalid directories:
- Directory traversal sequences (
..) are replaced with_ - Leading/trailing dots and spaces are stripped
- Windows reserved names (
CON,NUL,PRN,COM1–COM9,LPT1–LPT9) are prefixed with_ - Invalid filesystem characters are removed
The Rust version produces identical folder paths when using the same template string. If you're migrating from the Python version with the default folder structure, your existing directory layout will be preserved.