Releases: AlyShmahell/matchora
Release list
v0.0.4
Changelog
Matchora 0.0.4
- No local LLM, embedder, or llama.cpp. 🎉
- Scan grouping and candidate ranking are SequenceMatcher plus YAML.
- The release tarball is binary,
config/,public/, andLICENSE.
Scan grouping
Scan no longer calls an instruct model (prompt.md is gone). Each library child is classified from disk: seasons and extras fold into the walk-root title; named sibling folders become their own titles; leftover loose videos cluster with SequenceMatcher.
Word lists live under group: in config/default.yaml (video_ext, extras, release, kinds, seq_threshold). Years are taken only from a (YYYY) suffix or a trailing year token already on the name.
Ranking
Jobs record ranker: seq. Score is SequenceMatcher on normalized titles only:
- Exact title match is
1.0. - Otherwise title ratio, with a second try against
title + year(helpsGirls (2012)vsGirls). - Matching year still adds
0.15. - Synopsis is not used. A one-word query whose word appeared in a synopsis no longer scores
1.0(Wednesday vs Wednesday Club, Limitless vs Limitless with Chris Hemsworth, and similar).
True same-name collisions with no job year (Dark Matter 2015 vs 2024) stay manual.
Ingest
Unknown CSV headers are mapped with ingest.aliases, then SequenceMatcher against canonical fields when the ratio is at least group.seq_threshold. The instruct ingest.md path is gone.
Config
version: in config/default.yaml is the only version string (0.0.4). There is no root VERSION file and no compile-time version.
Grouping lists and match/http numbers come only from YAML. Missing or invalid required keys fail start. POST /v1/config that would clear a required list is 400 and is not written.
Removed: llama: block, ranker: embed, GGUF URLs, --prepare, and the llama admin form.
Packaging and docs
- One archive:
matchora-<version>-linux-amd64.tar.gz(no-llamatarball, no vendored llama.cpp). - Packaging fails if
versionis missing from the shipped YAML. - README is written for the release tarball: unpack,
./matchora, listen onhttp.addr.
Upgrade notes
- Delete any leftover
vendor/llama.cpptree; it is unused. - Overlay
data/config.yamlmust not rely onllama:keys. - Rematch existing sessions if you want title-only ranking on old jobs.
- Scan jobs still have empty
type. Jikan (types: [anime]) does not run on untyped scan unless the TVMaze pass is empty. English folder titles that TVMaze stores under a Japanese name (e.g. Erased → Boku Dake ga Inai Machi) can sit below other exact-title hits; the admin UI shows the top 5 candidates.
v0.0.3
Changelog
0.0.3 - 2026-08-29
Scan/ingest are isolated per request. Title-finding is back to the August 17 loop (one child, one instruct call, match kicked immediately), plus a title folder path.
Breaking
POST /v1/scanreturns202 {"session","files"}.POST /v1/ingestreturns202 {"session","jobs"}(jobs are no longer a bare array).GET/DELETE /v1/jobs,GET /v1/scan/status,GET /v1/match/log,POST /v1/match,POST /v1/retry, select/catalog-on-job, and catalog reads require?session=. Missing id is400; unknown or expired is404.- Jobs persist as
{data_dir}/jobs-{session}.json, notjobs.json. - Scan no longer attaches video files to jobs or maps them onto catalog episodes (that 0.0.2 experiment is reverted).
Changed (title-finding realignment)
Restored the August 17 grouping engine (bb1cdb1 / 0692b2d), not the later Units / batched Group / LLM SxxExx experiments.
- Immediate children only (
scan.Children). Onematch.Groupper child. Compact listing, ~5 filename samples,max_tokens: 256. Append+Kickinside the child loop so matching overlaps later grouping.- Prompt is the August 17 rules again (folder as title, one object per series, no SxxExx, extras are not shows).
- Added
Path:on the listing and{title,year,path}so a mixed folder can use{Path}/{child}(movie/OVA next to seasons). Parent:on folder listings; fallback title prefers Parent whenFolder:is Season N.- Same title from two children is still two jobs (no end-of-scan merge).
Added
- Server-minted session ids:
<UTC 20060102T150405Z>-<16 hex>. GET /v1/sessions— unexpired ids, newest first.session.ttl_ms(default and max 24h). Expired job files are purged; catalog on disk is not.GET /v1/catalog?session=lists only titles that session matched.DELETE /v1/catalogandDELETE /v1/catalog/{provider}/{id}— no session;409if any live session still has that match.- Admin UI stores the last session and sends
?session=on job/scan/catalog calls.
Fixed
- Image
Fetchno longer shares the provider API pacer (tvmaze/posterno longer queues for minutes behind search). - Admin scan poller no longer dies on the previous session while
POST /v1/scanis in flight; poller restarts aftersetSession. POST /v1/scandoes not walk the whole tree before202;filesis the sum of each child’s video count.ListVideos/ samples follow symlink library roots.
Removed
Units/FolderClass/ batchedGroupUnit/ LLM file S/E labels.- Unused
MergeJobs/ emptyGrouped.Filesleftovers.
v0.0.2
0.0.2
Scan jobs now carry video files and map them onto catalog episodes. Grouping stays one instruct call per library child (prompt.md); season/episode labels are only taken from explicit SxxExx in the filename.
Added
- Job
files(path, optionalseason/episode) filled during scan from videos under each child. - Catalog episodes get
path/pathswhen file S/E matches the catalog numbers (01=1). - Same
title+yearin one scan merge into one job (Season 1 + Season 2 of one show), withpathset to the show folder. - Season-folder listings include
Parent:so a failed group falls back to the show name, notSeason 1. - Vendored
llama-serveris started withPR_SET_PDEATHSIGSIGTERM so it exits if Matchora’s PID disappears. CleanStop()still SIGTERMs the process group.
Changed
prompt.mdmay returnfileson each show. Paths must be copied from the listing; do not guess S/E from01.mkvor aSeason Nfolder.browse_root: /treats absolute paths as inside the root.
Notes
- Ingest is unchanged (
ingest.md). Catalog join is still numeric, not a second LLM call. share/config/default.yamlversionis still0.0.1; bump it if the shipped YAML should matchVERSION.
v0.0.1
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
[0.0.1] - 2026-08-28
First public release. Matchora is a Go service that ingests title rows or scans a
video library, searches YAML-defined metadata APIs, and ranks candidates with a
local llama.cpp embedder or instruct model.
Added
Matching and providers
- Generic GET + JSON-path provider engine. Provider names are not hardcoded in Go.
- Shipped providers: TVMaze, Jikan v4, OMDb (movies, keyed), TMDB movie and TMDB TV (keyed).
- Type allowlists so a typed job only calls providers that list that type.
- Fast pass plus deferred providers (
defer: true) when hit count or best score is too low. - Parallel provider GETs per title; per-job HTTP clocks so a slow deferred call does not stall the batch.
- Per-provider pacing (
min_interval_ms), retries, per-attempt timeouts, capped exponential backoff, andRetry-After. - Provider cooldown after consecutive GET failures (jittered skip, reset on success).
- Ranking: MiniLM embeddings (cosine) or instruct chat; lexical token overlap + year bonus if the embed server is down.
- Auto-
matchedvsmanualvsunmatchedfrommin_score/min_margin; user confirm via select. - Season/episode catalog fetch for TV providers (TVMaze, TMDB TV), including a catalog-only request that does not change the match.
- Optional provider
detailGET (OMDb plot) and poster downloads. skip_episode_posterson ingest, scan, rematch, retry, select, and catalog.
Ingest and scan
POST /v1/ingestfor CSV (header) or JSON arrays:title,year,type,season,episode,imdb.- CSV alias map (
ingest.aliases) and type rewrite (episode/season→tv). - Instruct fallback to map unknown CSV columns using
config/ingest.md. POST /v1/scanlists videos underbrowse_root, groups dirty names with a local instruct model (config/prompt.md), then matches in the worker.- Scan grouping progress via
GET /v1/scan/status. - Expected Plex / Jellyfin / Infuse trees (movies vs series roots, optional
{tmdb-…}/{imdb-tt…}ids).
Catalog
- Matched, selected, and cataloged titles written under
{data_dir}/catalogas[uniqueid-id] Title (Year)/trees. tvshow.nfo/movie.nfo, season folders, episode.nfo, and downloaded posters.- Distinct
uniqueidslugs so TMDB movie vs TV ids cannot collide (tmdb-movie,tmdb-tv, OMDbimdb). GET /v1/catalogandGET /v1/catalog/{provider}/{id}; posters at/poster.jpg(and season/episode variants).- Clearing jobs does not delete the catalog tree.
Runtime and llama.cpp
- HTTP admin server on
:7680(User-Agent: matchora/{version}). - Writable
{exeDir}/data:jobs.json, optionalconfig.yamloverlay,secrets(YAML map of API keys). GET/POST /v1/secretsreport which key slots are set and merge the secrets file (values never returned). Slots are provider API keys only.GET/POST /v1/configread and deep-merge{data_dir}/config.yaml(same shape asdefault.yaml). Never includes secrets.- Listen address is
llama.host/llama.port(defaults127.0.0.1/8080);llama.base_urlis derived ashttp://{host}:{port}/v1. - On start, probe that URL. A healthy listener is left alone. If it is down, install llama.cpp + GGUFs into
{exeDir}/vendor/llama.cppand spawn one llama-server on127.0.0.1at that port (embed + optional instruct). Distinctllm_base_url(e.g. a stub) is left alone. - After a successful secrets or config POST, return the JSON body, stop a spawned llama-server, and re-exec so the next
Load/llama.Startapplies the files. Setters write files only. --prepareruns that install, verifies models, stops the spawned server, and exits.- Dist is binary +
config/+public/only; llama.cpp is not in the slim tree.
Admin console
- Verification UI at
/(folder picker viaGET /v1/fs, ingest upload, scan, job cards). - Secrets panel for provider keys and llama panel for host/port; both wait for
/healthafter the process restart. - Live match wait log, sticky status chips, score heatmap, retry errors/unmatched, clear jobs.
- Manual candidate select and per-candidate seasons catalog.
- Skip-episode-posters checkbox persisted in
localStorage.
HTTP API
GET /,/health,/v1/fs,/v1/jobs,/v1/match/log,/v1/scan/status,/v1/catalog,/v1/catalog/{provider}/{id}GET/POST /v1/secrets,GET/POST /v1/configPOST /v1/ingest,/v1/scan,/v1/match,/v1/retry,/v1/jobs/{id}/select,/v1/jobs/{id}/catalogDELETE /v1/jobs(empty list and abort in-flight scan grouping)
Build, package, and tests
- Podman linux/amd64 dist builder (
./build/run: run, rebuild, prepare, package). - Slim tarball
matchora-*-linux-amd64.tar.gz(binary, config, public, LICENSE). - Bundled tarball
matchora-*-linux-amd64-llama.tar.gz(same plus llama.cpp, GGUFs, and fetched third-party licenses). - Podman-only unit, smoke (stub metadata + stub chat; waits for
/healthafter secrets POST), and optional live (MATCHORA_LIVE=1) harness. - BSD 3-Clause license.