tile-twinx
Takes a raw YouTube title and splits it into artist, title and extras.
$ uv run app.py --json "Marshmello - Alone (Official Music Video)"
{"artist": "Marshmello", "title": "Alone", "extras": ["Official Music Video"]}
Install
uv add tile-twinx
OR
pip install tile-twinx
from Tile import parse_title
parse_title("Kesariya - Arijit Singh | T-Series | 2023")
# {'artist': 'Arijit Singh', 'title': 'Kesariya', 'extras': ['T-Series', '2023']}
No dependencies, Python 3.12+.
The demo app
app.py runs a list of real YouTube titles through the parser and prints a
table. With no arguments it uses its built-in list of 29 titles:
uv run app.py # the built-in list
uv run app.py "Alan Walker - Faded" # one title
uv run app.py --file my_titles.txt # a file, one title per line
yt-dlp --print "%(title)s" URL | uv run app.py # straight from yt-dlp
uv run app.py --random 5 # 5 random ones
uv run app.py --interactive # type titles by hand
ARTIST TITLE EXTRAS
-------------------------- ---------------------------------- ----------------------
Marshmello Alone Official Music Video
Kesariya Kesariya Full Song, 4K, 1080p, HD
Arijit Singh Kesariya T-Series, 2023
...
parsed 29 titles
artist found 29/29 100%
extras found 25/29 86%
Flags: --json, --dict, --explain (why the parser decided what it did),
--summary, --colour auto|always|never. uv run app.py --help lists them all.
app.py doubles as a smoke test: it holds 29 real titles whose expected output
is in the table above, so if a parser change breaks something it shows up
there.
Library
from Tile import parse_title, get_context
parse_title("Marshmello - Alone (Official Music Video)")
# {'artist': 'Marshmello', 'title': 'Alone', 'extras': ['Official Music Video']}
parse_title(raw, explain=True) # adds a "debug" list of decision notes
ctx = get_context("~/my-ignore.txt", "~/my-artists.txt") # your own data files
parse_title(raw, ctx)
Tile also exports build_context, Context, Segment and Token for
anyone poking at the internals. get_context caches by mtime and reloads
automatically when a data file changes.
Files
| Path | Purpose |
|---|---|
main.py |
the whole engine plus its CLI - still the single source of truth |
assets/ignore.txt |
junk words/phrases to strip (4k, lyric, official ...) |
assets/artist.txt |
artist names, Main Name | Alias per line |
Tile/__init__.py |
the public API, from Tile import ... |
app.py |
the demo app |
main.py stays in the repo root; the wheel ships that same file as
Tile/main.py so it installs inside the package, and Tile/__init__.py
supports both layouts. The checkout and an installed copy behave identically.
Both data files are re-read whenever they change, no restart needed. #
starts a comment in either one.
How it decides
- Clean - unescape
&, normalise unicode, unify dashes and quotes, split the title into chunks on-,|,•. - Brackets - each
(...)/[...]/{...}group is either metadata ((Official Video),(feat. X),(from "Album")) or real title text ((Humming)). A group whose every word is junk becomes an extra. - Junk -
ignore.txtwords go anywhere, except the handful that also start or end real titles (old,song,video,full,audio, ...), which are only removed next to other junk. That is what keeps Old Town Road and Video Games intact while Song 4K still loses its junk. - Credits -
feat./ft./prod./cover by/from "..."are pulled out before junk removal, then folded into the artist. - Artist - the best
artist.txtmatch: a chunk that is the artist beats one that merely contains the name, the earlier chunk beats a later one, and within a chunk the longest match wins. It then extends overx,&,,andfeat.collaborators. Without a match it falls back to the first chunk, then aSong by Artistform, then the last chunk. - Title - whatever is left, with extras in the order they appeared.
Adding to the data files
# ignore.txt - a single word is stripped anywhere, a phrase too
official teaser # safe anywhere: never part of a title
# artist.txt - aliases after a pipe, matched without case or accents
Alan Walker | Alan W.
# artist.txt - "!" marks a channel or label, never a performer
!Saregama
A label in its own chunk becomes an extra, so Kesariya - Arijit Singh | T-Series | 2023 gives Arijit Singh / Kesariya / ["T-Series", "2023"], while
T-Series - Kesariya still credits T-Series as the artist.
Run uv run app.py after editing: it reloads the data files, so a regression
shows up immediately in the table.
Known limits
- An unspaced dash is never a separator (
Marshmello-Alone-Officialstays one word), becauseSpider-ManandMerry-Go-Roundoutnumber the broken form. - When a title and its artist are both unknown and both are short, the first
chunk is taken as the artist. Adding the name to
assets/artist.txtfixes the whole family of titles at once.
Publishing
uv build # dist/*.whl + dist/*.tar.gz
twine check dist/* # verify metadata without uploading
uv publish # needs a token in ~/.pypirc or UV_PUBLISH_TOKEN
Distribution name tile-twinx, import name Tile.
Metadata
Release files for tile-twinx 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tile_twinx-1.0.1.tar.gz | 36.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tile_twinx-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 70.3 kB
Release files / tile_twinx-1.0.1.tar.gz
| Download URL | tile_twinx-1.0.1.tar.gz |
|---|---|
| Size | 36.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2562aae4bd89d689cd8bdbd5a4a5f030e07884da841b2fcf2917053ba6357bc0
|
|
BLAKE2b-256 checksum How to use checksums |
a645cd6b23a531abb2dbdfa5df32c142edf19c74fee07f4caf5c25c70ec75296
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / tile_twinx-1.0.1-py3-none-any.whl
| Download URL | tile_twinx-1.0.1-py3-none-any.whl |
|---|---|
| Size | 34.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ea5cff703d2027c17f25a1fdf6b85cd83daf74e90d147eb1c6bf85cfc6e0c7d0
|
|
BLAKE2b-256 checksum How to use checksums |
a0fb5a1f08675786ad819d14f10dcfd33716cb3583f1f3dc3d901498b556c3f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|