npm.io
3.0.0 • Published 16h ago

oleoo

Licence
MIT
Version
3.0.0
Deps
0
Size
74 kB
Vulns
0
Weekly
0
Stars
64

Oleoo

npm version crates.io Go Reference Fixtures License: MIT Ask DeepWiki

ā–’ā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆā–“ ā–“ā–ˆā–ˆā–ˆā–ˆā–ˆ ā–’ā–ˆā–ˆā–ˆā–ˆā–ˆ ā–’ā–ˆā–ˆā–ˆā–ˆā–ˆ ā–’ā–ˆā–ˆā–’ ā–ˆā–ˆā–’ā–“ā–ˆā–ˆā–’ ā–“ā–ˆ ā–€ ā–’ā–ˆā–ˆā–’ ā–ˆā–ˆā–’ā–’ā–ˆā–ˆā–’ ā–ˆā–ˆā–’ ā–’ā–ˆā–ˆā–‘ ā–ˆā–ˆā–’ā–’ā–ˆā–ˆā–‘ ā–’ā–ˆā–ˆā–ˆ ā–’ā–ˆā–ˆā–‘ ā–ˆā–ˆā–’ā–’ā–ˆā–ˆā–‘ ā–ˆā–ˆā–’ ā–’ā–ˆā–ˆ ā–ˆā–ˆā–‘ā–’ā–ˆā–ˆā–‘ ā–’ā–“ā–ˆ ā–„ ā–’ā–ˆā–ˆ ā–ˆā–ˆā–‘ā–’ā–ˆā–ˆ ā–ˆā–ˆā–‘ ā–‘ ā–ˆā–ˆā–ˆā–ˆā–“ā–’ā–‘ā–‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–’ā–‘ā–’ā–ˆā–ˆā–ˆā–ˆā–’ā–‘ ā–ˆā–ˆā–ˆā–ˆā–“ā–’ā–‘ā–‘ ā–ˆā–ˆā–ˆā–ˆā–“ā–’ā–‘ ā–‘ ā–’ā–‘ā–’ā–‘ā–’ā–‘ ā–‘ ā–’ā–‘ā–“ ā–‘ā–‘ā–‘ ā–’ā–‘ ā–‘ā–‘ ā–’ā–‘ā–’ā–‘ā–’ā–‘ ā–‘ ā–’ā–‘ā–’ā–‘ā–’ā–‘

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” | Scene/P2P/Warez release name parser | ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

- Named after an old French warez forum closed in 2008

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-2003 for COLLECTION)
    • Type (only movie or tvshow)
    • 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): If true, throws an error if essential tags (like source, encoding, resolution, or dub) are not found. If false, it attempts to parse as much as possible.
    • flagged (Boolean, default: true): If true, includes detected flags (like EXTENDED, DC, PROPER, etc.) in the generated output string, placed according to standard conventions. If false, these flags are omitted from the generated string (but still present in the flags array of the result).
    • erase (Array<String|RegExp>, default: []): An array of additional regular expression patterns (as strings or RegExp objects) to remove from the input name before 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 and currentYear + 4, and guess falls 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 year is missing, defaults to currentYear, the current system year unless set, as a string like parse returns it.
  • If resolution is missing, infers based on source (e.g., BluRay -> 1080p) or flags (e.g., UHD -> 2160p), otherwise defaults to SD.
  • Rebuilds generated with the guessed year and resolution.
  • Accepts the same options object as parse (though strict is 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:

  1. 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.

  2. Add Your Filenames: Append your movie/TV show filenames (one per line) to the ./tests/fixtures/releases.txt file.

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!).

  1. Check Parsing & Update Tests: Run the interactive script:

    yarn fixtures

    This 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 the accepted.json (correct) and refused.json (incorrect) fixture files.

    yarn test runs the same comparison without prompting: it prints every release whose parsing differs from accepted.json or refused.json, and exits with code 1 if there is any.

  2. 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.json or packages/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! Run yarn test to list every changed entry, then yarn fixtures to review them one by one and confirm that your changes only fix the intended issue and do not negatively affect other entries in accepted.json. A change to rules.json or to the algorithm applies to the three packages: copy rules.json into packages/go (go generate) and packages/rust, which embed it, then run go test ./... in packages/go and cargo test in packages/rust too, and fix SPEC.md when 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 fixtures script when you mark the parse as incorrect, or by running oleoo.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 fixtures and marked the parse as incorrect, include the comment you added explaining the error.

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.

License

MIT

Keywords