NTIndex is a static site generator for NeonTeam model-swap videos.
Site: https://ntindex.kro.kr
Use it to search NeonTeam swaps like A as B without digging through YouTube
titles manually.
It collects video metadata, stores the normalized source data in SQLite, and builds static JSON and HTML files for browsing and searching.
SQLite is the source of truth. Files under dist/ are generated artifacts.
- Python 3.11+
- uv
uv venv
uv sync --devDefault crawl uses yt-dlp against the configured NeonTeam channel.
uv run ntindex crawlDefault values:
--db:ntindex.sqlite3--channel-id:UCI4No3r3X66tSQbVgXse_MA
Use a different channel ID:
uv run ntindex crawl --channel-id UC_otherUse a channel URL directly:
uv run ntindex crawl --channel-url "https://www.youtube.com/@somechannel/videos"Use YouTube RSS/Atom for recent uploads only:
uv run ntindex crawl --recentUse a specific feed URL:
uv run ntindex crawl --feed-url "https://www.youtube.com/feeds/videos.xml?channel_id=UC_other"Import local JSON:
uv run ntindex crawl --input examples/videos.jsonExpected JSON item shape:
{
"title": "Furina as Nahida | Genshin Impact Model Swap",
"link": "https://www.youtube.com/watch?v=example",
"published_at": "2026-01-01T00:00:00Z"
}Only titles matching this pattern are indexed:
A as B | Game Model Swap
uv run ntindex buildDefault output directory:
dist/
Use a different output directory:
uv run ntindex build --dist publicGenerated files include:
dist/
├── index.html
├── search.json
├── style.css
├── app.js
└── game/
└── <game-slug>.html
Run crawl, then build:
uv run ntindex updateUse RSS for recent uploads:
uv run ntindex update --recentUse a custom output directory:
uv run ntindex update --dist publicMerge commands use canonical IDs. Rows are not deleted. Before applying changes, merge commands print a summary and ask for confirmation.
ididentifies the actual stored row.canonical_ididentifies the representative row used for search and build output.- Merge updates video rows to the canonical target ID.
- Build output still includes merged names as searchable aliases.
Important exception:
characters.game_idis not rewritten bymerge game.- Character rows keep their original game row so alias names remain traceable.
- Search and build output still resolve those games through
games.canonical_id. - This avoids
UNIQUE(name, game_id)conflicts when both merged games already contain the same character name.
In short, video references are normalized after merge, but character alias rows keep their original game ownership.
uv run ntindex merge game <old_id> <new_id>Example:
uv run ntindex merge game 12 3This makes game 12 resolve to the same canonical game as 3.
Skip the confirmation prompt:
uv run ntindex merge --yes game 12 3uv run ntindex merge character <old_id> <new_id>Example:
uv run ntindex merge character 51 8Characters can be merged only when they belong to the same canonical game.
Skip the confirmation prompt:
uv run ntindex merge --yes character 51 8Merged names remain searchable through search.json alias fields:
source_namestarget_names
NTIndex records titles that were crawled but could not be parsed into the
A as B | Game Model Swap shape.
List unresolved parse failures:
uv run ntindex failures listFilter by review status:
uv run ntindex failures list --status unreviewed
uv run ntindex failures list --status ignored
uv run ntindex failures list --status needs_parserInclude resolved failures:
uv run ntindex failures list --allLimit the number of rows:
uv run ntindex failures list --limit 20Output JSON:
uv run ntindex failures list --jsonSet review status:
uv run ntindex failures review <failure_id> ignored
uv run ntindex failures review <failure_id> needs_parser --note "parser candidate"
uv run ntindex failures review <failure_id> unreviewedReview statuses:
unreviewed: not checked yetignored: intentionally outside the currentA as Bdata modelneeds_parser: likely worth supporting with parser changes
Run tests:
uv run pytestCurrent tests cover:
- title parsing
- SQLite insertion and duplicate skipping
- static site build output
- CLI crawl/build/update paths
- RSS feed parsing
yt-dlpmetadata parsing- canonical merge behavior
SPEC.mdis the product specification.README.mdis the usage guide.dist/is generated and ignored by git.ntindex.sqlite3is local runtime data and ignored by git.uv.lockshould be committed for reproducible installs.