Skip to main content

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

  1. Clean - unescape &, normalise unicode, unify dashes and quotes, split the title into chunks on -, |, •.
  2. 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.
  3. Junk - ignore.txt words 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.
  4. Credits - feat./ft./prod./cover by/from "..." are pulled out before junk removal, then folded into the artist.
  5. Artist - the best artist.txt match: 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 over x, &, , and feat. collaborators. Without a match it falls back to the first chunk, then a Song by Artist form, then the last chunk.
  6. 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-Official stays one word), because Spider-Man and Merry-Go-Round outnumber 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.txt fixes 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)

Source distribution for tile-twinx 1.0.1
File Size Uploaded
tile_twinx-1.0.1.tar.gz 36.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tile-twinx 1.0.1
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page