Automatically organize files into folders by file type - run once, watch forever, or let a background service handle it.
arrange scans a directory and moves files into categorized subfolders (Pictures, Videos, Documents, Audio, etc.) based on their extensions. For video and audio it goes further: it parses filenames to detect TV series, movies, and multi-part films and builds a clean folder hierarchy automatically.
It can run as a one-shot command, react to new files in real-time via a filesystem watcher, or run as a persistent background service (macOS LaunchAgent / Linux systemd / Windows service).
Requires Go 1.25+.
macOS / Linux
git clone https://github.com/braswelljr/arrange.git
cd arrange
make build
sudo mv ./bin/arrange /usr/local/bin/arrangeWindows (PowerShell)
git clone https://github.com/braswelljr/arrange.git
cd arrange
make build-windows # produces bin/arrange.exe
move bin\arrange.exe C:\Windows\System32\arrange.exeOr install directly into %GOPATH%\bin on any platform:
make installarrange versionOrganize your Downloads folder right now:
arrange run ~/DownloadsFiles are moved into subfolders: ~/Downloads/Pictures, ~/Downloads/Videos, ~/Downloads/Documents, etc.
To keep it organized automatically, install it as a service.
Scans a source directory and moves every file into its matching category subfolder.
arrange run <src> [dest]| Argument | Description |
|---|---|
src |
Directory to scan |
dest |
Destination root for organized folders (default: same as src) |
Flags
| Flag | Short | Description |
|---|---|---|
--exclude |
-c |
Comma-separated list of filenames to skip |
--recursive |
-r |
Recursively scan all subdirectories |
--dry-run |
-n |
Preview every move (and duplicate removal) without touching files |
--keep-duplicates |
-k |
Keep byte-identical duplicates as -vN copies instead of removing |
Behaviour notes
- Video and audio files are organised into a clean hierarchy automatically — no flat
Videos/dump. TV series go toVideos/<Title>/Season XX/<Title> SxxExx [quality].ext; movies go toVideos/<Title> (YYYY)/<Title> (YYYY) [quality].ext. - Subtitles travel with their video. A
.srt/.ass/.subbeside a video (Movie.2020.en.srtnext toMovie.2020.mkv) is moved into the same folder and renamed to match the video, preserving its language/flag tag:Movie (2020).en.srt. Subtitles with no matching video go toSubtitles/. - Browser-generated duplicate suffixes (
file (1).pdf,file (2).pdf) are stripped before moving — the destination is alwaysfile.pdf. - Duplicate handling: if the destination already holds a byte-identical file, the source is removed as a redundant copy (use
--keep-duplicatesto version it as-vNinstead). If a same-named file has different content, a versioned suffix is always appended (file-v1.pdf) so nothing is overwritten. - Symlinks and hidden dotfiles (
.DS_Store,.localized) are never moved. - In-progress downloads (
.crdownload,.part,.aria2,.!qb, etc.) are never touched. - Torrent meta-files (
.torrent) are never moved. --recursivewalks all subdirectories and organises every file found, skipping already-organised category folders (Videos/,Documents/, etc.) when the destination is the same as the source.
Examples
# Organize Downloads in-place
arrange run ~/Downloads
# Also recurse into nested folders (Telegram Desktop, etc.)
arrange run --recursive ~/Downloads
# Move files from Downloads into a separate folder
arrange run ~/Downloads ~/Organized
# Skip specific files
arrange run ~/Downloads --exclude "notes.txt,resume.pdf"Parses video and audio filenames to detect TV series, movies, and multi-part films and arranges them into a clean hierarchy.
arrange media <src> [dest]| Argument | Description |
|---|---|
src |
Directory to scan |
dest |
Destination root (default: same as src) |
Output structure
TV series → <Title>/Season XX/<Title> SxxExx [quality].ext
Movie → <Title> (YYYY)/<Title> (YYYY) [quality].ext
Multi-part → <Title> (YYYY)/Part N/<Title> (YYYY) [quality].ext
Creator grouping
Add director or franchise names to media_creators in your config. All their titles will be grouped under a shared top-level folder:
config: "media_creators": ["Tyler Perry"]
result: Tyler Perry/
Why Did I Get Married (2007)/...
Madea Goes to Jail (2009)/...
Both "Tyler Perry's …" and "Tyler Perrys …" (common in dot-separated filenames) are matched automatically. Curly apostrophes (’) are normalised to straight ones, so Tyler.Perry’s.Zatima and Tyler.Perrys.Zatima never split into separate folders.
Title aliases
When the same show ships under different names (Zatima, Tyler Perry's Zatima), map them to one canonical title in your config. Matching ignores case, apostrophes, and spacing, so a single entry covers every spelling variant:
{
"title_aliases": {
"Tyler Perrys Zatima": "Zatima"
}
}Result: Zatima.*, Tyler.Perrys.Zatima.*, and Tyler.Perry’s.Zatima.* all land in one Videos/Zatima/Season XX/ tree.
Examples
arrange media ~/Downloads
arrange media ~/Downloads ~/MediaWatches a directory for new files and organizes them automatically as they arrive.
arrange watch <dir>Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--recursive |
-r |
false |
Also watch all subdirectories |
Behaviour notes
- Changes are debounced (800 ms) so a burst of files (archive extraction, batch copy) triggers a single organize pass.
- In recursive mode, newly created subdirectories are watched automatically.
- Files that arrive in app sub-folders (Telegram Desktop, WhatsApp, etc.) are sorted into the root watched directory - e.g. a video dropped into
~/Downloads/Telegram Desktop/is moved to~/Downloads/Videos/, not~/Downloads/Telegram Desktop/Videos/. - Directories listed in
excluded_dirsin your config are never watched.
# Watch Downloads and all its subdirectories
arrange watch --recursive ~/DownloadsThe process runs in the foreground. Use service to run it persistently in the background.
Manages arrange as a background service (macOS LaunchAgent, Linux systemd user unit, or Windows service).
arrange service <command>| Subcommand | Description |
|---|---|
install <dir> |
Install the service and set it to watch <dir> |
start |
Start the installed service |
stop |
Stop the running service |
status |
Check whether the service is running |
uninstall |
Remove the service entirely |
Print the current version.
arrange versionarrange runs as a LaunchAgent so it starts automatically on login and watches your Downloads folder silently in the background.
1. Build and install the binary
make build
sudo mv ./bin/arrange /usr/local/bin/arrangeThe binary path is recorded in the LaunchAgent plist - don't move it after installation.
2. Do an initial cleanup
arrange run ~/Downloads3. Install the LaunchAgent
arrange service install ~/DownloadsThis creates ~/Library/LaunchAgents/arrange.plist configured to run arrange watch ~/Downloads.
4. Start the service
arrange service start5. Verify
arrange service statusTo stop or remove:
arrange service stop # pause temporarily
arrange service uninstall # remove the LaunchAgent entirely1. Build and install the binary
make build
sudo mv ./bin/arrange /usr/local/bin/arrange2. Install and start the service
arrange service install ~/Downloads
arrange service startThis creates and enables a systemd user unit. The service restarts automatically on boot.
3. Check status
arrange service status
# or directly:
systemctl --user status arrangearrange service registers a native Windows service through the SCM. Run the
commands from an elevated (Administrator) prompt:
arrange service install C:\Users\<you>\Downloads # register, auto-start at boot
arrange service start
arrange service status
arrange service stop
arrange service uninstallThe service watches the given directory (recursively) and organizes files into
category folders under it, exactly like arrange watch. The executable path is
recorded with the SCM — don't move arrange.exe after installing.
Prefer no elevation? You can still run it at login via Task Scheduler: Create Basic Task → trigger "When I log on" → Start a Program
arrange.exe, argumentswatch C:\Users\<you>\Downloads.
The config file is created automatically on first run at:
| Platform | Default path |
|---|---|
| macOS / Linux | ~/.config/arrange/config.json (respects $XDG_CONFIG_HOME) |
| Windows | %APPDATA%\arrange\config.json |
| Custom | Pass --config-path <path> to any command |
Full schema:
{
"unknown_files_folder": "Other",
"excluded_dirs": [],
"media_creators": [],
"known_files": []
}The default configuration organizes files into:
| Folder | Example extensions |
|---|---|
Pictures |
jpg png gif webp heic svg psd raw arw nef … |
Videos |
mp4 mkv mov avi webm ts m2ts vob … |
Audio |
mp3 flac wav aac opus wma ogg m4a … |
Documents |
pdf docx xlsx pptx txt md csv tex … |
eBooks |
epub mobi azw3 fb2 djvu cbr cbz … |
Applications |
exe dmg deb apk msi appimage ipa … |
Archive |
zip tar gz rar 7z xz zst … |
DiskImages |
iso img vmdk vhd qcow2 … |
Fonts |
ttf otf woff woff2 fon … |
Database |
sqlite db sql mdb accdb … |
Subtitles |
srt ass vtt sub idx sup … |
Design |
fig sketch xd ai indd afdesign … |
3D Models |
obj fbx stl blend gltf glb c4d … |
Other |
anything not matched above |
Exempt entries (exempt_files: true) are never moved.
| Group | Extensions |
|---|---|
| Source code | go py js ts rs java html css json yaml … |
| Torrent meta | torrent magnet |
| In-progress downloads | crdownload part download !qb !ut !bt aria2 fdm idm ytdl … |
List director or franchise names in media_creators and arrange media will group all their titles under a shared folder:
{
"media_creators": ["Tyler Perry", "Christopher Nolan", "Pixar"]
}List subdirectory names in excluded_dirs to prevent arrange watch from watching them:
{
"excluded_dirs": ["node_modules", ".git", "tmp"]
}Files that land in unlisted app subdirectories (Telegram Desktop, WhatsApp, etc.) are still organized normally - they are moved to the correct category folder under the root watched directory.
Edit ~/.config/arrange/config.json to add or override categories:
{
"unknown_files_folder": "Other",
"media_creators": ["Tyler Perry"],
"known_files": [
{
"folder": "Design",
"extensions": ["fig", "sketch", "xd"],
"exempt_files": false
},
{
"folder": "",
"extensions": ["log"],
"exempt_files": true
}
]
}Set exempt_files: true on any entry to make those extensions always be skipped.
Available on every command:
| Flag | Short | Default | Description |
|---|---|---|---|
--config-path |
-C |
~/.config/arrange/config.json |
Path to a custom config file |
--help |
-h |
- | Help for the current command |