npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hdtilt

v0.7.1

Published

Open-source IPTV player: M3U, Xtream Codes and XMLTV, with a TV guide. Web/PWA, desktop, CLI, TUI, MCP and API.

Readme

hdtilt

An open-source IPTV player that works like TiviMate. Bring your own M3U playlist or Xtream Codes login and get a channel list, a TV guide and catch-up. It runs in the browser (installable PWA), on the desktop, and in the terminal.

hdtilt.com · MIT licensed · no account, no tracking, no channels included

hdtilt is a player. It ships with no content. You add a playlist from a provider you already pay for, or from any source you have the right to watch.

What it does

  • Playlists: M3U / M3U8 URLs, local .m3u files, Xtream Codes (player_api.php) logins. An Xtream get.php link pasted as an M3U is detected and upgraded to the Xtream API.
  • TV guide: XMLTV (plain or .gz), taken from the playlist's url-tvg or given by hand. Channels are matched by tvg-id, then by name. Shows what's on now and next, with progress bars and a full guide grid.
  • Catch-up: Xtream timeshift, plus the M3U catchup / catchup-source / tvg-rec forms (default, append, shift, flussonic).
  • Playback through @profullstack/player: HLS (hls.js), MPEG-TS (mpegts.js) and MP4. It restarts stalled live streams and explains codec failures.
  • Movies & Shows: an Xtream line's films and series in their own tab, a category at a time (a big line has hundreds of thousands of titles, so nothing loads them all at once). Search covers every title; a show opens into seasons and episodes; Continue watching resumes where you stopped, and the next episode follows. Providers serve films as ripped, usually MKV with AC-3 / E-AC-3 / DTS sound that no browser plays, so the server converts them on the fly: the picture is copied, only the sound becomes AAC (cheap), delivered as HLS. Seeking past what is converted restarts the conversion there. ◀ / ▶ jump 10 s / 30 s, n is the next episode. A plain M3U's /movie/ and /series/ entries show up there too.
  • Subtitles from OpenSubtitles on any film or episode: the CC button (or c) lists what exists in your language, best match first. Matches use the provider's TMDB id when it has one, else the cleaned-up title and year (an episode by its show, season and episode), and a moviehash of the file itself puts subtitles made for that exact rip on top (marked THIS FILE). −½s / +½s (or ◀ / ▶ in the panel) fix timing; the pick and its timing are remembered per title. Each file is fetched from OpenSubtitles once and kept, so a film someone already watched with subtitles costs nothing.
  • One page per view: /watch/<channel>, /channels/<group>, /favorites, /recent, /guide (lists take ?q= to filter), /vod/movies/<category>, /vod/shows/<category>, /vod/show/<id>, /settings, /account. Each can be bookmarked or reloaded, and the browser's Back button (or a remote's) steps back through them.
  • A global nav with Live TV (with a filter box), Movies & Shows, TV Guide, ★ Favorites, Recent, Account and Settings. It is always on screen outside the picture; over the picture, Back or Menu brings it up, focused for the remote.
  • iPhone and iPad Safari: they have no Media Source Extensions, so they cannot play an MPEG-TS stream directly. hdtilt asks an Xtream line for its HLS version when it offers one, and otherwise the server repackages the stream as HLS (ffmpeg -c copy, no transcoding, shared by everyone watching that channel). Self-hosters need ffmpeg on the PATH, or HDTILT_FFMPEG.
  • Favorites: tap ☆ on any channel row or on the channel banner, or press F. They sit under ★ Favorites in the nav and sync with your account.
  • The TiviMate basics: ↑/↓ zaps channels, OK opens the list, ◀ opens groups, digits jump to a channel number, Back returns to the last channel. Favorites and recents are built in. Every filter (Live TV, the guide, Movies & Shows, the CLI and MCP) finds all the words you type in any order, ignoring case, accents and punctuation, so espn us finds "US| ESPN HD" and sports1 finds "Sports 1". On a TV browser (Silk on Fire TV, Android TV, webOS, Tizen) big ▲ ▼ buttons page through every list, the guide and the category rails, because a TV pointer cannot scroll a list inside the page; ?pager=on or ?pager=off turns them on or off on any device. Works with a mouse, touch, a keyboard or a TV remote (Fire TV, Android TV, webOS, Tizen keys).

Accounts

On hdtilt.com an account comes first: sign up (username, email, password), confirm the email, then add your playlist. It keeps your playlists, favorites and recent channels the same on every screen you watch on. The desktop app and a self-hosted hdtilt serve need no account.

  • Username and password. Sign in with either your username or your email. A TV has no mail client and no passkey, so a password is the way in on one.
  • The email is confirmed before the account can sign in. The link signs you straight in, and "Forgot your password?" sends a reset link.
  • Sign in with CoinPay comes first on sign-in and sign-up (OAuth 2.1 code + PKCE S256 + state + nonce against coinpayportal.com). The account is bound to your CoinPay sub, never matched by email: a first CoinPay sign-in makes a new account, and an existing account connects CoinPay from its Account page while signed in. Disconnect is refused when CoinPay is the account's only way in. Server env: COINPAY_OAUTH_CLIENT_ID, COINPAY_OAUTH_CLIENT_SECRET.
  • The CLI and TUI sign in with OAuth 2.1: hdtilt login opens the browser (authorization code + PKCE, a loopback redirect), and hdtilt login --manual works over SSH by pasting a code; the sign-in page it opens offers Sign in with CoinPay too. Tokens live in ~/.config/hdtilt/auth.json (0600). hdtilt sync merges the account's playlists and favorites into the local config, and hdtilt tui syncs when it starts.
  • Provider logins inside a synced library are encrypted at rest (AES-256-GCM).

Every surface

| Surface | How | | --------- | ------------------------------------------------------------------------------------------------------------------------------ | | Web / PWA | hdtilt.com, or hdtilt serve on your own box | | Desktop | Electron app (Linux AppImage/deb, macOS dmg, Windows exe) from Releases | | CLI | hdtilt now, hdtilt play cnn, hdtilt guide 101 … | | TUI | hdtilt tui: groups, channels and what's on; Enter plays in mpv or VLC | | MCP | hdtilt mcp (stdio), or POST /mcp on any server; movies_shows browses and searches films and series | | API | JSON under /api (POST /api/vod for Movies & Shows), plus the stream proxy at /p/ and conversions at /hls/ |

Install

curl -fsSL https://hdtilt.com/install.sh | sh

That installs the hdtilt command into ~/.local (no root). It uses your Node 22+ if you have one, otherwise it fetches an official Node build (checksum verified) into ~/.local/share/hdtilt. Re-run it to upgrade; sh -s -- --version X pins a release; remove it with sh ~/.local/share/hdtilt/uninstall.sh. The desktop app (AppImage, deb, dmg, exe) is on the releases page. Or install the package yourself:

npm i -g hdtilt        # or: bunx hdtilt
hdtilt add home http://provider.example/get.php?username=U&password=P&type=m3u_plus
hdtilt now             # what's on
hdtilt tui             # browse and play (needs mpv or VLC)
hdtilt serve           # the web app on http://127.0.0.1:8930

Xtream Codes:

hdtilt add home --xtream http://provider.example:8080 --user U --pass P

Playlists and logins live in ~/.config/hdtilt/config.json (mode 0600). The desktop app and hdtilt serve read the same file, so a playlist added from the CLI shows up in the window.

CLI

hdtilt add <name> <m3u-url> [--epg <xmltv-url>]
hdtilt add <name> --xtream <server> --user <u> --pass <p>
hdtilt playlists | use <name> | remove <name> | refresh [name]
hdtilt groups
hdtilt channels [query] [--group <g>] [--json]
hdtilt now [query] [--group <g>]
hdtilt guide <channel> [--hours 12] [--from -6]
hdtilt url <channel> [--at <iso-time>]      # --at = catch-up
hdtilt play <channel> [--player mpv|vlc|ffplay]
hdtilt fav <channel>
hdtilt subs --key <opensubtitles-api-key> [--user <u> --pass <p>]
hdtilt subs <title> [--year Y] [--season S --episode E] [--lang en,fr] [--tmdb id] [--get <n> -o file.vtt]
hdtilt login [--manual] | whoami | sync | logout
hdtilt tui | serve [--port] [--host] [--public] | mcp

A channel can be named by its number, its id, or part of its name.

MCP

Add it to any MCP client:

{ "mcpServers": { "hdtilt": { "command": "npx", "args": ["-y", "hdtilt", "mcp"] } } }

Tools: list_playlists, add_playlist, list_groups, list_channels, whats_on, channel_guide, stream_url (with catch-up), movies_shows, find_subtitles, fetch_subtitle (local only). Over HTTP (POST /mcp), a public server keeps no playlists, so every call passes its own source.

API

| Method | Path | Body / result | | --------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | GET | /api/health | { ok, version, public } | | POST | /api/load | { source } → { channels, groups, epgUrl, account } | | POST | /api/guide | { url, channels:[{id,name,tvgId}], from?, to? } → { programmes: { [channelId]: [...] } } | | POST | /api/nownext | { url, channels } → { [channelId]: { now, next } } | | GET | /api/playlists, /api/playlists/:name | local installs only | | POST | /api/auth/register login verify resend forgot reset refresh logout | accounts (20 tries a minute per address) | | GET | /api/me | the signed-in account (Authorization: Bearer, a session or an OAuth token) | | GET / PUT | /api/library | the synced library; PUT { data, baseVersion }, 409 if another device wrote first | | GET | /.well-known/oauth-authorization-server | OAuth 2.1 metadata; /oauth/token, /oauth/revoke; client hdtilt-cli | | POST | /oauth/device_authorization | TV sign-in (client hdtilt-tv): { device_code, user_code, verification_uri }; approve at /link, poll /oauth/token with the device_code grant | | POST | /api/tv/load channels vod | the Roku app's paged view of a playlist: groups, then pages of channels or titles, each with play: { url, format, fallback? } | | POST | /api/subtitles | { title?, year?, tmdbId?, season?, episode?, languages?, url? } → { items: [{ fileId, language, release, downloads, hashMatch, … }], moviehash } | | GET | /subs/<fileId>.vtt?shift=<s> | one subtitle as WebVTT, cues moved by shift seconds | | GET | /hls/<base64url>/index.m3u8 | an MPEG-TS stream repackaged as HLS (for browsers without MSE) | | GET | /p/<base64url>/<name.ext> | stream proxy; HLS playlists are rewritten so segments and keys come back through it | | POST | /mcp | MCP JSON-RPC |

source is { "type": "m3u", "url": "…" } or { "type": "xtream", "server": "…", "username": "…", "password": "…" }, plus an optional "epg".

Roku

roku/ is a native SceneGraph app for hdtilt.com. On first launch it shows a code; open hdtilt.com/link on a phone, sign in and enter it. It then opens the account's playlists: Favorites, All channels, Search, Movies, Shows and every group, with ▲▼ to change channel. Streams a Roku decodes play straight from the provider; transport streams (and anything that fails) go through hdtilt's HLS remux.

Build the sideload zip with npx -p brighterscript bsc --project roku/bsconfig.json (dist-roku/hdtilt-roku.zip, also attached to every release), then upload it at http://<roku-ip> with the device in developer mode.

Privacy and the proxy

On hdtilt.com, playlists and logins are kept in your browser (IndexedDB) and sent to the server only to load the list. Nothing is stored there. Browsers can't play http:// streams on an https:// page, and most provider streams don't send CORS headers, so those streams pass through hdtilt.com's proxy. The public proxy only serves signed-in viewers (an HttpOnly media cookie set at sign-in, since a video element cannot send a token), refuses private and internal addresses, and limits each viewer to 3 concurrent streams.

If you'd rather nothing sat in between, use the desktop app or hdtilt serve. Both run the same server on your own machine, where the proxy can also reach tuners on your LAN.

Self-hosting

git clone https://github.com/profullstack/hdtilt.com && cd hdtilt.com
bun install && bun run build
PORT=8930 node bin/hdtilt.js serve --host 0.0.0.0 --public   # --public for a shared server

Environment: PORT, HOST, HDTILT_PUBLIC=1, HDTILT_JWT_SECRET (turns accounts on), DATABASE_URL (Postgres for accounts; memory without it), HDTILT_LIBRARY_KEY, RESEND_API_KEY, MAIL_FROM, SITE_URL, HDTILT_ISSUER (the CLI's account server), HDTILT_UA (the user-agent sent to providers), HDTILT_HOME / HDTILT_CACHE, HDTILT_PLAYER, OPENSUBTITLES_API_KEY (turns subtitles on; free at opensubtitles.com/consumers), OPENSUBTITLES_USERNAME / OPENSUBTITLES_PASSWORD (an account's daily download allowance instead of 5 per IP), HDTILT_SUBS_PER_DAY (new downloads per viewer per day on a public server, default 10).

Development

bun install
bun test          # core, server, proxy, MCP and CLI against a fake provider
bun run lint
bun run dev       # build the web app and serve it
bun run desktop   # the Electron window

Layout: src/core (playlists, Xtream, XMLTV, guide, config; most of it also runs in the browser), src/server (HTTP + MCP), src/cli (TUI, player launcher), web/ (the PWA), desktop/ (Electron), bin/hdtilt.js.

License

MIT © Profullstack, Inc.