🗂️ mimedy
Range un dossier en désordre selon le vrai type de chaque fichier, pas selon son extension.
Tidies up a messy folder by each file's real type, not by its extension.
mimedy = MIME + tidy (ranger) : le type MIME de chaque fichier décide de sa place.
mimedy = MIME + tidy: each file's MIME type decides where it belongs.
🇫🇷 Français
Pourquoi ?
Un dossier Téléchargements finit toujours par ressembler à ça : des PDF, des captures d'écran, des archives, un .json exporté un jour, un fichier sans extension dont personne ne se souvient…
La plupart des outils de rangement se fient à l'extension. Or une extension peut mentir, manquer ou être fausse. mimedy utilise Magika, le modèle de deep learning de Google qui identifie un fichier à partir de son contenu, pour décider où il doit aller.
$ mimedy ~/Downloads
[INFO] Loaded config from /Users/me/.config/mimedy/config.yaml
[INFO] Organizing /Users/me/Downloads
[INFO] 'export.csv' → Data/
[INFO] 'holidays.jpg' → Photos/
[INFO] 'invoice' → PDF/
[INFO] 'report.pdf' → PDF/report (1).pdf
[INFO] 'script.py' → Python/
[WARNING] Skipped 'locked.txt': Magika could not read the file (permission_error)
[INFO] 5 files to move into 4 folders, 1 skipped
Move 5 files? [y/N]: y
[INFO] Done: 5 moved, 0 failed
Fonctionnalités
- 🧠 Détection par le contenu : Magika reconnaît plus de 200 types de fichiers, même renommés ou sans extension.
- 🪜 Règles en cascade : fichiers cachés, fichiers volumineux, extensions, types MIME, puis le groupe Magika en dernier recours.
- 📋 Plan puis confirmation : tous les déplacements sont affichés avant d'être effectués, et rien ne bouge sans votre accord.
--dry-runs'arrête au plan. - 🧯 Robuste : un fichier illisible ou verrouillé est signalé et ignoré, sans interrompre le rangement.
- 🛡️ Aucun écrasement : si
photo.jpgexiste déjà, le nouveau fichier devientphoto (1).jpg. - 🐛 Mode
--verbose: indique pour chaque fichier la règle qui a décidé de sa destination. - ⚙️ Configuration YAML simple, entièrement facultative.
Installation
Prérequis : Python 3.10+ et uv.
git clone https://github.com/ochapeau/mimedy.git
cd mimedy
uv sync
Utilisation
💡 mimedy affiche toujours le plan complet et demande confirmation avant de déplacer quoi que ce soit.
# Afficher le plan, puis confirmer
uv run mimedy ~/Downloads
# Afficher le plan seulement
uv run mimedy ~/Downloads --dry-run
# Sans confirmation, par exemple dans un script
uv run mimedy ~/Downloads --yes --config my-config.yaml
# Comprendre pourquoi un fichier va à tel endroit
uv run mimedy ~/Downloads --dry-run --verbose
| Option | Description |
|---|---|
DIRECTORY |
Dossier à ranger (obligatoire). |
--config, -c PATH |
Fichier de configuration YAML (par défaut : voir Configuration). |
--dry-run, -n |
Affiche les déplacements prévus sans les effectuer. |
--yes, -y |
Déplace sans demander de confirmation. |
--verbose, -v |
Affiche la règle appliquée à chaque fichier. |
--lowercase, -l |
Garde en minuscules les dossiers nommés d'après Magika (video/ au lieu de Video/). |
--version, -V |
Affiche la version. |
--help, -h |
Affiche l'aide. |
--init-config |
Crée une configuration d'exemple commentée à l'emplacement par défaut (sans jamais écraser une configuration existante). |
--install-completion |
Active l'autocomplétion des options avec Tab dans votre shell (une seule fois suffit). |
Seuls les fichiers situés directement dans le dossier sont traités. Les sous-dossiers existants ne sont pas touchés, ce qui permet de relancer l'outil sans risque.
| Code de sortie | Signification |
|---|---|
0 |
Tout s'est bien passé, ou il n'y avait rien à ranger. |
1 |
Au moins un fichier n'a pas pu être traité, ou la confirmation a été refusée. |
2 |
Arguments ou configuration invalides. |
Configuration
Sans --config, mimedy lit le fichier de configuration personnel, s'il existe :
| Système | Emplacement |
|---|---|
| Linux, macOS | ~/.config/mimedy/config.yaml (ou $XDG_CONFIG_HOME/mimedy/config.yaml) |
| Windows | %APPDATA%\mimedy\config.yaml |
S'il n'existe pas, les valeurs par défaut sont utilisées. Un fichier passé avec --config doit en revanche exister. mimedy --help affiche l'emplacement exact sur votre machine.
Pour démarrer, créez une configuration d'exemple commentée, puis adaptez-la :
mimedy --init-config
# Created config file: /Users/me/.config/mimedy/config.yaml
hidden: "Hidden" # fichiers commençant par '.'
large_files:
threshold_mb: 500 # en Mo décimaux, comme le Finder ou l'Explorateur
target_dir: "Large"
extensions: # correspondance exacte sur l'extension
.blend: "Blender"
.csv: "Data"
mimetypes: # type MIME détecté par Magika
application/pdf: "PDF"
image/jpeg: "Photos"
Toutes les clés sont facultatives : celles qui manquent reprennent leur valeur par défaut. Les destinations peuvent contenir des sous-dossiers ("Code/Python"). config.example.yaml, le fichier copié par --init-config, contient un exemple complet et commenté.
Comment un fichier est-il classé ?
Les règles sont évaluées dans cet ordre ; la première qui correspond l'emporte :
| # | Règle | Exemple |
|---|---|---|
| 1 | Fichier caché | .env → Hidden/ |
| 2 | Fichier volumineux | film.mkv (2 Go) → Large/ |
| 3 | Extension | scene.blend → Blender/ |
| 4 | Type MIME (Magika) | facture (PDF sans extension) → PDF/ |
| 5 | Groupe Magika | script.py → Code/ |
Les règles 1 à 3 ne lisent pas le contenu des fichiers : elles sont instantanées. Magika n'est appelé que si elles ne suffisent pas.
Structure du projet
mimedy/
├── pyproject.toml
├── src/mimedy/
│ ├── main.py # Point d'entrée de la CLI (Typer)
│ ├── config.py # Chargement et validation de la configuration
│ ├── config.example.yaml # Configuration d'exemple commentée
│ ├── organizer.py # Règles de classement, planification et déplacements
│ └── errors.py # Exceptions du projet
└── tests/ # Tests pytest (configuration, règles, plan, CLI)
Développement
Le code est vérifié par Ruff (lint et formatage) à chaque commit, et testé avec pytest. L'intégration continue lance les deux à chaque push, sur Python 3.10 à 3.13.
uv run pre-commit install # vérifications automatiques à chaque commit
uv run pytest # lancer les tests
Feuille de route
- Configuration typée et validée
- Configuration globale dans
~/.config/mimedy/ - Tests automatisés et intégration continue
- Publication sur PyPI (
uv tool install mimedy) - Interface en terminal (TUI), en option
Licence
Distribué sous licence MIT. Voir LICENSE.
🇬🇧 English
Why?
A Downloads folder always ends up looking the same: PDFs, screenshots, archives, a .json exported at some point, a file with no extension that nobody remembers…
Most organizing tools trust file extensions. But an extension can lie, be missing, or simply be wrong. mimedy uses Magika, Google's deep learning model that identifies a file from its content, to decide where it belongs.
$ mimedy ~/Downloads
[INFO] Loaded config from /Users/me/.config/mimedy/config.yaml
[INFO] Organizing /Users/me/Downloads
[INFO] 'export.csv' → Data/
[INFO] 'holidays.jpg' → Photos/
[INFO] 'invoice' → PDF/
[INFO] 'report.pdf' → PDF/report (1).pdf
[INFO] 'script.py' → Python/
[WARNING] Skipped 'locked.txt': Magika could not read the file (permission_error)
[INFO] 5 files to move into 4 folders, 1 skipped
Move 5 files? [y/N]: y
[INFO] Done: 5 moved, 0 failed
Features
- 🧠 Content-based detection: Magika recognizes over 200 file types, even when renamed or missing an extension.
- 🪜 Cascading rules: hidden files, large files, extensions, MIME types, then the Magika group as a fallback.
- 📋 Plan, then confirm: every move is shown before it happens, and nothing moves without your approval.
--dry-runstops at the plan. - 🧯 Robust: an unreadable or locked file is reported and skipped, without stopping the run.
- 🛡️ Never overwrites: if
photo.jpgalready exists, the new file becomesphoto (1).jpg. - 🐛
--verbosemode: shows which rule decided each file's destination. - ⚙️ Simple YAML configuration, entirely optional.
Installation
Requirements: Python 3.10+ and uv.
git clone https://github.com/ochapeau/mimedy.git
cd mimedy
uv sync
Usage
💡 mimedy always shows the full plan and asks for confirmation before moving anything.
# Show the plan, then confirm
uv run mimedy ~/Downloads
# Only show the plan
uv run mimedy ~/Downloads --dry-run
# No confirmation, e.g. in a script
uv run mimedy ~/Downloads --yes --config my-config.yaml
# Understand why a file goes where it goes
uv run mimedy ~/Downloads --dry-run --verbose
| Option | Description |
|---|---|
DIRECTORY |
Folder to organize (required). |
--config, -c PATH |
YAML configuration file (default: see Configuration). |
--dry-run, -n |
Show planned moves without performing them. |
--yes, -y |
Move without asking for confirmation. |
--verbose, -v |
Show which rule applied to each file. |
--lowercase, -l |
Keep folders named after Magika groups lowercase (video/ instead of Video/). |
--version, -V |
Show the version. |
--help, -h |
Show the help. |
--init-config |
Create a commented example config at the default location (never overwrites an existing one). |
--install-completion |
Enable Tab completion of the options in your shell (once is enough). |
Only files directly inside the folder are processed. Existing subfolders are left untouched, so the tool can safely be run again.
| Exit code | Meaning |
|---|---|
0 |
Everything went fine, or there was nothing to organize. |
1 |
At least one file could not be processed, or the confirmation was declined. |
2 |
Invalid arguments or configuration. |
Configuration
Without --config, mimedy reads your personal configuration file, if it exists:
| System | Location |
|---|---|
| Linux, macOS | ~/.config/mimedy/config.yaml (or $XDG_CONFIG_HOME/mimedy/config.yaml) |
| Windows | %APPDATA%\mimedy\config.yaml |
If it doesn't exist, the defaults are used. A file passed with --config, however, must exist. mimedy --help shows the exact location on your machine.
To get started, create a commented example config, then adapt it:
mimedy --init-config
# Created config file: /Users/me/.config/mimedy/config.yaml
hidden: "Hidden" # files starting with '.'
large_files:
threshold_mb: 500 # decimal MB, like Finder or Explorer
target_dir: "Large"
extensions: # exact extension match
.blend: "Blender"
.csv: "Data"
mimetypes: # MIME type detected by Magika
application/pdf: "PDF"
image/jpeg: "Photos"
Every key is optional: missing ones fall back to their default value. Destinations may include subfolders ("Code/Python"). See config.example.yaml, the file copied by --init-config, for a complete, commented example.
How is a file classified?
Rules are evaluated in this order; the first match wins:
| # | Rule | Example |
|---|---|---|
| 1 | Hidden file | .env → Hidden/ |
| 2 | Large file | movie.mkv (2 GB) → Large/ |
| 3 | Extension | scene.blend → Blender/ |
| 4 | MIME type (Magika) | invoice (PDF with no extension) → PDF/ |
| 5 | Magika group | script.py → Code/ |
Rules 1 to 3 don't read file contents, so they are instant. Magika only runs when they aren't enough.
Project structure
mimedy/
├── pyproject.toml
├── src/mimedy/
│ ├── main.py # CLI entry point (Typer)
│ ├── config.py # Configuration loading and validation
│ ├── config.example.yaml # Commented example configuration
│ ├── organizer.py # Classification rules, planning and moves
│ └── errors.py # Project exceptions
└── tests/ # pytest tests (configuration, rules, plan, CLI)
Development
Code is checked by Ruff (linting and formatting) on every commit, and tested with pytest. Continuous integration runs both on every push, on Python 3.10 to 3.13.
uv run pre-commit install # automatic checks on every commit
uv run pytest # run the tests
Roadmap
- Typed, validated configuration
- Global configuration in
~/.config/mimedy/ - Automated tests and continuous integration
- Publish on PyPI (
uv tool install mimedy) - Optional terminal interface (TUI)
License
Released under the MIT License. See LICENSE.
Metadata
Release files for mimedy 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mimedy-1.0.0.tar.gz | 49.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mimedy-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.2 kB
Release files / mimedy-1.0.0.tar.gz
| Download URL | mimedy-1.0.0.tar.gz |
|---|---|
| Size | 49.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9d60fb6e2bbe23fd28c6c41b3899eaf4797b1e63916baa7e8180a5c10335550c
|
|
BLAKE2b-256 checksum How to use checksums |
1baa777827823bcfe184121eee660ba763223de9435654d20411362ecf87b3f4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / mimedy-1.0.0-py3-none-any.whl
| Download URL | mimedy-1.0.0-py3-none-any.whl |
|---|---|
| Size | 17.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bf8e723b97a78477198da05c65dbfc976787addc984a8e870d1897d1774b522f
|
|
BLAKE2b-256 checksum How to use checksums |
c61346e9f22d5fd0e4b9570ef52e9632e010134cff54f6dd1496367316712db0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|