nautd/webui: scripting, labels, settings, set-location, pause fix

Session checkpoint on webui-plugin:
- engine dump (nautctl dump) + engine endgame integration
- per-file move locations persistence; torrent-level "Set location"
  with reset/keep-relative/leave-separate handling + residual prune
- Lua: naut.get_labels, define_settings/get_setting (script_host struct)
- daemon-owned labels (category+tags) + taxonomy persistence; webui write-through
- fix: pausing a completed/seeding torrent now sticks (stop wins over result)
- automation tab responsive layout; anime_sort label gating + settings

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ookami125 2026-06-21 23:19:41 -04:00
parent 6dc711cf57
commit b633b7d216
40 changed files with 3305 additions and 3267 deletions

View file

@ -200,6 +200,78 @@ nautctl --socket /tmp/nautd.sock add file.torrent /downloads/42
If `torrent_id` is unknown or is being removed, `naut.move_file` raises a
`move_file failed` error in the hook.
### `naut.get_labels(torrent_id)`
Return the torrent's labels as a **plain array (table) of strings**. Labels are
the user-assigned tags/category set in the web UI (or via the `set_labels` RPC);
they are stored on the daemon, persisted across restarts, and surfaced here so a
script can branch on them (e.g. route a finished file by its label).
**Arguments**
| # | Name | Lua type | Notes |
|---|---|---|---|
| 1 | `torrent_id` | `integer` | must be ≥ 0 |
**Return value:** a sequence table of strings, e.g. `{"anime", "airing"}`. The
table is **empty** (`#labels == 0`) when the torrent has no labels or is unknown
— it is never `nil`, so it is always safe to iterate.
```lua
function on_file_complete(event)
local labels = naut.get_labels(event.torrent_id)
for _, label in ipairs(labels) do
if label == "anime" then
naut.move_file(event.torrent_id, event.index,
"/archive/anime/" .. event.path)
return
end
end
end
```
Labels reflect the daemon's current state at call time (re-read on every call),
so a script always sees the latest assignment.
### `naut.define_settings({ ... })`
Declare the user-configurable variables the script reads, so they can be edited
in the web UI (**Automation ▸ Settings**) instead of by hand in the source. Pass
an array of entries:
| Field | Lua type | Meaning |
|---|---|---|
| `key` | `string` | identifier passed to `naut.get_setting` |
| `label` | `string` | human label shown in the form (defaults to `key`) |
| `type` | `string` | `"string"`, `"bool"`, or `"number"` (drives the widget + the type returned by `get_setting`) |
| `default` | string/bool/number | value used until the user sets one |
Call it once at load time (re-declaring replaces the schema). The values the user
saves persist across restarts, independently of the script source.
### `naut.get_setting(key)`
Return the current value of a declared setting: the user-saved value if present,
otherwise the declared default. The result is **typed** per the schema — a Lua
`boolean` for `bool`, a `number` for `number`, a `string` otherwise — or `nil`
if the key was never declared. Re-read it on each use so live edits take effect
without reloading the script.
```lua
naut.define_settings({
{ key = "library_root", label = "Library root", type = "string",
default = "/media/anime" },
{ key = "only_video", label = "Only video files", type = "bool",
default = true },
})
function on_file_complete(event)
if naut.get_setting("only_video") and not is_video(event.path) then return end
local root = naut.get_setting("library_root")
naut.move_file(event.torrent_id, event.index, root .. "/" .. basename(event.path))
end
```
---
## 7. Error handling & observability