Skip to content

Repository files navigation

UniFile

VersionLicensePythonPlatformStatusAI PoweredTags

Unified AI-powered file organization platform — combining tag-based library management, 7-level classification, LLM intelligence, cleanup tools, duplicate detection, and media metadata into a single premium dark-themed desktop app.

Screenshot

Overview

UniFile merges the best ideas from five file organization projects into one cohesive tool:

Source ProjectStarsWhat UniFile Takes From It
TagStudio42kTag-based file library with hierarchical tags, aliases, color coding, field system
FileOrganizerFoundation: 7-level classification, Ollama LLM, PyQt6 GUI, 384+ categories
Local-File-Organizer3.1kAI file analysis with vision models (planned: Nexa SDK backend)
classifier1.1kRule-based file sorting by extension (planned: category preset merge)
mnamer1kMedia metadata lookup patterns via TMDb, OMDb, TVMaze, and guessit

Quick Start

git clone https://github.com/SysAdminDoc/UniFile.git
cd UniFile
python -m venv .venv
.\.venv\Scripts\python -m pip install -e ".[full]"
.\.venv\Scripts\python run.py

Source imports do not install packages or download models. To opt in to the legacy dependency bootstrap for a source checkout, run:

python run.py --install-deps

Ollama is optional. Install it from ollama.com, start it with ollama serve, then download a model manually (ollama pull qwen2.5:7b) or from Settings > Ollama LLM. UniFile falls back to rule-based classification when Ollama is unavailable.

Features

Tag Library (NEW in v8.0)

Full tag-based file management adapted from TagStudio's SQLAlchemy models:

FeatureDescription
Hierarchical TagsParent-child tag relationships with unlimited nesting
Tag AliasesMultiple names for the same tag
Color-Coded Tags20 color presets for visual organization
Category TagsDistinguish organizational categories from descriptive tags
Quick PresetsOne-click Favorite, Important, Review, Archive tags
Entry Fields21 built-in field types (title, author, AI summary, TMDb ID, etc.)
Custom Field SchemasPer-library currency, date, status/enum, checkbox, and text fields with validation
Auto-TaggingClassification results automatically create and apply tags
Bulk OperationsScan directories, bulk-add files, batch tag assignment
Tag SearchReal-time search across tags and entries

The tag library stores data in .unifile/unifile_tags.sqlite within your library directory — non-destructive, no files are modified. When an adjacent <filename>.<extension>.xmp exists, its standard dc:subject keywords are imported additively when an entry is opened. Tag assignments also write the current UniFile tags back to that XMP sidecar without touching the original file; third-party keywords remain intact across later updates. Tag Library and local index databases share an explicit SQLite policy: WAL, 10-second busy timeout, foreign keys enabled, NORMAL synchronous mode, and automatic checkpoints every 1,000 pages. ORM sessions are thread-owned; long-lived direct connections declare whether they are thread-confined or serialized behind a module lock, and all connections are disposed on close or process exit. Keep WAL-backed libraries on local storage; UNC/network shares are rejected for the Tag Library because their locking and WAL semantics are not reliable. Backups use SQLite's online backup API and a DELETE-journal snapshot, so restore never copies a live -wal file by hand.

TagStudio migration is available from the Tag Library panel and headless CLI. Import accepts a TagStudio library folder or .TagStudio/ts_library.sqlite/.db file and reads it in SQLite read-only mode. Export writes an additive TagStudio-compatible .TagStudio/ts_library.sqlite; existing TagStudio records are retained, and preserved .TagStudio/thumbs data is copied without changing the original files.

python -m unifile import-tagstudio /path/to/tagstudio /path/to/unifile-library --json
python -m unifile export-tagstudio /path/to/unifile-library /path/to/tagstudio-export --json

Use --dry-run with import-tagstudio to inspect counts without creating a UniFile database, or --no-thumbnails to omit cached thumbnail transfer.

Book Library

The built-in Book Library profile narrows scans to .epub, .pdf, .mobi, and .azw3 files. EPUB and PDF metadata is extracted locally; ISBNs are normalized with the optional isbnlib2 package, while MOBI/AZW3 files fall back safely to filename metadata. When a Tag Library is open, a Book Library scan adds title, author, ISBN, language, genre, series, publisher, synopsis, and reading-status fields plus deterministic book, genre:*, language:*, series:*, and reading:* tags.

Remote enrichment is explicit and cached. OpenLibrary is queried first and Google Books fills missing values; covers are downloaded only when --download-covers is requested. The API client uses a one-request-per-second default interval and an identifying User-Agent. Use the headless commands for repeatable workflows:

python -m unifile books scan /path/to/ebooks --library /path/to/unifile-library --lookup --download-covers --json
python -m unifile books export-opf /path/to/unifile-library --output /path/to/calibre-metadata --json

OPF export is non-destructive: each book gets a deterministic folder containing a Calibre-compatible metadata.opf, and cached cover art is copied when available. Generated metadata defaults to .unifile/calibre-opf when --output is omitted.

Video Project Awareness

Project Audit reads .aep, .prproj, .drp, and .fcpbundle projects without opening or modifying them. It resolves referenced media, reports assets shared by multiple projects, lists media files with no project reference, and separates missing paths. Open Ctrl+K → Project Audit from the desktop UI, or use the read-only CLI report:

python -m unifile projects audit /path/to/projects --json
python -m unifile projects audit /path/to/projects --library /path/to/unifile-library --apply

--apply is explicit: resolved assets receive project:* and project-reference tags plus project names, project modified timestamps, and reference counts in the tag library. Binary project formats use bounded string extraction, while Final Cut bundles also inspect FCPXML/SQLite content and managed media.

Media Lookup (NEW in v8.0)

Unified movie, TV, book, audiobook, and audio metadata lookup powered by TMDb, TVDB, TVMaze, OMDb, OpenLibrary, Google Books, and MusicBrainz (adapted from mnamer): TMDb, TVDB, OMDb, and OpenSubtitles use your own API keys via the Media Lookup panel or API_KEY_TMDB, API_KEY_TVDB, API_KEY_OMDB, and API_KEY_OPENSUBTITLES; TVDB can also use API_KEY_TVDB_PIN. TVMaze, OpenLibrary, Google Books, and MusicBrainz work without a key. OpenSubtitles downloads additionally require the user account credentials accepted by its API.

Saved media, AI-provider, Envato, AcoustID, and OpenSubtitles credentials are stored in the operating-system keyring; environment variables take precedence. UniFile migrates supported legacy plaintext credential files into the keyring and removes the plaintext source without creating a backup copy. If the keyring is unavailable, UniFile does not read or persist plaintext secrets and reports that saving is unavailable.

FeatureDescription
Video SearchTMDb → OMDb movie fallback and TVDB → TMDb → TVMaze TV fallback with full details and episode lists
Book SearchOpenLibrary → Google Books fallback for books and audiobooks, including authors, ISBN, covers, and publication data
Audio SearchMusicBrainz recording lookup with artist, album, release, and Cover Art Archive metadata
Provider Key StatusMissing or rejected credentials are shown before and after searches, with environment-variable precedence
guessit ParserParse video, book, audiobook, and audio filenames to auto-detect title, year, season, and episode
Artwork PreviewFull poster or cover-art display with synopsis, genres, and external IDs
Cover Art EmbeddingSelect a local MP3, FLAC/Ogg, MP4/M4A, or EPUB file and fetch missing artwork from the reviewed provider result; writes are atomic, cached, and undoable
Apply to TagsPush normalized media metadata into matching Tag Library fields and genre tags
Copy MetadataOne-click copy of all metadata fields to clipboard
Subtitle + Chapter SidecarsReview .srt/.ass OpenSubtitles matches and save TMDb-derived .chapters.json metadata beside a local video
NFO SidecarsSave reviewed movie, TV, music, or book metadata as Kodi/Plex-compatible .nfo XML beside local media; the headless CLI also accepts Tag Library field JSON
RAW Photo FamiliesRecognize CR2, CR3, NEF, ARW, ORF, DNG, and related RAW files; collapse same-stem RAW+JPEG captures into one move item and prefer RAW EXIF before filling gaps from the JPEG
Cached RequestsAPI responses cached for 6 days to reduce API calls

For format-level metadata work, select a file, open Tools → Batch Metadata Editor, and choose Inspect Raw Metadata. The inspector enumerates EXIF, XMP sidecar, ID3, mutagen audio, and PDF fields, lets you edit writable values in a proposed-value column, and requires an explicit preview before applying. JPEG/TIFF EXIF, ID3, mutagen, and PDF writes are atomic and backed up for undo; XMP changes remain non-destructive adjacent .xmp sidecars.

Nexa SDK Backend (NEW in v8.0)

Alternative local AI backend using Nexa SDK (adapted from Local-File-Organizer):

FeatureDescription
LLaVA VisionImage understanding and description via LLaVA v1.6
Llama 3.2 TextText summarization and classification via Llama 3.2 3B
One-Click SwitchToggle between Ollama and Nexa in Settings
Image ClassificationVision model describes images, text model classifies
File Content AnalysisReads text files and generates summaries for classification
Model Catalog5 pre-configured model options for vision and text

Enable in Settings > Ollama LLM > Alternative Backend: Nexa SDK. Requires pip install nexaai.

AI Classification

FeatureDescription
Ollama LLMLocal AI-powered category + name inference via Ollama
Explicit Ollama SetupChecks local Ollama/model readiness and points to Settings/manual setup when missing
384+ Built-in CategoriesCovers design, video, audio, print, web, 3D, photography
7-Level PipelineExtension > Keyword > Fuzzy > Metadata > Composition > Context > LLM
Multiple ProfilesDesign Assets, Book Library, PC Files, Photo Library, and custom profiles
Multiple LibrariesSwitch registered Tag Libraries from the sidebar with scoped AI, rules, and theme preferences
Color Palette SearchIndex dominant image colors and search with color:blue or natural color-tone phrases
Rules EditorCustom if/then rules with condition builder UI
Natural Language RulesCompile one routing request into a local, reviewable action plan
Rename TemplatesToken-based rename templates with live preview and move-time rendering, including media season/episode tokens

Natural Language Rules

Open Settings → Natural Language Rules, describe one routing request, and choose the source folder. UniFile asks the configured provider for one structured rule, evaluates matching files locally, and previews the resulting action DAG with exact source and destination paths. Click Apply approved plan only after reviewing the rows; the apply step makes no further AI calls, never overwrites an existing file, and records successful moves for undo. The same dialog can open a JSON plan exported by the headless scan command, so CLI dry runs and provider proposals use the same validated action-list contract.

Multiple Libraries

Register existing library folders with the + control in the sidebar's LIBRARY section. The active selector switches the open Tag Library and keeps each library's Ollama model/settings, classification rules, and theme in its own .unifile folder. Opening a folder directly from the Tag Library also registers it automatically; forgetting a registration never deletes its files.

Color Palette Search

Images added or scanned into a Tag Library receive a small, deterministic dominant-color palette index. Use Index Colors in the Tag Library header to rebuild the index for existing images, then click a Dominant color swatch or search with color:blue or phrases such as show me files with predominant blue tones. Swatches select rank-zero palette entries; searches remain local and read-only, and unsupported or unreadable images are simply left without palette rows.

Timeline Filtering

The PC File Organizer shows a date histogram above scan results. Choose file creation or modification dates, then adjust the From/To range handles to narrow the results without changing the scan or hiding files whose dates are unknown. Use Reset to restore the full result set.

Keyboard Shortcuts

Open Settings → Keyboard Shortcuts… to review every application binding and replace any shortcut in place. Duplicate bindings and Windows shell shortcuts are rejected before saving; clear a field to disable a binding, or use Reset defaults to restore the shipped set. The Tools menu shows the active bindings for the command palette and voice control.

Custom Field Schemas

Open Field Schemas in the Tag Library header to add fields scoped to the active library. Currency fields support optional minimum and maximum values; status fields use a fixed choice list; dates normalize to YYYY-MM-DD; and checkboxes accept true/false values. Select one file and choose Edit Fields to update built-in or custom metadata. Invalid values are rejected before they reach the library database, and blank values clear the stored field.

Voice Control

Open Settings → Voice Control or press Ctrl+Shift+V to type a command or transcribe an existing audio/video file with the configured offline Whisper model. Local grammar handles scan, tag, and search actions (including phrases such as show me large video files); an optional AI fallback is explicit and disabled by default. Every command is previewed before it runs, and tag writes require the dialog's review button.

Organization Modes

ModeDescription
Categorize FoldersSort folders into category groups using AI + rules
Categorize + Smart RenameFull AI rename + categorization in one pass
PC File OrganizerSort individual files by extension/type with per-category output paths
Rename .aep FoldersRename After Effects project folders by their largest .aep filename

Cleanup Tools

ToolDescription
Empty FoldersFind and delete empty directories
Empty FilesFind zero-byte files
SweepReview empty folders, zero-byte files, and broken shortcuts in one pass; move selected results to undoable UniFile Recovery
Temp / Junk FilesFind .tmp, .bak, Thumbs.db, etc.
Broken FilesDetect corrupt/truncated files
Big FilesFind files above a configurable size threshold
Old DownloadsFind stale files in download folders

Duplicate Finder

  • Progressive hash-based detection: Size > Prefix hash > Suffix hash > Full SHA-256
  • Perceptual image hashing for near-duplicate photos
  • Side-by-side comparison dialog
  • Configurable similarity tolerance

Photo Organization

  • EXIF metadata extraction (date, camera, GPS)
  • Photo map view with geotagged markers (Leaflet)
  • AI event grouping — cluster photos by vision descriptions
  • Face detection and person-based organization (optional)
  • Virtualized thumbnail grid with fixed-size item delegates and visible-item loading; large PC result tables use a lazy QAbstractTableModel/QTableView surface instead of one widget per result
  • Shared thumbnail cache stores encoded previews in a bounded SQLite-indexed filesystem cache with read-only mmap reads and LRU eviction; configure or clear it at Settings → All Settings → System → Thumbnail Cache

Watch Mode

  • Monitor folders and auto-organize new files
  • System tray integration with minimize-to-tray
  • Watch history log with timestamps

Windows Shell Integration

  • unifile install-shell adds "Organize with UniFile" to folder context menus and the Send To menu.
  • Explorer launches include --source <folder> --show-preview, so a shell-opened folder is scanned and the review preview opens automatically.

UI & UX

FeatureDescription
7 Color ThemesSteam Dark, Catppuccin Mocha, OLED Black, GitHub Dark, Nord, Dracula, and WCAG AA High Contrast
Accessibility Font SizeSettings → Accessibility adjusts the base UI size from 8–20 px with a live preview
Screen Reader SupportMajor controls expose Qt accessible names and descriptions for NVDA, JAWS, and Windows Narrator
RTL Layout SupportSettings → Layout Direction supports automatic locale detection plus explicit left-to-right and right-to-left Qt BiDi layouts
Review-First WorkspaceStronger hierarchy, calmer action layout, richer empty states, and clearer trust/status messaging across the main shell
Premium Secondary PanelsTag Library, Media Lookup, and Virtual Library now use clearer section hierarchy, calmer states, and theme-aware premium surfaces
Refined Editor WorkflowsCategory and rule editors now use better summaries, calmer action emphasis, and clearer preview-oriented guidance for power users
Guided Helper DialogsBefore/After, Event Grouping, and rename-source picking now surface stronger summaries and more intentional review-first guidance
Live Theme PreviewSee themes applied instantly before committing
Customizable SidebarDrag section headers to reorder Organize, Tools, Library, Smart Views, and Profile; click a header to collapse it; state persists in QSettings
Before/After PreviewVisual directory tree comparison
Dashboard ChartInteractive category distribution with drag-reassign
File Preview PanelSplit-view with image preview, text excerpt, metadata
Drag & DropDrop folders onto the window to set source
Undo TimelineVisual timeline of all operations with one-click rollback
Trusted Plugin SystemPython plugins are discovered but disabled until explicitly trusted; manifest capabilities and resource limits are reviewed before approval

Safety

  • Preview before apply — full destination tree preview before any files move
  • Protected paths — system folders guarded at scan, apply, and delete layers
  • Safe merge-move — merging into existing folders preserves all files
  • Progressive hash dedup — SHA-256 + perceptual hash prevents overwrites
  • Full undo log — every operation recorded with one-click rollback
  • SQLite transaction replay — apply operations are journaled in WAL mode and can be replayed newest-first by batch or by a configurable last-N count from Undo Timeline
  • Disk space protection — bulk moves and renames stop before execution when the destination would fall below the configurable free-space floor (500 MB by default); configure it in Settings → All Settings → System
  • Checkpointed scans — PC scans persist completed results to SQLite in 500-item batches and resume interrupted work without reclassifying unchanged items
  • Background scan throttle — scans yield between items and pause on known battery power by default; configure pacing and battery behavior in Settings → All Settings → System
  • CSV audit trail — every classification logged with timestamp, method, confidence
  • Confidence tiers — Auto-apply (90%+), Suggest (70–89%), and Skip (<70%) labels; scheduled applies use only the high-confidence tier, with per-profile overrides in Settings → All Settings → Confidence Tiers
  • Crash handler — unhandled exceptions saved to crash log with MessageBox notification
  • Redacted diagnostics export — Settings > Tools creates a support ZIP with paths, emails, and API keys removed
  • Plugin trust gate — local Python plugins are fingerprinted and must be explicitly trusted before execution

Architecture

unifile/
├── __init__.py # Package version
├── __main__.py # Entry point with crash handler
├── bootstrap.py # Optional dependency probes + explicit opt-in installer
├── config.py # Settings, themes, protected paths
├── categories.py # 384+ category definitions
├── classifier.py # 7-level classification engine
├── engine.py # Rule engine, scheduler, templates
├── naming.py # Smart rename logic
├── metadata.py # File metadata extraction
├── thumbnail_cache.py # Shared mmap-backed thumbnail cache with LRU eviction
├── virtualized_view.py # Paged PC results model and thumbnail-grid delegates
├── ollama.py # Ollama LLM integration
├── photos.py # Photo/EXIF/face processing
├── files.py # PC file organizer logic
├── cache.py # Classification cache, undo log
├── models.py # Data models (ScanItem, etc.)
├── workers.py # QThread workers for scanning/applying
├── plugins.py # Plugin system, profiles, presets
├── profiles.py # Scan profile management
├── confidence.py # Per-profile confidence tiers and auto-apply policy
├── cleanup.py # Cleanup scanners (6 types)
├── duplicates.py # Duplicate detection engine
├── widgets.py # Custom Qt widgets (charts, map, preview)
├── main_window.py # Main application window (UniFile class)
├── nexa_backend.py # Nexa SDK AI backend (LLaVA + Llama 3.2)
├── scan_mixin.py # Scan pipeline + auto-tag integration
├── apply_mixin.py # Apply/move operations
├── tagging/
│ ├── db.py # SQLAlchemy engine, Base, PathType
│ ├── models.py # Tag, Entry, Folder, ValueType ORM models
│ └── library.py # TagLibrary CRUD API
├── media/
│ └── providers.py # Video, book, audiobook, and audio APIs + guessit parser
└── dialogs/
├── tag_library.py # Tag Library browser panel
├── media_lookup.py # Media Lookup panel (video/book/audio search)
├── cleanup.py # Cleanup results dialog
├── duplicates.py # Duplicate comparison dialog
├── editors.py # Rules/category editors
├── settings.py # Settings dialog
├── theme.py # Theme preview dialog
└── tools.py # Tool dialogs

Configuration

Ollama Settings

Click Settings > Ollama LLM to configure:

SettingDefaultDescription
URLhttp://localhost:11434Ollama server address
Modelqwen2.5:7bModel for classification
Timeout30sPer-item LLM timeout
Vision batch size32Images grouped into one multimodal request; failed images retry individually

Provider Health

Open Settings > AI & Intelligence > Provider Health to review local request history for configured AI providers. The dashboard shows average latency, error rate, input/output token totals, optional estimated token cost, and a compact latency sparkline. Probe Enabled Providers runs reachability checks in the background; history is stored locally in ai_provider_health.json and errors are redacted before storage.

The AI Providers settings also support native Anthropic Messages and Google Gemini generateContent adapters. They use the configured API key from the operating-system keyring or provider environment variables, send structured-output requests where supported, and keep the existing priority-based fallback chain.

Confidence Tiers

Classification results are labeled by confidence: Auto-apply (90% or higher), Suggest (70–89%), or Skip (below 70%). Scheduled --auto-apply jobs select only Auto-apply rows; interactive scans keep Suggest rows available for review. Open Settings → All Settings → Confidence Tiers to override the thresholds for each built-in scan profile.

Manual category corrections are retained as local few-shot examples and the 10 most recent examples are supplied to later AI classifications as quoted hints. They are included in rules-bundle export/import and never require a remote learning service.

Recommended models:

ModelSizeSpeedAccuracyInstall
qwen2.5:7b4.7 GBMediumBestollama pull qwen2.5:7b
llama3.2:3b2.0 GBFastestGoodollama pull llama3.2:3b
gemma3:4b3.3 GBFastGoodollama pull gemma3:4b
mistral:7b4.1 GBMediumGoodollama pull mistral:7b

Themes

6 dark themes with live preview: Steam Dark (default), Catppuccin Mocha, OLED Black, GitHub Dark, Nord, Dracula.

CLI Usage

python run.py # Launch GUI
python run.py --install-deps # Opt in to dependency bootstrap
python run.py --source "C:/Users/You/Downloads"# Auto-scan a folder
python run.py --profile MyProfile --auto-apply # Automated profile scan
python run.py --dry-run --profile MyProfile # Simulate without moving
python run.py --source DIR --output-json plan.json # Export scan plan as JSON
python -m unifile # Alternative launch
python -m unifile --version # Print version# Headless classification (no GUI, no Qt)
python -m unifile classify path/to/file.pdf --json
python -m unifile classify path/to/folder --json
python -m unifile scan path/to/inbox --json --destination path/to/organized
python -m unifile scan path/to/inbox --apply-rules --destination path/to/organized
python -m unifile scan path/to/inbox --apply-rules --dry-run --output-json plan.json
python -m unifile watch path/to/inbox --apply-rules --destination path/to/organized
python -m unifile watch path/to/inbox --once --include-existing --json
python -m unifile tag --library path/to/unifile-library --query "cat AND outdoor" --json | jq '.entries[]'
python -m unifile report --library path/to/unifile-library --format html --output report.html
python -m unifile report --library path/to/unifile-library --format pdf --output report.pdf
python -m unifile verify path/to/library --json --output health.json
python -m unifile backup path/to/unifile-library --dest path/to/backups --json
python -m unifile backup-verify path/to/backups/unifile-backup-YYYYMMDD-HHMMSS.zip --json
python -m unifile restore path/to/backup.zip path/to/new-library --dry-run --json
python -m unifile restore path/to/backup.zip path/to/new-library --json
python -m unifile nfo generate path/to/movie.mkv --metadata-json metadata.json --json
python -m unifile nfo generate path/to/episode.mkv --kind episode --no-overwrite
# Inventory subcommands (no GUI)
python -m unifile list-profiles --json
python -m unifile list-models --url http://localhost:11434 --json
# Manifest-backed plugin scaffolding (no GUI required)
python -m unifile plugin create --name "My Plugin"
python -m unifile plugin create --name "My Plugin" --output C:/path/to/plugins --json
# Qt-free Flask API (set UNIFILE_API_KEY for non-health routes)
python -m unifile serve --host 127.0.0.1 --port 8787
curl http://127.0.0.1:8787/health

Loopback is the default. Binding serve to a LAN or wildcard address requires --allow-remote and TLS termination at a trusted reverse proxy.

The classify subcommand is safe to use in cron jobs and CI — it loads zero Qt modules and runs purely against the rule-based classifier. scan is also Qt-free and review-first: it prints or exports a versioned move plan, and only --apply-rules performs collision-safe moves. Use --destination to place category folders beneath an explicit root; without it, the command uses the same configured category destinations as the desktop app. Only results at or above the default 80% confidence floor are candidates, and --min-confidence can adjust that threshold. With --dry-run, the exported scan envelope includes a canonical action_plan JSON object. Open that file from Settings → Natural Language Rules → Open JSON plan… to review the source/destination diff and approve the same transactional apply path used by natural-language rules. watch polls recursively without importing Qt, establishes the current folder as the baseline, then classifies files only after their size and modification time remain stable for 500 ms. Use --apply-rules to enable collision-safe moves; without it, arrivals are reported for review. --once --include-existing is useful for scripts, and SIGINT/SIGTERM trigger a bounded graceful flush. tag --query maps bare terms and AND/OR expressions to tag predicates, while selectors such as tag:, ext:, rating:, and field: remain available. The JSON response contains query, normalized_query, count, and entries so it can be piped directly to jq. report exports the category-tag distribution and bounded file inventory from a Tag Library as a self-contained HTML, dependency-free text PDF, or JSON file; use --limit when generating a report for a very large library. backup writes a versioned SQLite snapshot and a SHA-256 manifest. Config JSON is allowlisted and credential-like keys are omitted; secrets remain in the OS keyring or environment. backup-verify --json validates archive member paths, checksums, SQLite integrity, and supported schema metadata without writing. restore --dry-run --json performs the same recovery checks without creating a library. A real restore stages all payloads, upgrades supported older schemas, and creates a pre-restore snapshot before any replacement; failed DB/config writes roll back to that snapshot. The file inspector also shows ranked Related Files matches for shared tags, photographer, date range, or filename pattern. Enter another path in the Link field to save a symmetric manual relationship in UniFile app data.

Headless Docker deployment

docker-compose.yml starts unifile-api and Ollama with separate library, SQLite, and model volumes. Configure a non-empty UNIFILE_API_KEY, SCAN_INTERVAL (seconds, rounded to a cron minute), and OLLAMA_URL before running:

docker compose up -d --build
curl -H "X-API-Key: $UNIFILE_API_KEY" http://127.0.0.1:8787/health

The API exposes authenticated /scan, /tag, /search, /report, and scheduled-job routes plus a small /admin status page. It is review-first: scans return the same versioned JSON plan shape used by --output-json, while tag writes are explicit. /scan accepts the same destination, apply-rules, dry-run, confidence, and limit options as the CLI. Verification is opt-in with {"verify": true}; ordinary scans do not write the file-health ledger. Leave UNIFILE_ALLOW_UNAUTHENTICATED=0 in shared deployments. The container acknowledges its wildcard bind explicitly, but UniFile does not terminate TLS; expose it through an HTTPS reverse proxy and do not publish the Ollama port beyond the trusted host. API request bodies default to 1 MiB, responses to 2 MiB, and each client to 120 requests per 60 seconds. Set MAX_CONTENT_LENGTH, MAX_RESPONSE_BYTES, RATE_LIMIT_REQUESTS, and RATE_LIMIT_WINDOW_SECONDS to tighten those limits.

unifile verify and the authenticated /verify endpoint maintain a library-local .unifile/file_health.json ledger. The first verification establishes SHA-256 baselines; later runs report changed, missing, and unstable files without modifying the source tree. Export a JSON, CSV, or text diff with --output, or schedule a verify job through /jobs (for example 0 3 * * 0 for weekly checks) and set its health_log path for persistent log export. Scheduled jobs use local wall time by default; set the job's timezone to UTC or an IANA name such as America/New_York when the schedule must be portable. Sunday accepts both cron aliases 0 and 7, and restricted day-of-month/day-of-week fields retain standard OR semantics. During daylight saving changes, nonexistent spring-forward wall times are skipped, while ambiguous fall-back times are evaluated once per actual offset-aware occurrence.

Mobile Companion

Start the read-only companion on demand. UniFile defaults to loopback and prints a URL with a short-lived bootstrap fragment. Open it from a phone on the same network and optionally install it as a PWA:

python -m unifile mobile --library /path/to/unifile-library

The bootstrap fragment is exchanged once for a short-lived header-only session, then removed from the browser address/history. API requests reject credential-bearing query URLs; sessions can be rotated or revoked by the client. The companion browses entries, tags, fields, search results, and image previews. Mobile mode does not expose tag/scan/job writes, keeps paths relative to the configured library, and generates thumbnails in memory when Pillow is available. Use --host 127.0.0.1 for local-only access. LAN binding requires an explicit acknowledgement and HTTPS reverse proxy:

python -m unifile mobile --library /path/to/unifile-library \
--host 0.0.0.0 --allow-remote

UNIFILE_MOBILE_HOST, UNIFILE_MOBILE_PORT, and UNIFILE_MOBILE_SESSION_TTL configure the bind and session lifetime.

Collaborative LAN Tagging

For a shared library, initialize a library-scoped administrator and create role-limited tokens locally:

python -m unifile collab init --library /path/to/unifile-library --user-id admin
python -m unifile collab add-user --library /path/to/unifile-library --user-id editor --role editor
python -m unifile collab add-user --library /path/to/unifile-library --user-id viewer --role viewer
python -m unifile serve --library /path/to/unifile-library --collaborative --host 0.0.0.0 --port 8787
python -m unifile collab search http://server:8787 --user viewer --token TOKEN --query "tag:important"
python -m unifile collab tag http://server:8787 --user editor --token TOKEN --entry-id 42 --tag important

The server stores only SHA-256 token hashes in .unifile/collaboration.json. Viewers can search, editors can apply existing tags, and administrators can manage tags, per-tag role ACLs, rules, users, and the audit log. tag:confidential is administrator-only by default. Tag writes use per-field timestamps and return a 409 conflict with the current version when a stale client write loses.

JSON scan plan format

--output-json <path> writes a plan file after the scan completes. Use it to integrate UniFile with other tooling (e.g. feed plans into jq / an approval queue / a CI job):

{
"version": "1",
"timestamp": "2026-04-22T14:30:00",
"source": "C:/Users/You/Downloads",
"mode": "PC File Organizer",
"items": [
{ "name": "invoice.pdf", "src": "...", "dst": "...",
"category": "Documents", "confidence": 90, "method": "extension",
"size": 45312, "selected": true, "status": "Pending" }
]
}

Prerequisites

  • Python 3.10+
  • 8 GB RAM minimum (for Ollama LLM models)
  • ~5 GB disk space for the default qwen2.5:7b model
  • Internet connection only when you choose to install dependencies or download AI models
  • Works without Ollama — falls back to rule-based engine automatically

Install optional dependencies with pip install -e ".[full]". Missing optional packages disable their related feature instead of triggering runtime installs. pyproject.toml is the dependency source of truth; parser-heavy optional packages use audited lower bounds and requirements.txt delegates to the runtime/dev extras.

For local semantic embeddings, install pip install -e ".[onnx]" (or .[onnx-gpu] instead for the CUDA runtime) and place an exported all-MiniLM-L6-v2 ONNX graph (model.onnx) plus tokenizer.json under %APPDATA%\UniFile\models\all-MiniLM-L6-v2 (or select another folder in Semantic Search Settings). Auto mode prefers CUDA when available, falls back to the ONNX CPU provider, and then uses Ollama when no local graph is present.

For optional semantic duplicate detection, use the same ONNX extra and select the Semantic duplicates (CLIP/SigLIP) option in Duplicate Finder. Choose a local exported image graph named model.onnx or vision_model.onnx; it must accept NCHW float32 pixel_values and return one [batch, embedding] vector per image. The default cosine threshold is 0.92 and can be changed in the dialog. UniFile never downloads the image model or requires PyTorch for this feature.

For workflow scripts, open Tools → Plugins, create or edit a script in the embedded editor, validate it, and explicitly choose Trust & Enable before it can run. Scripts live under %APPDATA%\UniFile\plugins and declare Workflow-Hook: on_scan_item or Workflow-Hook: on_apply in their module docstring. The restricted unifile.script API exposes item, classifier, tag_library/library, file_ops, and log; imports and arbitrary standard-library access are rejected, and each hook runs in a bounded child process. Tag commands are applied only when a Tag Library is open; file-operation commands remain disabled by default unless a host supplies explicit allowed roots.

Manifest-backed plugins live in a folder containing plugin.yaml and a Python entrypoint. Manifest v2 declares a stable id, display name/version, a list or mapping of supported hooks to public function names, bounded capabilities, resources (timeout_ms, max_output_bytes, and max_items), and an isolation mode. v2 entrypoints default to a spawn-isolated process; the supervisor bounds JSON payloads, reaps timed-out children, and keeps high-risk hooks out of in-process execution. Tools → Plugins shows the capability/resource contract and the capability diff before Trust & Enable. v1 or unversioned manifests remain readable, but previously trusted packages require an explicit migration approval and high-risk hooks remain disabled unless moved to process isolation. UniFile validates the YAML and entrypoint path before discovery and includes the declared contract in the trust fingerprint. Tools → Plugins → Community Plugin Index reads a bounded HTTPS JSON catalog for browsing only; it never downloads or executes catalog entries.

Outbound provider calls share the bounded unifile.network policy: HTTP(S) URLs are validated, responses are size-limited, only safe methods retry, and diagnostics redact credentials and payloads. Provider health counters expose request, failure, timeout, cancellation, and latency totals without retaining URLs or request bodies. Local Ollama endpoints are explicitly scoped to the configured local service.

Provider integrations use the ProviderAdapter text/vision contract. The built-in Ollama, OpenAI-compatible, Anthropic, and Gemini adapters are selected from the provider configuration, while OfflineProvider (also exported as FakeAIProvider) lets tests and embedded callers inject deterministic responses without network access. ProviderChain(..., adapters={...}) is the supported injection point for custom or offline adapters.

Developer checks:

make dev # install runtime + dev extras
make deps-check # verify pyproject/requirements/bootstrap alignment
make test# deps-check + isolated full pytest; cleans its owned temp tree
python tools/run_tests.py # direct Windows-safe pytest run (forces Qt offscreen)
python -m pytest tests/test_ui_matrix.py -q # 190-case pointer-free Qt theme/font/RTL matrix
make cov # pytest-cov gate: >=60% in classifier, engine, learning, and tag library
make lint # Ruff
make typecheck # strict mypy check for public SDK/core engine contracts
make docs # build SDK API and tutorial docs under build/docs
make translations # extract en.ts, compile maintained .qm catalogs, and validate safety labels
make audit # pip-audit --local
make release-audit # artifact-scoped SBOM/license/vulnerability gate for current ZIP/MSI/SDK outputs
make release-contract # synchronized version surfaces + live CLI/API scan contract
make complexity # bounded orchestration-module complexity budgets
make benchmark-search # reproducible disposable-library search and cancellation benchmark
make build # clean PyInstaller build + frozen --version/classify/GUI smoke + SHA-256
make build-smoke # rerun frozen smoke/checksum against an existing dist/UniFile/UniFile.exe
python tools/build_msi.py # build the unsigned per-machine WiX MSI from dist/UniFile
python tools/build_portable_zip.py # build UniFile-portable-vX.Y.Z.zip
make sdk # build the PyQt-free unifile-sdk wheel under dist/sdk

Translation contributors edit the maintained non-English .ts catalog under unifile/translations/, then run make translations. The command regenerates the English baseline with pylupdate6, compiles .qm files with lrelease, and fails on missing/unfinished critical actions, lost placeholders, missing plural forms, or suspiciously long labels. If Qt tools are not on PATH, set PYLUPDATE6 and LRELEASE to their executable paths before running the command. The package-data and PyInstaller rules include both .ts and .qm files in release artifacts; the offscreen i18n tests also verify Spanish runtime switching, plural output, and RTL direction handling.

The Windows MSI installs the frozen application under Program Files, creates a Start Menu shortcut, adds the install directory to the system PATH, associates .unifile library files, and registers the Explorer context-menu actions. Windows may request administrator approval for this machine-wide installation. It is intentionally unsigned; release signing is outside this project.

The portable ZIP is self-contained and does not install anything. Extract it and launch UniFile.exe; its adjacent portable.flag makes the app store configuration, caches, and databases in unifile-data beside the executable.

When enabled, UniFile checks the GitHub Releases API in a background thread after startup. A newer stable release appears as a Download/Dismiss banner; UniFile never installs updates automatically. Toggle Settings → Check for Updates on Startup to disable the check; the disable_update_check setting is persisted with the rest of the application configuration.

Embedding hosts can use the isolated unifile-sdk project for the core engine without installing PyQt6:

fromunifile_sdkimportClassifier, PatternLearner, SemanticIndex, TagLibrary

Build it locally with make sdk (or python tools/build_sdk.py). The wheel exposes the classification, tag-library, semantic-index, and learning APIs; desktop UI modules are not part of its public surface.

Related Tools

ToolBest For
UniFile (this repo)Everything — AI classification, tag library, media lookup, vision AI, cleanup, duplicates, photo organization
FileOrganizerFocused file organization without the tag library overhead — lighter, simpler, same core classification engine

UniFile is built directly on FileOrganizer's foundation. If you only need folder sorting and cleanup without tag-based library management or media metadata, FileOrganizer is the lighter option.

Roadmap

  • Media Lookup — TMDb/OMDb/TVMaze metadata panel (from mnamer's provider system)
  • Nexa SDK Backend — Alternative AI backend with Llama 3.2 + LLaVA vision (from Local-File-Organizer)
  • Category Presets — Per-directory config, import/export, extension-based presets (from classifier)
  • Search Query Language — Advanced tag search with boolean operators (tag:, ext:, field:, AND/OR/NOT)
  • Preview Panel — Rich file preview with tag overlay, image thumbnails, metadata, and field display

License

MIT License — see LICENSE for details.

About

AI-powered unified file organization platform combining 5 engines: 7-level classification, tag-based library management, media metadata lookup, rule-based sorting, and multi-model LLM integration (Ollama + Nexa SDK). PyQt6 dark-themed desktop app.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages