# πŸ“Ί 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/ Health page: https://guide.demo.johnnybegood.fr/health.php Setup page (read only on demo): https://guide.demo.johnnybegood.fr/setup.php 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 - 🩺 **Admin health page** β€” check config, XMLTV, M3U, response times, and metadata coverage - πŸ–¨οΈ **24h printable export** β€” generate a polished daily schedule for PDF/print and PNG export - πŸ§ͺ **Demo mode** β€” drop a `.demo` file at the project root to expose admin pages safely in read-only mode - πŸ”” **Update notifications** β€” a badge appears in the topbar when a new release is available on GitHub - πŸ”„ **Auto-reload** EPG every 30 minutes - 0️⃣ **Zero build tooling** β€” vanilla PHP/JS/CSS, with bundled local assets --- ## πŸš€ Installation ### Requirements - A web server running **PHP 8.0+** with **php-curl** and **php-xml** extensions - **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 + php-xml The built-in player uses `proxy.php` to relay HTTP streams over HTTPS, and the admin tools parse XMLTV directly. This requires **php-curl** and **php-xml**: ```bash # Debian/Ubuntu β€” adjust version to match your PHP apt install php8.4-curl php8.4-xml 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.** Admin tools are available here once unlocked with the same key: ```text http://guide.your-domain.com/health.php http://guide.your-domain.com/export.php ``` `health.php` is admin-only by default. `export.php` is public so visitors can print or save the daily guide. ---
Editing the config later ### 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. ---
Health page ## 🩺 Health page The health page gives you a quick operational view of your instance without needing SSH first: - checks whether `config.json` exists and is writable by PHP - shows the current PHP version and whether `php-curl` / `php-xml` are loaded - tests each configured XMLTV source - tests each configured M3U playlist when present - shows response times, final URL after redirects, content type, and basic metadata counts - highlights XMLTV coverage for things like `category`, `sub-title`, `rating`, and `date` By default, the page is protected by the same admin key as `setup.php`: ```text http://guide.your-domain.com/health.php ``` If you enable demo mode with a `.demo` file, visitors can open the page without the key. For monitoring tools such as Uptime Kuma, GridTV also exposes a lightweight probe endpoint: ```text http://guide.your-domain.com/health.php?format=probe ``` - returns HTTP `200` when the instance looks healthy - returns HTTP `503` when config, PHP extensions, or the first XMLTV source fail - returns compact JSON suitable for external monitoring If you prefer a plain text response: ```text http://guide.your-domain.com/health.php?format=probe&plain=1 ``` ---
24h export ## πŸ–¨οΈ 24h export The export page builds a printable 24-hour TV guide from the current XMLTV source: - filter by source - choose the target date - restrict the export to a time window (`00h-24h`, morning, afternoon, evening, prime time) - limit the number of visible channels - choose between a readable card layout or a dense timeline - export to PDF via the browser print dialog - export to PNG directly from the page Public URL: ```text http://guide.your-domain.com/export.php ``` This page is intentionally public so you can share a β€œTV guide sheet” with other people. ---
Demo mode ## πŸ§ͺ Demo mode If you want to expose your instance publicly without letting visitors modify it, create an empty `.demo` file at the project root: ```bash touch /var/www/gridtv/.demo ``` When `.demo` exists: - `setup.php` becomes read-only - `health.php` can be viewed without entering the admin key - visitors can inspect the current configuration safely To disable demo mode: ```bash rm /var/www/gridtv/.demo ``` > `.demo` is ignored by Git, just like `config.json`. ---
Project structure ## πŸ“ Project structure ``` gridtv/ β”œβ”€β”€ index.php # Entry point β”œβ”€β”€ setup.php # Setup + re-configuration (admin key protected) β”œβ”€β”€ health.php # Admin diagnostics page β”œβ”€β”€ export.php # 24h printable export (PDF/image friendly) β”œβ”€β”€ proxy.php # HTTPβ†’HTTPS stream proxy β”œβ”€β”€ version.json # Current version (used for update check) β”œβ”€β”€ Dockerfile β”œβ”€β”€ docker-compose.yml β”œβ”€β”€ .gitignore # local instance files 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 ## 🎨 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 ## πŸ“‘ 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 ## ▢️ 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 ## βš™οΈ 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 ## 🧩 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 ## 🌍 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 ## 🀝 Contributing PRs are welcome! Got an idea, a fix, or a feature request β€” open an issue or send a PR. ---
License ## πŸ“„ License GNU Affero General Public License v3.0 (AGPLv3) ---
Roadmap ## πŸ—ΊοΈ 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 - **Admin health page** β€” diagnostics for config, XMLTV, M3U, timing, and metadata coverage - **Update notifications** β€” topbar badge links to latest GitHub release ### πŸ”œ Coming soon - **Dark/Light auto mode** β€” follow system preference when using the default theme