Movies get their own independent metadata-source setting for the first time (previously hardcoded to TMDB) so the GUI's Fetch Data Settings now has two separate pickers - TV episode source (TMDB/TVmaze/OMDb) and movie source (TMDB/OMDb) - and a batch can freely mix sources across its TV and movie halves. The -rename CLI's --db flag gained omdb alongside tmdb/tvmaze, with movies now looked up online for both tmdb and omdb.
Aurora Curator
A fast, cross-platform media renamer — a ground-up rewrite of FileBot (via its FB-Mod fork), built from scratch as its own project: new stack, new architecture, new branding.
Status: early scaffold, but the core loop works end to end. The naming-expression
engine is ported and tested, the faithfully-recreated two-pane UI is wired to real
native file/folder loading and drag-and-drop, local filename-based season/episode and
movie title/year detection ("Match") is working, real online metadata lookups
("Fetch Data") are working for both TV episodes and movies — including fuzzy title
disambiguation, so a search that returns several similarly-named results doesn't just
take the source's top-ranked hit. TV episodes can resolve against TMDB, TVmaze, or
OMDb, and movies against TMDB or OMDb — each picked independently in Fetch Data
Settings, TMDB stays the default for both, TVmaze needs no API token at all (it has no
movie catalog, so it's TV-only), and TMDB/OMDb each need their own free saved
credential before a batch resolved against them will succeed. The actual on-disk operation is wired up (with a
confirmation step showing the full old-path/new-path plan before anything touches disk,
a per-row ✕ to drop an individual file from that plan at the last step, a Move/Copy/
Symlink selector for what to actually do at each target, and — independently — a Skip/
Auto-number/Override selector for what happens when the computed name already exists:
leave it alone (the original, and still default, behavior), save alongside it as
"Name (1).ext", or replace it outright). It can also organize files into subfolders —
including ones that don't exist yet — when the naming scheme includes a /. Every
completed batch is logged to a history panel that can undo it (moving files back for
Move, or just removing what was created for Copy/Symlink — undoing always uses its own
skip-on-conflict policy regardless of what the original rename used, so an undo can
never overwrite something else as a side effect). A single loaded batch can freely mix
TV episodes and movies
— each file only fills in the fields that apply to it. "Match" also parses release-tag
text straight out of the filename — resolution, source (BluRay/HDTV/WEB-DL/…),
video/audio codec, release group — so naming schemes can reference
{resolution}/{source}/{video codec}/{audio codec}/{release group} (or
FileBot's own {vf}/{source}/{vc}/{ac}/{group}) without a media prober; the
rest of FileBot's full media-metadata bindings (real probed video/audio stream info)
are not yet ported. The Align Up/Down buttons reorder the (shared) file list — the two
panes always stay positionally paired — and up to 9 naming schemes can be saved as
presets, recalled with a click or a 1-9 keypress (persisted locally, same pattern as
Settings/History). The app can also be driven entirely from the command line —
aurora-curator -rename --format "<naming scheme>" [options] <file-or-folder>... — for
scripting batch renames without opening the window at all; see "Command-line usage"
below. See "Porting roadmap" below.
The naming scheme is fully user-editable from the app (the "Naming Scheme" button next
to the New Names list) — the default is {show name} - {season number}x{episode number} - {episode name}, but it's just a starting point, not a fixed template.
Plain, human-readable, multi-word field names like {show name} work directly (no
Rhai knowledge needed), FileBot's own short field names ({n}, {s00e00}, {t})
still work side by side with them, and real Rhai script is available for anyone who
wants it (e.g. {show name.to_upper()}). See "Naming scheme syntax" below for how
that's implemented.
Why a rewrite, and why this stack
The original is a ~20k+ line Java/Swing app built with Ant. The goals were: faster startup and UI, faster matching/renaming, a codebase that's actually pleasant to work in, and a genuine rebrand — not a coat of paint.
Aurora Curator is Tauri: a Rust core (crates/aurora-curator-core, src-tauri) plus a
TypeScript/React frontend (src), compiled to a small native binary per platform
(Windows/macOS/Linux) with a system webview instead of shipping a bundled browser
engine. That's the concrete "faster" win over both the original JVM/Swing app and
an Electron-style rewrite: quick cold start, small binary, real OS-native windowing.
An earlier attempt used Kotlin + Compose Multiplatform to stay on the JVM and reuse FileBot's Java logic directly. That was abandoned (nothing was salvaged into this repo) after discovering the sandbox this was originally built in blocks Maven Central and the Gradle Plugin Portal, making it impossible to compile or test. Tauri was chosen as the replacement because both halves — Cargo via crates.io, npm via the public npm registry — are reachable, so every piece described as "done" below is actually compiled/tested, not just written and hoped for.
Compatibility notes
Goal: keep CLI flags, config file locations, and the outer { } naming-scheme
template syntax compatible with FileBot conventions, so existing automation mostly
keeps working.
One deliberate gap: FileBot's { } expressions ran arbitrary Groovy code
(JVM-only). Aurora Curator's Rust core has no Groovy engine, so expressions are evaluated with
Rhai instead. Simple bindings ({n}, {s00e00}, {t}) are
identical. More complex chained-method expressions will need small syntax tweaks
(e.g. Groovy's .toUpperCase() → Rhai's .to_upper()) — Rhai already ships most of the
common string/array methods FileBot's expression helpers wrapped, so most patterns
translate directly.
Naming scheme syntax
Every { } token in an expression is resolved in this order
(crates/aurora-curator-core/src/format/expression.rs::compile_scriptlet):
- A single bare identifier (
{n},{season}) is a direct binding lookup — the fast path, and how FileBot's own short field names keep working unmodified. - Otherwise it's compiled as Rhai script (
{n.to_upper()},{n + " " + t}) — real code, with every current binding available as a script variable. - If it's neither a valid identifier nor valid Rhai, but it still only contains
letters, digits, underscores and spaces (e.g.
{show name},{episode number}), it falls back to a literal multi-word binding key instead of raising a syntax error — this is what lets a human-typed field name with spaces work as a plain lookup without anyone needing to know Rhai. Anything that isn't a plausible field name (stray punctuation, unmatched brackets, etc.) still surfaces as a real syntax error at that point, exactly as before.
An undefined binding contributes an empty string rather than failing outright (so
{n}{missing} still renders); the overall expression only errors if the total
rendered output would be empty.
/ between two { } scriptlets (or anywhere in the literal text) needs no special
handling in the expression engine at all — it's just ordinary literal text, the same as
a space or a dash. It only becomes meaningful one layer up, at rename time (see
"Organizing into subfolders" below): a scheme like {show name}/{episode name} renders
to a name containing a real /, and the rename engine treats that as "put this file in
a subfolder" rather than rejecting it.
Organizing into subfolders
A naming scheme that includes / (e.g. {show name}/Season {season number}/{episode number} - {episode name}) moves the file into that subfolder as part of renaming,
creating any missing intermediate folders automatically
(crates/aurora-curator-core/src/rename/mod.rs::rename_one → fs::create_dir_all). This is
still always scoped to the file's own directory tree — the target is resolved relative
to the source file's parent directory, never as an absolute path, and every path
component is validated (validate_relative_target) to reject empty segments (a
leading, trailing, or doubled / — which also catches an absolute-looking
/etc/passwd-style path), ./.. segments, and backslashes (Aurora Curator always uses /
for the naming scheme's folder separator regardless of OS). If the resolved target
already exists and isn't the same underlying file, what happens next is up to the
chosen conflict policy — Skip (default), Auto-number, or Override; see item 6 of the
porting roadmap below for the full behavior of each.
Project layout
crates/aurora-curator-core/ Pure Rust core: naming-expression engine (ported), local
filename-based season/episode *and movie title/year* matching
(ported, `matching/`), plus filename-based release-tag parsing in
the same module (`guess_release_info` — resolution, source, video/
audio codec, release group), text-similarity scoring for
disambiguating search results (`similarity/`, partial
`EpisodeMetrics` port), three web metadata clients — `web/tmdb.rs`
(TV search + episode lookup and movie search + lookup),
`web/tvmaze.rs` (TV search + episode lookup only — TVmaze has no
movie catalog, and no API key/auth at all), and `web/omdb.rs` (TV
*and* movie search + lookup, single flat root-URL REST API, plain
`apikey` query-param auth rather than TMDB's Bearer token) — all
three disambiguated via
`similarity/` and tested against a mocked HTTP server via
`mockito`, the on-disk
rename engine (`rename/` — Move/Copy/Symlink actions,
Skip/Auto-number/Override conflict policies, can also organize
into (auto-created) subfolders when
the naming scheme includes `/`), a rename history/undo log
(`history/`, a partial `HistoryDialog` port, undo-aware of which
action produced each batch), and pure `-rename` command-line
argument parsing/file expansion (`cli/` — no Tauri or network
dependency at all, see "Command-line usage" below). Not yet
ported: full media metadata bindings, other web datasource
clients (TheTVDB/AniDB/OpenSubtitles/etc). No Tauri dependency, so
it builds and unit-tests standalone.
src-tauri/ Tauri app shell: exposes aurora-curator-core as commands to the frontend
(naming-scheme rendering, filename matching for both TV and
movies, metadata fetch — `metadata.rs` for TV episodes (against
whichever of TMDB/TVmaze/OMDb is the stored TV source) plus a
movie-fetch command alongside it (against whichever of TMDB/OMDb
is the stored movie source — independent of the TV source)
— executing a rename
plan, native file/folder dialogs, drag-and-drop expansion), plus
`settings.rs` (TMDB token, OMDb API key, and which TV *and* movie
metadata source Fetch Data uses — two independent settings),
`history.rs` (rename history/undo
log), `presets.rs` (up to 9 saved naming schemes) — all three
stored as JSON in the OS app-config directory — `cli.rs` (the
`-rename` command-line mode's orchestration: builds and executes a
rename plan against `aurora-curator-core::cli`'s parsed arguments,
with no Tauri `App`/window ever created for it — `main.rs` branches
to it before the GUI is touched at all), and app
metadata/icons/window config (tauri.conf.json).
src/ React + TypeScript UI (Vite). Faithful recreation of the original
two-pane rename screen (Aurora theme) wired to real file loading,
drag-and-drop, live naming-scheme preview, local TV/movie
matching, metadata fetch for both (behind a Fetch Data Settings
modal holding independent TV-source (TMDB/TVmaze/OMDb) and
movie-source (TMDB/OMDb) pickers plus the TMDB token and OMDb key
inputs), a
confirm-before-touching-disk modal for the actual
rename, a History panel that can undo a past rename, Align Up/Down
list reordering, and a Presets panel (plus `1`-`9` hotkeys) for
saved naming schemes.
scripts/gen_icons.py Regenerates src-tauri/icons/ from the real logo mark
(`scripts/logo/icon-source.png`, cropped from the supplied
lockup) — replace that source image to change the look.
Building
Prerequisites: Rust (stable) + Cargo, Node.js 18+, and Tauri's platform prerequisites
(see https://v2.tauri.app/start/prerequisites/ — on Linux this means
libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev and a C
toolchain; macOS/Windows need Xcode / the VS Build Tools + WebView2 respectively).
# Rust core (no UI deps needed):
cargo test -p aurora-curator-core
# Full app in dev mode (opens a native window):
npm install
npm run tauri dev
# Production build (installer/bundle for your current OS):
npm run tauri build
Verified in the environment this was built in: cargo test --workspace (all 128
passing, including the TMDB, TVmaze, and OMDb client tests — those run against a local
mock HTTP server via mockito, no live network or API key needed to build/test), cargo check -p aurora-curator (compiles clean against real Tauri + GTK/WebKit system
libraries), and npm run build (TypeScript type-checks, Vite bundles). The actual GUI
window has not been visually verified anywhere yet — that sandbox has no display.
Run npm run tauri dev on your machine to see and click through it for the first time.
Setting up metadata sources (for "Fetch Data")
Everything else in the app works fully offline; "Fetch Data" is the one feature that talks to the network, and it's the only one that needs any setup at all — and even that setup is optional for TV.
TV episodes and movies each have their own independent source picker in the app (click the key icon, "Fetch Data Settings", next to Fetch Data) — a batch can freely mix TV and movie files resolved against two different sources at once:
- TVmaze — TV only (it has no movie catalog). Free, no account or API token needed at all. Just pick it and go.
- TMDB — the default for both TV and movies. Needs a
free account:
- Create a TMDB account, then go to Settings → API in your account and generate an API Read Access Token (not the older numeric "API Key" — Aurora Curator uses the Bearer token method TMDB documents as the default).
- In the app, open "Fetch Data Settings" and paste the token in. It's saved locally
in the OS app-config directory and only ever sent to
api.themoviedb.org.
- OMDb — TV and movies, an alternative to TMDB for
either or both. Needs a free API key:
- Request a free key at https://www.omdbapi.com/apikey.aspx (OMDb emails it to you).
- In the app, open "Fetch Data Settings" and paste the key into the OMDb field. It's
saved locally in the OS app-config directory and only ever sent to
www.omdbapi.com.
A batch of TV-only files can run entirely on TVmaze with no credential configured at all. Otherwise, whichever of TMDB/OMDb is actually selected — for TV, for movies, or both — needs its own credential saved first; picking TMDB for TV and OMDb for movies (or any other mix) only needs the two credentials that combination actually uses. Note OMDb returns a normal-looking (HTTP 200) "no results" response for both a genuine no-match and an invalid API key, so a bad key currently surfaces as "not found" rather than a clearer credential error — a known OMDb API limitation, not something Aurora Curator can distinguish from its response alone.
Command-line usage
The same binary that opens the GUI can also run a batch rename straight from a script or
terminal — a port of FileBot's net.filebot.cli flag surface (-rename, --format,
--db, --action, --conflict), with a couple of deliberate differences noted below.
Passing -rename (or -h/--help) on the command line skips the window entirely:
aurora-curator -rename --format "{show name} - {season number}x{episode number} - {episode name}" \
--action move --conflict skip ~/Downloads/some-show/
--format "<naming scheme>"(required) — the same expression syntax as the GUI's Naming Scheme field: FileBot short names ({n},{s00e00},{t}), human-readable aliases ({show name},{episode name},{movie name},{resolution},{video codec},{release group}, …), or real Rhai script.{name}/{ext}fall back to the file's own current filename/extension when nothing else matched it.--db <tmdb|tvmaze|omdb>(optional) — resolves TV episodes against that online source before rendering names, the command-line equivalent of clicking "Fetch Data" after Match. Movies are also looked up online whenever this istmdboromdb— TVmaze has no movie catalog, so--db tvmazeonly affects the TV half of a batch — and with no--dbat all every file uses local filename matching only, same as Match without Fetch Data in the GUI.--db tmdb/--db omdbeach need the matching token/key that's already been saved through the app's own Fetch Data Settings at least once — the CLI never accepts an API token or key as an argument, so there's nothing secret to leak into your shell history or a script.--action <move|copy|symlink|test>(defaultmove) — what to do at each computed target;testprints the full plan and touches nothing on disk, useful for checking a naming scheme before committing to it.--conflict <skip|auto-number|override>(defaultskip,autonumber/autoalso accepted) — what happens when the computed target already exists.-non-strict— accepted for compatibility with existing FileBot scripts, but is a genuine no-op here: the local matcher has no confidence score to loosen, and online disambiguation (similarity::best_title_match) always runs regardless of this flag.- Every remaining argument is treated as an input file or folder; folders are expanded recursively (hidden files/dotfolders skipped), matching exactly what the GUI's own "Select Folder…" and drag-and-drop do.
Deliberately not ported: FileBot's --output <dir> flag. The rename engine is
scoped, as a safety invariant, to never move a file outside its own source directory
tree (see "Organizing into subfolders" above) — a naming scheme's own / already covers
organizing into a subfolder, and relaxing that invariant just for --output wasn't
judged worth it.
Porting roadmap (not yet done)
Roughly in priority order — each references the original FileBot source file it's ported from, for anyone picking this up:
- Full naming-scheme bindings —
net.filebot.format.MediaBindingBean(1354 lines): all the{n},{s00e00},{airdate},{vf}(video format), etc. binding properties. A filename-tag-based subset is now ported and wired to "Match" (guess_release_infoincrates/aurora-curator-core/src/matching):{vf}/{resolution},{source},{vc}/{video codec},{ac}/{audio codec},{group}/{release group}. What's still missing is everything MediaBindingBean derives from actually probing the media file (real video/audio stream resolution, duration, bitrate, frame rate, file size/hash, etc.) — Aurora Curator ships no ffprobe/MediaInfo dependency, so those bindings only work when a release name happens to carry the info as text. - String/expression helper methods —
net.filebot.format.ExpressionFormatMethods(636 lines): FileBot-specific Groovy string extensions like.pad(),.sortName(). Only a small subset (any,all_of,concat) is ported so far (crates/aurora-curator-core/src/format/functions.rs); Rhai's built-ins cover some of the rest already. - Matching/similarity engine —
net.filebot.similarity.*. Local filename-based season/episode detection is done (crates/aurora-curator-core/src/matching, ported from the pattern-matching half ofSeasonEpisodeMatcher.java, adapted to Rust'sregexcrate which lacks lookahead/lookbehind — wired to the "Match" button), and so is a local movie title/year heuristic (guess_moviein the same module) used as a fallback whenever a filename carries no season/episode marker — a release name is either a TV episode or a movie, never both. Title disambiguation is also done (crates/aurora-curator-core/src/similarity, a partial port of the scoring idea behindEpisodeMetrics.java's fuzzy title-matching cascade — Sorensen-Dice similarity over normalized-title character bigrams), shared by both the TV and movie TMDB lookups: "Fetch Data" picks whichever TMDB search result is textually closest to the local guess rather than just trusting TMDB's relevance/popularity-ranked first hit, and reports "not found" instead of guessing when nothing in the results is a plausible match at all.DateMatcher(numbering/air-date-based identification, the other half of the originalEpisodeMetrics) is not ported — Aurora Curator doesn't do date-based episode identification. - Media detection —
net.filebot.media.MediaDetection(1356 lines): ties file-name parsing + matching together to auto-identify what a file is. - Web datasource clients —
net.filebot.web.*(~40 files): TheMovieDB, TheTVDB, AniDB, OMDb, TVmaze, OpenSubtitles, etc. Three are done: TMDB, for both TV and movies (crates/aurora-curator-core/src/web/tmdb.rs,reqwest+serde_json, Bearer-token auth) — search-by-name plus per-season episode lookup for TV, search-by-title (optionally narrowed by release year via TMDB's ownprimary_release_yearfilter) for movies — TVmaze, for TV only (crates/aurora-curator-core/src/web/tvmaze.rs, samereqwest+serde_jsonshape, no auth at all — TVmaze has no movie catalog to search, and its per-show episode endpoint returns every season in one flat list rather than TMDB's per-season lookup, so the client fetches once and filters locally, skipping any unnumbered/special episodes rather than panicking on them) — and OMDb, for both TV and movies (crates/aurora-curator-core/src/web/omdb.rs, samereqwest+serde_jsonshape but a single flat root-URL REST API rather than TMDB's per-resource paths, plainapikeyquery-param auth rather than a Bearer token, and human-text release dates ("16 Jul 2010"/"N/A") normalized to ISO form locally; a movie search result only carries a bare year, solookup_moviefollows up with a by-ID detail request to get the actual release date). All three are wired to "Fetch Data" — independent "TV episode source" (TMDB/TVmaze/OMDb) and "Movie source" (TMDB/OMDb) pickers in Fetch Data Settings choose which source a batch's TV and movie items resolve against respectively (TMDB stays the default for both; a batch can mix sources across its TV and movie halves) — disambiguating multi-result searches via the similarity-scoring pass described in item 3 above rather than blindly trusting any source's top-ranked hit. All other sources (TheTVDB, AniDB, OpenSubtitles) are not yet started. - Rename engine + CLI —
net.filebot.cli.CmdlineOperations(1235 lines) andArgumentBean(415 lines, the full flag surface:-rename,--db,--format,--action,--conflict,-get-subtitles,-script, etc.). The rename operation is done (crates/aurora-curator-core/src/rename, wired to the UI's Rename button behind a confirmation step) including themove-into-subfolder--actionvariant — a naming scheme like{n}/{s00e00}organizes files into subfolders, creating any missing folders automatically, always scoped to the file's own directory tree. Thecopy/symlink--actionvariants are done too — an action selector in the confirmation modal picks Move/Copy/Symlink for the whole batch; Copy and Symlink both leave the source file untouched and produce a new file/link at the computed target, and both are undoable from the History panel the same as Move (undo for Copy/Symlink just removes what was created, since there's nothing to move back). The--conflictvariants are done too — a second selector in the same modal picks Skip (default, leaves the conflicting file alone), Auto-number ("Name (1).ext", counting up to the first free slot), or Override (replaces the conflicting file outright — the one irreversible option, called out with red styling in the UI; undoing that batch later restores the source, never what got overwritten, since undo always uses its own fixed skip-on-conflict policy no matter which policy the original rename used). The CLI flag surface is done too —-rename/--format/--db/--action/--conflict/-non-strict/-h(crates/aurora-curator-core/src/clifor pure argument parsing and directory expansion,src-tauri/src/cli.rsfor the orchestration — local matching, optional TMDB/TVmaze/OMDb lookup, and the actual rename, all without ever constructing a TauriApp); see "Command-line usage" above.--output <dir>and-get-subtitles/-script/the rest of FileBot's non-rename CLI modes are not ported —-get- subtitlesand-scripthave no analog yet since subtitle-fetching and a script-mode runtime aren't built at all. - The rest of the UI — the Rename panel (file loading, drag-drop, live preview,
local TV/movie matching, confirmed rename with per-item exclude, rename history/undo,
Align Up/Down reordering, naming-scheme Presets with
1-9hotkeys) is built; the other five sidebar panels (Episodes/Subtitles/SFV/Filter/List) are still stub screens. Align Up/Down is button-based (swaps the selected file with its neighbor), not the original's drag-and-drop-within-a-list interaction. - Undo/history for renames — done, as a flat JSON-backed undo stack rather than a
full port of
net.filebot.HistoryDialog's session/element log (crates/aurora-curator-core/src/history,src-tauri/src/history.rs). Every successful rename is recorded to ahistory.jsonin the OS app-config directory (same location assettings.json); the History panel lists past batches and can revert one back to its original paths, skipping (never overwriting) any file whose original path now has something else at it. Capped at the 50 most recent batches. - Packaging — code signing, auto-update, installers per platform via
npm run tauri build.