From e6dfdcd734024d023e5e1e30180f1f954c844316 Mon Sep 17 00:00:00 2001 From: alucena Date: Thu, 5 Mar 2026 09:08:52 +0100 Subject: [PATCH] ajustes-pre-programacion --- CLAUDE.md | 74 ++++++++++++++++++++++ channels.json | 2 +- run.sh | 2 +- tv-logos-main/countries/spain/rfef-tv.png | Bin 0 -> 15638 bytes 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 CLAUDE.md create mode 100644 tv-logos-main/countries/spain/rfef-tv.png 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 0000000000000000000000000000000000000000..8f541c6a2bb2e834261415099adb1c6a47b56e72 GIT binary patch literal 15638 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 literal 0 HcmV?d00001