diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..eacf8dd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,74 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +**Setup:** +```bash +python -m venv venv +venv/bin/pip install -r requirements.txt +``` + +**Run:** +```bash +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 + +1. **Startup**: `app.py` builds a logo index by walking `tv-logos-main/` (PNG files), then loads `channels.json` if it exists, otherwise falls back to `example.m3u`. +2. **M3U parsing** (`parse_m3u`): Reads `#EXTINF` lines and `acestream://` URLs only — non-Acestream URL lines are silently discarded. `#EXTGRP` lines provide group logos. +3. **Channel grouping** (`group_channels`): Channels sharing the same `base_name` + `group` (case-insensitive) are merged into a single channel with multiple `mirrors`. Each mirror stores `resolution` and `acestream_hash`. +4. **Logo resolution** (`resolve_logo`): Priority is local logo by tvg-id slug → local logo by channel name slug → external `tvg-logo` URL → empty. The logo index strips trailing 2-3 letter country suffixes for fuzzy matching. +5. **Global state**: The `state` dict holds `channels`, `groups`, `group_meta`, and `source_file`. `channels.json` is hot-reloaded on `/api/channels` requests if its mtime changed. + +### Channel object schema + +```json +{ + "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/` | Serve files from `tv-logos-main/` | +| GET | `/flags/.svg` | Serve country flag SVGs from `svg/` | + +### Key files + +- `app.py` — entire backend; no separate modules +- `static/index.html` — entire frontend; all JS inline, Tailwind via `static/tailwind.js` +- `channels.json` — persisted channel data; auto-loaded at startup; hot-reloaded on change +- `example.m3u` — fallback playlist used if `channels.json` is absent +- `tv-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). diff --git a/channels.json b/channels.json index 68323fa..3d4efd3 100644 --- a/channels.json +++ b/channels.json @@ -273,7 +273,7 @@ "group": "RFEF", "subcategory": "", "country_code": "es", - "logo_url": "https://static.wikia.nocookie.net/logopedia/images/d/df/Logo_Primera_RFEF_2021.png/revision/latest?cb=20230620215900&path-prefix=es", + "logo_url": "/logos/countries/spain/rfef-tv.png", "tags": ["\u26BD"], "mirrors": [ { diff --git a/run.sh b/run.sh index f705dc8..080d21b 100755 --- a/run.sh +++ b/run.sh @@ -7,4 +7,4 @@ kill $(ss -anp 2>/dev/null | grep ':5000' | grep -oP 'pid=\K\d+' | sort -u) 2>/d sleep 1 echo "Arrancando A13 TV..." -.venv/bin/python app.py +venv/bin/python app.py diff --git a/tv-logos-main/countries/spain/rfef-tv.png b/tv-logos-main/countries/spain/rfef-tv.png new file mode 100644 index 0000000..8f541c6 Binary files /dev/null and b/tv-logos-main/countries/spain/rfef-tv.png differ