Skip to content

Johnnybegood90/GridTV

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

183 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“Ί GridTV

A real-time IPTV TV guide, built for Tunarr and any XMLTV/M3U source.

PHP Apache Nginx Docker License


GridTV Preview


🌐 Demo

A public demo instance is available here:

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
  • πŸ”” Program reminders β€” save a browser reminder from the program popup and get notified 15 minutes before airing
  • πŸ”— 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)
  • πŸ—‚οΈ Category filter β€” narrow the guide to movies, sports, news, kids content, etc.
  • πŸ” 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, with browser-side admin key memory for convenience
  • 🩺 Admin health page β€” check config, XMLTV, M3U, response times, redirects, payload size, and metadata coverage/quality
  • πŸ–¨οΈ 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)

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:

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

git clone https://github.com/Johnnybegood90/GridTV.git /var/www/gridtv
cd /var/www/gridtv

2. Set permissions

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:

# 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
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;
    }
}
nginx -t && systemctl reload nginx

Adjust php8.2-fpm.sock to match the PHP-FPM version installed on your server.

Apache vhost
<VirtualHost *:80>
    ServerName guide.your-domain.com
    DocumentRoot /var/www/gridtv

    <Directory /var/www/gridtv>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
a2enmod php8.4 rewrite
systemctl reload apache2
Caddy
guide.your-domain.com {
    root * /var/www/gridtv
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server
}
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:

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. On the same browser, GridTV also remembers the admin key in localStorage so you do not need to type it again on every visit.

Admin tools are available here once unlocked with the same key:

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:

nano /var/www/gridtv/config.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,
  "allow_personal_m3u": 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 payload size
  • highlights XMLTV coverage for things like category, sub-title, rating, date, desc, logos, and stop times
  • gives you a rough XMLTV quality score per source
  • shows M3U stream counts, HTTP vs HTTPS split, and duplicate stream URLs

By default, the page is protected by the same admin key as setup.php:

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:

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:

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:

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:

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:

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   # Program reminder modal + 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 + category filter
        β”œβ”€β”€ reminders.js # Program reminders (localStorage + browser notifications)
        β”œβ”€β”€ 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:

/*
 * @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:

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:


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:

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

About

Real-time IPTV TV guide and any XMLTV/M3U source. Self-hosted, zero dependencies.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors