Oleoo
- Named after an old French warez forum closed in 2008āāāāāā āāā āāāāāā āāāāāā āāāāāā āāāā āāāāāāā āā ā āāāā āāāāāāā āāā āāāā āāāāāāā āāāā āāāā āāāāāāā āāā āāā āāāāāāā āāā ā āāā āāāāāā āāā ā āāāāāāāāāāāāāāāāāāāāāāā āāāāāāāā āāāāāāā ā āāāāāā ā āāā āāā āā āā āāāāāā ā āāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā | Scene/P2P/Warez release name parser | āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Oleoo parses media release names (movies and TV shows from scene/P2P sources) and extracts structured metadata: title, year, quality, language, and more, across many common (and uncommon) naming conventions.
It exists in JavaScript, Go and Rust. The three packages read the same rules.json, follow the same SPEC.md, and give the same result on the 6697 releases of tests/fixtures/. They share one version number and are released together.
| Language | Package | Directory | Regex engine |
|---|---|---|---|
| JavaScript | oleoo on npm, zero dependency |
packages/js (reference) |
built-in RegExp |
| Go | github.com/thcolin/oleoo/packages/go/v3 |
packages/go |
dlclark/regexp2 |
| Rust | oleoo on crates.io |
packages/rust |
fancy-regex |
Features
- Comprehensive Metadata Extraction identifying:
- Title (with intelligent cleaning and formatting)
- Alternative Title (from parentheses, brackets, or after
-) - Year (including ranges like
2001-2003forCOLLECTION) - Type (only
movieortvshow) - Source (BluRay, WEB-DL, HDTV, CAM, etc.)
- Resolution (1080p, 720p, 4K/2160p, SD, etc.)
- Encoding (x264, x265/HEVC, XviD, etc.)
- Audio details (AC3, DTS, AAC, Atmos, channels like 5.1, etc.)
- Language(s) (MULTi, MULTi-VFF, MULTI-VF2, FRENCH, VOSTFR, VFQ, TRUEFRENCH, ENGLiSH, etc., including combinations)
- Season and Episode number(s) for TV shows (handles various formats like S01E01, S01E01-E03, 1x01)
- Release Group
- Numerous Flags (Extended, Unrated, Director's Cut, Repack, Proper, Collection, Internal, 3D, HDR, Remux, etc.)
- Robust Parsing: Handles various delimiters (
.,,-,_) and common scene/P2P naming patterns. - Flexible Modes:
parse(): Strict parsing based on recognized tags.guess(): Best-effort parsing that fills in missing year/resolution details.
- Standardized Output: Generates a clean, standardized filename based on extracted data.
- Customizable: Allows providing default values and custom patterns to erase before parsing.
Installation
# JavaScript
yarn add oleoo # or: npm install oleoo
# Go (1.26 or later)
go get github.com/thcolin/oleoo/packages/go/v3
# Rust
cargo add oleoo
Basic Usage
JavaScript
import oleoo from 'oleoo'
oleoo.parse('Mr.Robot.S01.PROPER.VOSTFR.720p.WEB-DL.DD5.1.H264-ARK01')
// {
// "original": "Mr.Robot.S01.PROPER.VOSTFR.720p.WEB-DL.DD5.1.H264-ARK01",
// "language": "VOSTFR",
// "languages": ["VOSTFR"],
// "source": "WEB-DL",
// "encoding": "h264",
// "resolution": "720p",
// "dub": null,
// "year": null,
// "flags": ["PROPER", "DD5.1"],
// "season": 1,
// "episode": null,
// "episodes": [],
// "type": "tvshow",
// "group": "ARK01",
// "title": "Mr Robot",
// "generated": "Mr.Robot.S01.PROPER.VOSTFR.720p.WEB-DL.DD5.1.h264-ARK01",
// "score": 6
// }
oleoo.parse('Zero Dark Thirty (2012) [1080p BluRay HDR] [FR(VFF)-EN] [x265 10-bit AC3] [GWEN]')
// {
// "original": "Zero Dark Thirty (2012) [1080p BluRay HDR] [FR(VFF)-EN] [x265 10-bit AC3] [GWEN]",
// "language": "MULTi-VF2",
// "languages": ["TRUEFRENCH", "FRENCH", "ENGLiSH"],
// "source": "BLURAY",
// "encoding": "x265",
// "resolution": "1080p",
// "dub": "AC3",
// "year": "2012",
// "flags": ["10bit", "HDR"],
// "season": null,
// "episode": null,
// "episodes": [],
// "type": "movie",
// "group": "GWEN",
// "title": "Zero Dark Thirty",
// "generated": "Zero.Dark.Thirty.2012.MULTi-VF2.1080p.10bit.BLURAY.HDR.x265.AC3-GWEN",
// "score": 8
// }
oleoo.guess('My Movie (2023)', { defaults: { language: 'ENGLiSH', resolution: 'SD' } })
// {
// "original": "My Movie (2023)",
// "language": "ENGLiSH",
// "languages": [],
// "source": null,
// "encoding": null,
// "resolution": "SD",
// "dub": null,
// "year": "2023",
// "flags": [],
// "season": null,
// "episode": null,
// "episodes": [],
// "type": "movie",
// "group": null,
// "title": "My Movie",
// "generated": "My.Movie.2023.ENGLiSH-NOTEAM",
// "score": 1
// }
try {
oleoo.parse('Not.a.Movie-v28.1-macOS', { strict: true })
} catch (error) {
console.error(error.message)
// "Not.a.Movie-v28.1-macOS" does't follow scene release naming rules
}
Go
Options are functions, Parse and Guess return an error instead of throwing, and a null field is a nil pointer. A Release marshals to the same JSON as the JavaScript result.
import "github.com/thcolin/oleoo/packages/go/v3"
release, err := oleoo.Parse("Mr.Robot.S01.PROPER.VOSTFR.720p.WEB-DL.DD5.1.H264-ARK01")
if err != nil {
log.Fatal(err)
}
fmt.Println(release.Title, *release.Season, *release.Language, *release.Resolution)
// Mr Robot 1 VOSTFR 720p
fmt.Println(release.Generated)
// Mr.Robot.S01.PROPER.VOSTFR.720p.WEB-DL.DD5.1.h264-ARK01
language := "ENGLiSH"
guessed, _ := oleoo.Guess("My Movie (2023)", oleoo.Defaults(oleoo.Release{Language: &language}))
fmt.Println(guessed.Generated)
// My.Movie.2023.ENGLiSH-NOTEAM
_, err = oleoo.Parse("Not.a.Movie-v28.1-macOS", oleoo.Strict(true))
fmt.Println(err)
// "Not.a.Movie-v28.1-macOS" does't follow scene release naming rules
The options are Strict(bool), Flagged(bool), Erase(patterns ...string), Defaults(Release) and CurrentYear(int).
Rust
parse and guess take an Options and return a Result. Release implements serde::Serialize and serializes to the same JSON as the JavaScript result.
use oleoo::{guess, parse, Defaults, Options};
let release = parse("Mr.Robot.S01.PROPER.VOSTFR.720p.WEB-DL.DD5.1.H264-ARK01", &Options::default())?;
println!("{} {:?} {:?} {:?}", release.title, release.season, release.language, release.resolution);
// Mr Robot Some(1) Some("VOSTFR") Some("720p")
println!("{}", release.generated);
// Mr.Robot.S01.PROPER.VOSTFR.720p.WEB-DL.DD5.1.h264-ARK01
let options = Options {
defaults: Defaults { language: Some("ENGLiSH".into()), ..Defaults::default() },
..Options::default()
};
println!("{}", guess("My Movie (2023)", &options)?.generated);
// My.Movie.2023.ENGLiSH-NOTEAM
let strict = Options { strict: true, ..Options::default() };
if let Err(error) = parse("Not.a.Movie-v28.1-macOS", &strict) {
println!("{error}");
// "Not.a.Movie-v28.1-macOS" does't follow scene release naming rules
}
Options holds strict, flagged, erase, defaults and current_year.
API Reference
The reference below is the JavaScript one. Go and Rust take the same options under their own names, and return the same fields.
oleoo.parse(name, [options])
Parses the release name string and returns an object with extracted metadata.
name(String): The release name to parse.options(Object, optional):strict(Boolean, default:false): Iftrue, throws an error if essential tags (like source, encoding, resolution, or dub) are not found. Iffalse, it attempts to parse as much as possible.flagged(Boolean, default:true): Iftrue, includes detected flags (likeEXTENDED,DC,PROPER, etc.) in thegeneratedoutput string, placed according to standard conventions. Iffalse, these flags are omitted from thegeneratedstring (but still present in theflagsarray of the result).erase(Array<String|RegExp>, default:[]): An array of additional regular expression patterns (as strings or RegExp objects) to remove from the inputnamebefore parsing begins. Useful for removing recurring junk specific to your source.defaults(Object, default:{}): An object to provide fallback values for specific fields if they cannot be parsed. Supported keys:language,resolution,year.currentYear(Number, default: the current system year): The year a release is parsed in. A year is accepted between 1901 andcurrentYear + 4, andguessfalls back to it. Set it to get the same result on any date.
oleoo.guess(name, [options])
Similar to parse, but always uses strict: false and attempts to infer missing details:
- If
yearis missing, defaults tocurrentYear, the current system year unless set, as a string likeparsereturns it. - If
resolutionis missing, infers based on source (e.g., BluRay -> 1080p) or flags (e.g., UHD -> 2160p), otherwise defaults toSD. - Rebuilds
generatedwith the guessedyearandresolution. - Accepts the same
optionsobject asparse(thoughstrictis ignored).
Return Value (Object)
Both parse and guess return an object with the following structure:
{
original: string | null, // Input string after basic cleanup (extension removal, erase patterns)
language: string | null, // Primary/combined language code (e.g., "FRENCH", "MULTi", "MULTi-VFF")
languages: Array<string>, // Array of all detected language codes (e.g., ["FRENCH", "ENGLiSH"])
source: string | null, // Standardized source tag (e.g., "BluRay", "WEB-DL", "HDTV")
encoding: string | null, // Standardized encoding tag (e.g., "x264", "x265")
resolution: string | null, // Standardized resolution tag (e.g., "1080p", "720p", "2160p", "SD")
dub: string | null, // Primary detected audio tag (e.g., "AC3", "DTS", "AAC-5.1") - *See Limitations*
year: string | null, // Detected year or year range (e.g., "2023", "2001-2003")
flags: Array<string>, // Array of detected flags (e.g., ["EXTENDED", "PROPER"]), empty if none
season: number | null, // Detected season number for TV shows
episode: string | null, // Formatted episode number(s) for TV shows (e.g., "01", "01-03")
episodes: Array<number|string>, // Array of detected episode numbers (e.g., [1], [1, 2, 3]), or strings for dated episodes (e.g., ["04.02"])
type: 'movie' | 'tvshow', // Detected media type
group: string | null, // Detected release group
title: string | null, // Cleaned and formatted title
alternativeTitle?: string, // Optional: Extracted alternative title
completeTitle?: string, // Optional: Combined title and alternative title
generated: string | null, // Standardized filename generated based on parsed data and `flagged` option
score: number // Parsing score (0-8), higher is better (points for Year, Source, Encoding, Resolution, Dub, Language, Group, Flags)
}
Limitations / Known Issues
While Oleoo aims for broad compatibility, some release name patterns can be challenging:
- Group Detection: Can sometimes be confused by tags, alternative titles in parentheses, or multiple hyphenated parts near the end of the filename.
- Title Boundaries: Occasionally, tags (especially language tags) might be incorrectly included in the extracted title if the structure is unusual. Filenames without clear separators are difficult.
- Complex Structures: Very unconventional filenames, heavy use of nested brackets/parentheses, or ambiguous terms might lead to partial or incorrect parsing.
- Bounds: a name longer than 1024 characters, or an episode range of more than 9999 episodes (
E1-10000), is refused with an error. The longest release of the fixtures holds 205 characters. - Characters above U+FFFF (emoji): the JavaScript and Go packages count them as two UTF-16 code units, the Rust package as one character, so results can differ on such names. See
SPEC.md.
Help Improve Oleoo!
The real world has countless release name variations! Help make Oleoo more robust by testing it with your own filenames. More diverse test cases lead to a better parser for everyone.
Contributing test cases is easy:
Get Ready: Clone the repo (
git clone https://github.com/thcolin/oleoo.git) and install dependencies (cd oleoo && yarn install). The JavaScript package is the reference: fixtures are reviewed against it.Add Your Filenames: Append your movie/TV show filenames (one per line) to the
./tests/fixtures/releases.txtfile.
You can quickly add many names using a command like
ls /path/to/your/movies >> tests/fixtures/releases.txt(but please review and clean up the added list!).
Check Parsing & Update Tests: Run the interactive script:
yarn fixturesThis command parses all names in
releases.txt. For any new or changed results, it will show the parsed output and ask you to confirm if it's correct (y) or incorrect (n). If incorrect, add a comment explaining the problem. This updates theaccepted.json(correct) andrefused.json(incorrect) fixture files.yarn testruns the same comparison without prompting: it prints every release whose parsing differs fromaccepted.jsonorrefused.json, and exits with code 1 if there is any.Share Your Findings (Create a PR):
- The best way to contribute your new test cases is to open a Pull Request on GitHub.
- Please include your updated versions of these three files in the PR:
./tests/fixtures/releases.txt./tests/fixtures/accepted.json./tests/fixtures/refused.json
- If you were also able to fix any parsing issues you found in
rules.jsonorpackages/js/src/index.js, include those changes in the same PR! Important: Modifying the rules (rules.json) or the parsing logic (packages/js/src/index.js) can easily introduce regressions (breaking previously correct parses). This is the main challenge! Runyarn testto list every changed entry, thenyarn fixturesto review them one by one and confirm that your changes only fix the intended issue and do not negatively affect other entries inaccepted.json. A change torules.jsonor to the algorithm applies to the three packages: copyrules.jsonintopackages/go(go generate) andpackages/rust, which embed it, then rungo test ./...inpackages/goandcargo testinpackages/rusttoo, and fixSPEC.mdwhen the algorithm changes.
Reporting Issues without a PR:
Found a filename Oleoo doesn't handle correctly? Your bug reports are valuable! If you don't want to create a Pull Request with updated test fixtures, please open an Issue on the project's GitHub Issues page.
To help fix the issue quickly, please include as much detail as possible in your report:
- Essential: The full original filename that failed to parse correctly.
- Highly Recommended:
- The actual JSON output you got from Oleoo for that filename. You can copy this from the output of the
yarn fixturesscript when you mark the parse as incorrect, or by runningoleoo.parse()directly. Please format it using Markdown code blocks (```json ... ```). - A description of what's wrong with the actual output, or what you expected the output to be (e.g., "Expected group to be 'XYZ' but got 'ABC'", "Year '2023' was missed", "Flag 'REPACK' should be present").
- If you used
yarn fixturesand marked the parse as incorrect, include the comment you added explaining the error.
- The actual JSON output you got from Oleoo for that filename. You can copy this from the output of the
Even just providing the problematic filename is helpful, but more detail makes debugging much faster! Thank you for contributing!
Porting Oleoo
The Go and Rust packages were written from SPEC.md and rules.json alone, and another language can follow the same path. Every pattern and list lives in rules.json, written in a regex dialect that other engines can read, and SPEC.md describes the algorithm step by step, with notes on the engines already used. A port is conformant when it gives the results of tests/fixtures/accepted.json and tests/fixtures/refused.json for every release of tests/fixtures/releases.txt, with currentYear set to 2026.