3.6 KiB
3.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
Setup:
python -m venv venv
venv/bin/pip install -r requirements.txt
Run:
bash run.sh # kills anything on port 5000, then starts the app
# or directly:
venv/bin/python app.py
The app runs on http://localhost:5000. There are no tests.
Architecture
This is a single-page IPTV channel manager for Acestream links. The entire backend is app.py (Flask) and the entire frontend is static/index.html (vanilla JS + bundled Tailwind).
Data flow
- Startup:
app.pybuilds a logo index by walkingtv-logos-main/(PNG files), then loadschannels.jsonif it exists, otherwise falls back toexample.m3u. - M3U parsing (
parse_m3u): Reads#EXTINFlines andacestream://URLs only — non-Acestream URL lines are silently discarded.#EXTGRPlines provide group logos. - Channel grouping (
group_channels): Channels sharing the samebase_name+group(case-insensitive) are merged into a single channel with multiplemirrors. Each mirror storesresolutionandacestream_hash. - Logo resolution (
resolve_logo): Priority is local logo by tvg-id slug → local logo by channel name slug → externaltvg-logoURL → empty. The logo index strips trailing 2-3 letter country suffixes for fuzzy matching. - Global state: The
statedict holdschannels,groups,group_meta, andsource_file.channels.jsonis hot-reloaded on/api/channelsrequests if its mtime changed.
Channel object schema
{
"id": "dazn-laliga", // URL-safe slug, deduplicated with -N suffix
"name": "DAZN LaLiga", // base name without resolution/quality markers
"group": "Sports",
"subcategory": "", // set manually via JSON import
"country_code": "es", // 2-letter ISO, derived from logo path or tvg-logo URL
"logo_url": "/logos/...", // local or external URL
"tags": [], // emoji/string tags set manually
"mirrors": [
{ "resolution": "1080p", "acestream_hash": "abc123..." }
]
}
API endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/channels |
Returns current channel list (hot-reloads JSON if changed) |
| POST | /api/import/m3u |
Upload M3U file, replaces state |
| POST | /api/import/json |
Upload JSON export, replaces state |
| GET | /api/export/json |
Download channels as JSON |
| GET | /api/export/m3u |
Download channels as M3U (Acestream) |
| GET | /logos/<path> |
Serve files from tv-logos-main/ |
| GET | /flags/<code>.svg |
Serve country flag SVGs from svg/ |
Key files
app.py— entire backend; no separate modulesstatic/index.html— entire frontend; all JS inline, Tailwind viastatic/tailwind.jschannels.json— persisted channel data; auto-loaded at startup; hot-reloaded on changeexample.m3u— fallback playlist used ifchannels.jsonis absenttv-logos-main/— local PNG logo library (indexed at startup, not committed to git)svg/— country flag SVGs named by ISO 3166-1 alpha-2 code (e.g.es.svg)
Frontend state
The JS App object in index.html holds all UI state: channels, groups, groupMeta, activeGroup, activeSubcategory, activeTags, search. All rendering is done by renderAll() → renderSidebar() + renderGrid(). Event delegation is used throughout (no per-card listeners). Clicking a channel card opens a modal with sorted mirrors (diamond > gold > silver > bronze quality tiers).