# πŸ“Ί GridTV > A real-time IPTV TV guide, built for [Tunarr](https://github.com/chrisbenincasa/tunarr) and any XMLTV/M3U source. ![PHP](https://img.shields.io/badge/PHP-8.0+-777BB4?style=flat-square&logo=php&logoColor=white) ![Apache](https://img.shields.io/badge/Apache-ready-D22128?style=flat-square&logo=apache&logoColor=white) ![Nginx](https://img.shields.io/badge/Nginx-ready-009639?style=flat-square&logo=nginx&logoColor=white) ![Docker](https://img.shields.io/badge/Docker-ready-2496ED?style=flat-square&logo=docker&logoColor=white) ![License](https://img.shields.io/badge/license-AGPLv3-blue?style=flat-square) --- ![GridTV Preview](assets/preview.png) --- ## 🌐 Demo A public demo instance is available here: πŸ‘‰ https://guide.demo.johnnybegood.fr/ This demo runs with sample XMLTV feeds to showcase the interface. --- ## ✨ Features - πŸŽ›οΈ **Timeline grid** β€” horizontal EPG-style view with a live "now" indicator - πŸ“… **Date in timebar** β€” midnight markers show the day/date so you always know where you are - πŸ“± **Responsive** β€” optimized list view on mobile - ⏱️ **Live progress bar** on the currently airing program - πŸ•ΆοΈ **Past programs** automatically dimmed - πŸ“‹ **Hover tooltip** β€” title, synopsis, season/episode, schedule, duration - 🎬 **Program popup** β€” click any program for full details + IMDb search link - πŸ”— **One-click copy** buttons for EPG & M3U URLs in the topbar - ▢️ **Built-in HLS player** β€” click any channel or live program to watch in a PiP overlay - πŸ”€ **HTTPβ†’HTTPS proxy** β€” streams Tunarr over HTTP transparently from an HTTPS page - 🎨 **Theme system** β€” drop a CSS file in `themes/` and it appears in the menu automatically - πŸ“‘ **Multi-EPG sources** β€” configure multiple EPG/M3U sources, switch from the topbar - πŸ‘€ **Personal EPG** β€” optionally let visitors use your instance with their own EPG/M3U URLs (saved in localStorage) - πŸ” **Search** β€” filter the grid by channel name or program title/synopsis (debounced) - ⭐ **Favorites** β€” pin channels to the top of the grid, persisted in localStorage - 🌍 **i18n** β€” auto-detects browser language, supports EN / FR / ES (add your own in `locales/`) - βš™οΈ **Re-editable setup** β€” protected by an admin key, no SSH required to update config - πŸ”” **Update notifications** β€” a badge appears in the topbar when a new release is available on GitHub - πŸ”„ **Auto-reload** EPG every 30 minutes - 0️⃣ **Zero JS dependencies** β€” vanilla PHP, hls.js loaded from CDN --- ## πŸš€ Installation ### Requirements - A web server running **PHP 8.0+** with **php-curl** extension - **Apache** or **Nginx** β€” or just use **Docker** - An **EPG source in XMLTV format** (e.g. Tunarr, Jellyfin, xTeVe...) - *(Optional)* An **M3U playlist** β€” required for the built-in player --- ### Option A β€” Docker (recommended) ```bash git clone https://github.com/Johnnybegood90/GridTV.git cd GridTV docker compose up -d ``` Then open `http://localhost:8080` and follow the setup wizard. Docker stores the generated configuration in `./data/config.json` on the host. To run on a custom port: ```bash PORT=9000 docker compose up -d ``` To back up or migrate your Docker setup, keep the `data/` directory. --- ### Option B β€” Native PHP host #### 1. Clone the repo ```bash git clone https://github.com/Johnnybegood90/GridTV.git /var/www/gridtv cd /var/www/gridtv ``` #### 2. Set permissions ```bash chmod 775 /var/www/gridtv chown -R www-data:www-data /var/www/gridtv ``` #### 3. Install php-curl The built-in player uses `proxy.php` to relay HTTP streams over HTTPS. This requires the **php-curl** extension: ```bash # Debian/Ubuntu β€” adjust version to match your PHP apt install php8.4-curl systemctl reload apache2 # or: systemctl reload nginx ``` #### 4. Reverse proxy examples
Show reverse proxy / vhost examples These examples are intentionally minimal. Replace `guide.your-domain.com` with your domain and adjust the PHP socket or upstream target to match your host.
Nginx + PHP-FPM ```nginx server { listen 80; server_name guide.your-domain.com; root /var/www/gridtv; index index.php; location / { try_files $uri $uri/ /index.php; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; } } ``` ```bash nginx -t && systemctl reload nginx ``` > Adjust `php8.2-fpm.sock` to match the PHP-FPM version installed on your server.
Apache vhost ```apache ServerName guide.your-domain.com DocumentRoot /var/www/gridtv AllowOverride All Require all granted ``` ```bash a2enmod php8.4 rewrite systemctl reload apache2 ```
Caddy ```caddy guide.your-domain.com { root * /var/www/gridtv php_fastcgi unix//run/php/php8.2-fpm.sock file_server } ``` ```bash systemctl reload caddy ``` > If you use `xcaddy` or a distro package, keep the same site block and only adapt the PHP-FPM socket path.
Traefik (Docker labels) Use this if GridTV runs in Docker and Traefik is your front proxy: ```yaml services: gridtv: build: . volumes: - ./data:/data labels: - "traefik.enable=true" - "traefik.http.routers.gridtv.rule=Host(`guide.your-domain.com`)" - "traefik.http.routers.gridtv.entrypoints=websecure" - "traefik.http.routers.gridtv.tls=true" - "traefik.http.services.gridtv.loadbalancer.server.port=80" ``` You still need a running Traefik instance with `websecure` configured and DNS pointing to it.
#### 5. First launch β€” Setup Open your browser at `http://guide.your-domain.com`. GridTV detects the missing config and automatically redirects you to the setup page: | Field | Description | |---|---| | **Group name** | Displayed top-left in the topbar | | **EPG sources** | Add one or more XMLTV sources, each with an optional M3U URL | | **Personal EPG** | Toggle to allow visitors to use your instance with their own EPG/M3U | Once submitted, `config.json` is created on the server. **The setup page becomes inaccessible until you re-enter your admin key.** --- ### Editing the config later You can re-open the setup page at any time using the admin key generated during first setup: ``` http://guide.your-domain.com/setup.php ``` Enter your admin key to unlock the configuration form. The key is stored in `config.json` under `admin_key` β€” if you lose it, you can retrieve it there via SSH. Or edit `config.json` directly: ```bash nano /var/www/gridtv/config.json ``` ```json { "group_name": "MyGroup TV", "epg_sources": [ { "name": "Main", "epg_url": "http://192.168.0.3:8000/api/xmltv.xml", "m3u_url": "http://192.168.0.3:8000/api/channels.m3u" }, { "name": "Sports", "epg_url": "http://192.168.0.3:8001/api/xmltv.xml", "m3u_url": "" } ], "allow_personal_epg": true } ``` > Instances running the old single-source format (`epg_url` at root) are **migrated automatically** on first load. --- ## πŸ“ Project structure ``` gridtv/ β”œβ”€β”€ index.php # Entry point β”œβ”€β”€ setup.php # Setup + re-configuration (admin key protected) β”œβ”€β”€ proxy.php # HTTPβ†’HTTPS stream proxy β”œβ”€β”€ version.json # Current version (used for update check) β”œβ”€β”€ config.example.json # Config template β”œβ”€β”€ Dockerfile β”œβ”€β”€ docker-compose.yml β”œβ”€β”€ .gitignore # config.json excluded β”œβ”€β”€ locales/ # i18n translation files β”‚ β”œβ”€β”€ en.json β”‚ β”œβ”€β”€ fr.json β”‚ └── es.json β”œβ”€β”€ themes/ # CSS theme files β”‚ β”œβ”€β”€ default.css β”‚ β”œβ”€β”€ magazine.css β”‚ β”œβ”€β”€ cyberpunk.css β”‚ └── steampunk.css └── src/ β”œβ”€β”€ config.php # Config loader, migration, locale detection β”œβ”€β”€ css/ # CSS modules (one file per feature) β”‚ β”œβ”€β”€ base.css # Variables, reset, global layout β”‚ β”œβ”€β”€ topbar.css # Topbar, nav, source switcher, theme selector β”‚ β”œβ”€β”€ grid.css # Grid, channels, programs, ruler, tooltip, loading β”‚ β”œβ”€β”€ mobile.css # Mobile list view β”‚ β”œβ”€β”€ player.css # HLS PiP player β”‚ β”œβ”€β”€ modals.css # Personal EPG modal β”‚ β”œβ”€β”€ search.css # Search bar + highlight β”‚ β”œβ”€β”€ program.css # Program info modal + midnight marker β”‚ β”œβ”€β”€ favorites.css# Favorite button + separator β”‚ └── updater.css # Update notification badge β”œβ”€β”€ tpl/ # HTML templates β”‚ β”œβ”€β”€ head.php # DOCTYPE, , includes CSS modules β”‚ β”œβ”€β”€ topbar.php # Topbar (nav, source switcher, search, theme) β”‚ β”œβ”€β”€ grid.php # Grid + mobile + PiP + overlays β”‚ β”œβ”€β”€ modals.php # Program info modal + Personal EPG modal β”‚ └── footer.php # JS modules loader, └── js/ # JavaScript modules (one file per feature) β”œβ”€β”€ config.js # Constants + PHP-injected vars (incl. locale) β”œβ”€β”€ utils.js # Helpers, clock, EPG fetch β”œβ”€β”€ epg.js # Grid rendering β”œβ”€β”€ mobile.js # Mobile list view β”œβ”€β”€ tooltip.js # Hover tooltip β”œβ”€β”€ sources.js # Multi-EPG switcher + Personal EPG β”œβ”€β”€ m3u.js # M3U parser β”œβ”€β”€ player.js # HLS PiP player β”œβ”€β”€ themes.js # Theme switcher β”œβ”€β”€ search.js # Search + filter (debounced) β”œβ”€β”€ program.js # Program info modal + IMDb link β”œβ”€β”€ favorites.js # Favorites (localStorage) β”œβ”€β”€ updater.js # GitHub update checker └── live.js # Live updates + responsive ``` > `config.json` is listed in `.gitignore` β€” your private URLs will never be pushed to GitHub. --- ## 🎨 Themes GridTV ships with 4 built-in themes. To add your own, create a CSS file in `themes/` with these metadata comments at the top: ```css /* * @name My Theme * @emoji πŸŒ™ */ :root { --bg: #0a0b0d; --accent: #e8c842; } ``` Drop it in `themes/` β€” it appears in the theme selector automatically. --- ## πŸ“‘ Multiple EPG Sources Configure as many EPG/M3U sources as you want in `config.json`. A dropdown appears in the topbar when more than one source is defined. If `allow_personal_epg` is `true`, a **"✏ Personal EPG"** option appears in the dropdown, letting any visitor enter their own XMLTV/M3U URLs. Their choice is saved in `localStorage` β€” zero server impact. --- ## ▢️ Built-in Player Click on any **channel name** or **currently airing program** to open a PiP player in the bottom-right corner. Requires an **M3U URL** in the active source and the **php-curl** extension. The `proxy.php` handles HTTPβ†’HTTPS relay transparently. --- ## βš™οΈ Advanced configuration Tweak these constants in `src/js/config.js`: ```js const PX_PER_MIN = 5; // Horizontal zoom (pixels per minute) const GRID_HOURS = 72; // Total timeline duration (hours) const ROW_H = 80; // Channel row height (px) ``` --- ## 🧩 EPG Compatibility GridTV parses the standard **XMLTV format**. Tested with: - βœ… [Tunarr](https://github.com/chrisbenincasa/tunarr) - βœ… [xTeVe](https://github.com/xteve-project/xTeVe) - βœ… [Jellyfin](https://jellyfin.org/) - βœ… Any XMLTV-compliant source --- ## 🌍 Internationalization GridTV auto-detects the visitor's browser language and serves the matching locale. Supported out of the box: **English**, **French**, **Spanish**. To add a new language, create a file in `locales/` based on an existing one: ```bash cp locales/en.json locales/de.json # then translate the values ``` GridTV will automatically pick it up for browsers with that language set β€” no code changes needed. --- ## 🀝 Contributing PRs are welcome! Got an idea, a fix, or a feature request β€” open an issue or send a PR. --- ## πŸ“„ License GNU Affero General Public License v3.0 (AGPLv3) --- ## πŸ—ΊοΈ Roadmap ### βœ… Done - Timeline grid with live "now" indicator - Mobile responsive list view - Built-in HLS PiP player - HTTPβ†’HTTPS proxy - Theme system (4 built-in themes) - Multi-EPG sources with topbar switcher - Personal EPG (localStorage) - Modular codebase (src/tpl + src/js + src/css) - Docker support - **Search** β€” filter grid by channel name or program title/synopsis (debounced) - **Date in timebar** β€” midnight markers with day/date in the ruler - **Program popup** β€” full details + IMDb search link on any program click - **i18n** β€” EN / FR / ES with browser auto-detection, extensible via locales/ - **Favorites** β€” pin channels to the top of the grid (localStorage) - **Setup re-editable** β€” protected by an admin key, auto-generated on first setup - **Update notifications** β€” topbar badge links to latest GitHub release ### πŸ”œ Coming soon - **Dark/Light auto mode** β€” follow system preference when using the default theme - **Grid export** β€” export today's schedule as PDF or image