Files
GridTV/README.md
T

591 lines
17 KiB
Markdown

# 📺 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
<details>
<summary>Show reverse proxy / vhost examples</summary>
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.
<details>
<summary>Nginx + PHP-FPM</summary>
```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.
</details>
<details>
<summary>Apache vhost</summary>
```apache
<VirtualHost *:80>
ServerName guide.your-domain.com
DocumentRoot /var/www/gridtv
<Directory /var/www/gridtv>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
```
```bash
a2enmod php8.4 rewrite
systemctl reload apache2
```
</details>
<details>
<summary>Caddy</summary>
```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.
</details>
<details>
<summary>Traefik (Docker labels)</summary>
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.
</details>
</details>
#### 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.
---
<details>
<summary>Editing the config later</summary>
### 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.
---
</details>
<details>
<summary>Health page</summary>
## 🩺 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.
---
</details>
<details>
<summary>24h export</summary>
## 🖨️ 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.
---
</details>
<details>
<summary>Demo mode</summary>
## 🧪 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`.
---
</details>
<details>
<summary>Project structure</summary>
## 📁 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, <head>, 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, </body></html>
└── 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.
---
</details>
<details>
<summary>Themes</summary>
## 🎨 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.
---
</details>
<details>
<summary>Multiple EPG Sources</summary>
## 📡 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.
---
</details>
<details>
<summary>Built-in Player</summary>
## ▶️ 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.
---
</details>
<details>
<summary>Advanced configuration</summary>
## ⚙️ 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)
```
---
</details>
<details>
<summary>EPG Compatibility</summary>
## 🧩 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
---
</details>
<details>
<summary>Internationalization</summary>
## 🌍 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.
---
</details>
<details>
<summary>Contributing</summary>
## 🤝 Contributing
PRs are welcome! Got an idea, a fix, or a feature request — open an issue or send a PR.
---
</details>
<details>
<summary>License</summary>
## 📄 License
GNU Affero General Public License v3.0 (AGPLv3)
---
</details>
<details>
<summary>Roadmap</summary>
## 🗺️ 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
</details>