Naut/examples
ookami125 50a357968a examples: anime library sorter + pure-Lua Anitomy parser
Add a worked nautd scripting example that files each anime episode into a
media-server-friendly library the moment its file finishes verifying:

    <SORTED_ROOT>/<Title>/Season NN/<Title> - SNNENN.<ext>

- examples/anitomy.lua: a compact, dependency-free reimplementation of Anitomy
  (title/season/episode/release-group/resolution/year) in pure Lua — no
  require/io/os, so it embeds in the sandbox.
- examples/anime_sort.lua: on_file_complete hook that parses the filename and
  calls naut.move_file(); the embedded parser is a verbatim copy of anitomy.lua.
- examples/test_anitomy.lua, test_anime_sort.lua: parser battery + end-to-end
  path-building test with an embedded-vs-module drift guard. Wired into ctest as
  example_anitomy / example_anime_sort when a lua interpreter is present.
- docs: examples/README.md plus pointers from README and docs/scripting.md.

Verified end to end against a live nautd: file_complete -> move_file -> on-disk
relocate into the sorted tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:25:23 -04:00
..
anime_sort.lua examples: anime library sorter + pure-Lua Anitomy parser 2026-06-15 12:25:23 -04:00
anitomy.lua examples: anime library sorter + pure-Lua Anitomy parser 2026-06-15 12:25:23 -04:00
README.md examples: anime library sorter + pure-Lua Anitomy parser 2026-06-15 12:25:23 -04:00
test_anime_sort.lua examples: anime library sorter + pure-Lua Anitomy parser 2026-06-15 12:25:23 -04:00
test_anitomy.lua examples: anime library sorter + pure-Lua Anitomy parser 2026-06-15 12:25:23 -04:00

Examples

Drop-in Lua scripts for the nautd scripting layer (see ../docs/scripting.md).

anime_sort.lua — sort anime into a library as it downloads

Files each episode into a clean, media-server-friendly tree the moment its file finishes verifying — before the rest of the torrent completes — using Naut-Torrent's on_file_complete hook + naut.move_file():

<SORTED_ROOT>/<Anime Title>/Season NN/<Anime Title> - SNNENN.<ext>

Movies (no detectable episode) go to <SORTED_ROOT>/<Title>/<Title> (year).<ext>.

Use it

# 1. edit SORTED_ROOT (and options) at the top of the script
# 2. start the daemon with the script
nautd --socket /tmp/nautd.sock --script examples/anime_sort.lua

# 3. register each torrent's storage so the move can resolve + relocate its files
nautctl --socket /tmp/nautd.sock add_torrent \
  '{"torrent_id":1,"torrent":"show.torrent","root":"/downloads/1"}'

As each file completes you'll see, e.g.:

[anime_sort] [SubsPlease] Frieren - 12 [1080p].mkv  ->  /library/Frieren/Season 01/Frieren - S01E12.mkv

Options (top of the script)

Option Default Effect
SORTED_ROOT /sorted destination library root
ONLY_VIDEO true skip non-video files (subtitles, .nfo, samples) and leave them in place
KEEP_ORIGINAL_NAME false true keeps the original filename instead of Title - SNNENN.ext

anitomy.lua — the anime filename parser

A compact, dependency-free reimplementation of the ideas in Anitomy: given a scene/fansub filename it returns the anime title, season, episode (plus release group, resolution, year, extension). Pure Lua, no require/io/os, so it can be embedded verbatim in a sandboxed script — which is exactly what anime_sort.lua does.

local anitomy = require("anitomy")
local p = anitomy.parse("[Erai-raws] Jujutsu Kaisen 2nd Season - 17 [1080p].mkv")
-- p.title = "Jujutsu Kaisen", p.season = 2, p.episode = 17, p.extension = "mkv"

anime_sort.lua carries a verbatim copy of this parser (the sandbox can't require); test_anime_sort.lua asserts the two stay in agreement.

What it recognizes

  • [Group] Title - 12 [1080p].mkv (dash-delimited episode)
  • Title S04E28, Title S2 - 03, Title 2nd Season - 17, Title Season 2 - 14
  • Title.S03E11.1080p.x264.mkv (dotted delimiters)
  • Title_-_01v2_[720p].mkv (underscores, version suffix)
  • Episode 25, Ep5, #07, absolute numbering (- 500)
  • ranges (01-12), release-group and CRC/resolution stripping, movie years

Heuristic limitations (honest)

  • A title that ends in a number with no episode marker (Mob Psycho 100.mkv) treats the number as the episode — same as Anitomy.
  • Absolute episode numbering reports season 1 (there is no season marker to read).
  • Episode titles after the number (- 05 - The Battle) are dropped.

Tests

lua examples/test_anitomy.lua      # parser battery
lua examples/test_anime_sort.lua   # end-to-end path building + drift guard

These also run under ctest (as example_anitomy / example_anime_sort) when a lua interpreter is on PATH.